Getting Cg shaders to work in panda

I am just getting to the point where i want to start using shaders in panda so I looked at the shaders section in the panda manual http://www.panda3d.org/manual/index.php/Shader_Basics

just to see if I could get them to work I copied the exact shader from that page. My problem is that whatever object I set it to turns white rather than having the desired effect of switching the red and green channels. I have no errors in the output so I am not sure what I am doing wrong!

myShader = Shader.load("test.sha")
model.setShader(myShader)

these are the only two lines of code I am using other than the shader itself, am I missing something?

The problem is that the shader in the sample code doesn’t implement texturing. Shaders replace everything in the OpenGL pipeline, so if you turn on shaders, you’ve turned off everything else - lights, textures, fog, everything. If you want those things back, you have to add them to the shader itself.

So I’ve gone ahead and changed the shader so that it supports a model with one texture:

//Cg

void vshader(
  float4 vtx_position : POSITION,
  float4 vtx_texcoord0 : TEXCOORD0,
  float4 vtx_color: COLOR,
  out float4 l_position : POSITION,
  out float4 l_texcoord0 : TEXCOORD0,
  out float4 l_color0 : COLOR0,
  uniform float4x4 mat_modelproj)
{
  l_position=mul(mat_modelproj, vtx_position);
  l_texcoord0 = vtx_texcoord0;
  l_color0 = vtx_color;
}

void fshader(
  uniform sampler2D tex_0,
  float4 l_texcoord0 : TEXCOORD0,
  float4 l_color0 : COLOR0,
  out float4 o_color : COLOR)
{
  float4 texcolor = tex2D(tex_0, l_texcoord0.xy);
  float4 fullcolor = texcolor * l_color0;
  o_color=float4(fullcolor.g, fullcolor.r, fullcolor.b, fullcolor.a);
}

And a sample program that uses the shader:

import direct.directbase.DirectStart

from pandac.PandaModules import *

m = loader.loadModel("panda-model")
m.setScale(0.01)
m.reparentTo(render)
base.cam.setPos(0,-20,3)
s = Shader.load("shatest.sha")
m.setShader(s)

run()

You’ll notice that the panda’s feet and ears are green instead of pink.

Josh Yelon, maybe put this in the /samples directory for new people to learn?

Did something change with panda since this post? if I try to use your code to test if shaders would work i get the message “shader is not in supported shader-language” I just copy pasted the code in a .sha file.

any ideas?

The //Cg needs to be the very first line of the shader.

(When I posted that on the forums, phpbb2 added a blank line. Don’t know why.)

Prof. Yelon, your previous line must be accidentally selected too, at least the CR.
see ?

//Cg
blah blah blah
import *
correct

Thanks again your explanation was very helpful!!

Ok, now that I know that shaders are working on my machine, I have tried writing one of my own using various Cg tutorials on the net, I don’t completely understand Cg yet but I worked up this shader to do simple perpixel lighting, however whatever object I apply it to simply does not render as if it is transparent!

//Cg

void vshader(
  float3 vtx_position : POSITION,
  float3 vtx_normal: NORMAL,
  float2 vtx_texcoord0: TEXCOORD0,
  out float3 l_position : POSITION, 
  out float3 l_normal,
  out float2 l_texcoord0 : TEXCOORD0,
  uniform float4x4 mat_modelproj)
{
  l_position = vtx_position;
  l_normal = vtx_normal;
  l_texcoord0 = vtx_texcoord0;
}

void fshader(
  uniform sampler2D tex_0,
  uniform float4 k_LightPos,
  uniform float4 k_EyePos,
  uniform float4 k_ambient,
  float3 l_position,
  float3 l_normal,
  float2 l_texcoord0 : TEXCOORD0,
  out float4 o_color : COLOR)
{
  float3 N = normalize(l_normal);
  float3 EyeDir = normalize(k_EyePos - l_position);
  float3 LightDir = normalize(k_LightPos - l_position);
  float3 Angle = normalize(LightDir + EyeDir);
  
  float NdotL = dot(LightDir, N);
  float NdotA = dot(Angle, N);
  float4 Light = lit(NdotL, NdotA, 32);
  
  float4 texcolor = tex2D(tex_0, l_texcoord0.xy);
  o_color = Light.y * Light.z + k_ambient + texcolor;

} 

any ideas? Thanks again for all the help!

In almost every vertex shader, you’ll find this line:

l_position=mul(mat_modelproj, vtx_position);

Here’s the deal: the position data stored in the model is relative to the center of the model. To render it, the shader needs to convert this position data into a more useful form. First, it needs to be converted to camera-space (ie, relative to the camera). Second, after conversion to camera space, it needs to be projected down into 2D. The one line of code shown above does both things in a single step.

The reason you can’t see your model is that the position data is in the wrong form, therefore, the model is showing up in an unexpected place (probably offscreen).

There’s another mistake in your shader.

Basically, every time you calculate anything in a shader, you should be asking yourself: what coordinate system do I want to be working in? There are lots of coordinate systems to choose from, but most calculations are done in one of these: model space, world space, camera space.

So take this calculation, for instance:

float3 EyeDir = normalize(k_EyePos - l_position);

I don’t know for sure, but I’m betting you were thinking of this taking place in world space. If so, then this is buggy, because l_position is not in world space, it’s in model space.

To convert model space to world space, you could do this:

world_position = mul(trans_model_to_world, l_position);

But remember, that’s only one possible solution. Another idea would be, since l_position is already in model space, to do all the calculations in model space. Then you would need to make sure that the k_EyePos that you pass in is also in model space. My suggestion would be: don’t pass in k_EyePos. Instead, use this:

uniform float4 mspos_camera;

That’s “model space position of the camera.” Pretty much the same as what you were saying with k_EyePos, except that panda converts it to model space for you.

After that, you’ll end up with an EyeDir which is also in model space.

There’s no right answer, but long story short, don’t get your coordinate systems confused.

ok this all makes sense to me, I made a few adjustments to my code as you all have suggested however now I am getting a strange error its really long but it is something like this

:display:gsg(error): SHADER INPUT ASSERT: 0607351C
Assertion failed: !input->get_nodepath().is_empty() at line 937 of c:\temp\mkpr\panda\src\display\graphicsStateGuardian.cxx

and it is followed by a second error very similar to this one, anyone have any clue what this could mean?

Just so you know - I haven’t forgotten this question, I just haven’t had a chance to test it yet.