Cg shader (nvidia: "The Cg Tutorial" - Chapter 5)

Hello all,

I am fairly new to shader programming an currently learning with the nvidia cg tutorial.
In chapter 5 the vertex shader receives a struct as input from the application, and now i am curious if i can pass a struct from panda3d to the shader. I guess i can do this with “setShaderInput” but at the moment i always get the following error message: “:gobj(error): shaders/BasicShader.sha: Invalid type for a k-parameter (uniform in unknown k_material.k_shininess)”.
The structs i want to pass to the shader are “Material” and “PointLight” of the following shader:

//Cg

struct Material {
	uniform float3 k_Ke;
	uniform float3 k_Ka;
	uniform float3 k_Kd;
	uniform float3 k_Ks;

	uniform float  k_shininess;
};

struct PointLight {
	float4 position;
	
	float3 color;

	float  kC;
	float  kL;
	float  kQ;
};

 float attenuation(float3 P, 
					PointLight light)
{

  float d = distance(P, light.position);

  return 1 / (light.kC + light.kL * d +

              light.kQ * d * d);

}

void computeLighting(PointLight  light,
					float3 P,
					float3 N,
					float3 eyePosition,
					float  shininess,

				out float3 diffuseResult,
				out float3 specularResult)
{
  float atten = attenuation(P, light);

  // Compute the diffuse term
  float3 L = normalize(light.position.xyz - P);
  float diffuseLight = max(dot(N, L), 0);
  diffuseResult = atten * light.color * diffuseLight;

  // Compute the specular term
  float3 V = normalize(eyePosition - P);
  float3 H = normalize(L + V);
  float specularLight = pow(max(dot(N, H), 0),
                            shininess);

  if (diffuseLight <= 0) specularLight = 0;
  specularResult = atten * light.color * specularLight;
}


void vshader(				float3 vtx_position : POSITION,
							float3 vtx_normal   : NORMAL,

                        out float4 oPosition : POSITION,
                        out float3 objectPos : TEXCOORD0,
                        out float3 oNormal   : TEXCOORD1,

                    uniform float4x4 modelViewProj)
{
	float4 position = (position.xyz, 1);
	oPosition = mul(modelViewProj, position);
	objectPos = position.xyz;
	oNormal = vtx_normal;
}

void fshader(		float3 vtx_position  : TEXCOORD0,
					float3 l_normal    : TEXCOORD1,
						 
				out float4 o_color     : COLOR,

				uniform PointLight lights[],
				Material k_material,

				uniform float3 k_globalAmbient,
				uniform float3 k_eyePosition)
{
	float3 P = vtx_position;
	float3 N = normalize(l_normal);

	// Compute the emissive term
	float3 emissive = k_material.k_Ke;

	// Compute the ambient term
	float3 ambient = k_material.k_Ka * k_globalAmbient;

	// Compute diffuse and specular light
	float3 diffuseLight, specularLight;

	float3 diffuseSum   = 0;
	float3 specularSum  = 0;

	for (int i = 0; i < lights.length; i++) 
	{
		computeLighting(lights[i], vtx_position, l_normal, k_eyePosition, k_material.k_shininess, diffuseLight, specularLight);
		diffuseSum += diffuseLight;
		specularSum += specularLight;
	}

	// Modify diffuse and specular light by material constants Kd and Ks
	float3 diffuse = k_material.k_Kd * diffuseLight;
	float3 specular = k_material.k_Ks * specularLight;

	// Mix all
	o_color.xyz = emissive + ambient + diffuse + specular;
	o_color.w = 1;
}

This is the corresponding python code:

from pandac.PandaModules import Vec3
myShader = Shader.load("shaders/BasicShader.sha")


x = Vec3()

render.setShaderInput("k_material.k_Ke", 0,0,0)
render.setShaderInput("k_material.k_Ka", 0,0,0)
render.setShaderInput("k_material.k_Kd", 0,0,0)
render.setShaderInput("k_material.k_Ks", 0,0,0)
render.setShaderInput("k_material.k_shininess", 0.0)

render.setShaderInput("k_globalAmbient", 0,0,0)
render.setShaderInput("k_eyePosition", 0,0,0)

render.setShader(myShader)

All your comments, hints and suggestions are welcome.

You should get rid of the k_ prefix, everywhere.

Thanks for you reply. I read that with the k_ prefix here as well but if i delete the prefixes even more errors of a similar type are shown (probably they were just hidden by the k_ prefix).

:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 material.Ke)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 material.Ka)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 material.Kd)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 material.Ks)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in unknown material.shininess)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 globalAmbient)
:gobj(error): shaders/BasicShader.sha: invalid parameter name (uniform in float3 eyePosition)

Is it even a panda-pythonic way to submit shader variables with a struct? In the Cg-Tutorial it’s advised to use input and output structures, as well as functions to keep the code cleaner but that doesn’t seem to work well together with panda.

Edit:
I just tried it with a smaller shader and the k_ prefix is definitely needed in the shader file but not in the python file.

This is the working example without structs:

//Cg
struct simpleTextureOutputVertex 
{
	float4 position : POSITION;
	float2 texcoord0    : TEXCOORD0;
};


simpleTextureOutputVertex vshader(float4 vtx_position : POSITION,
			 in float2 vtx_texcoord0: TEXCOORD0,
             uniform float4x4 mat_modelproj)
{
	simpleTextureOutputVertex OUT;
	
	OUT.position=mul(mat_modelproj, vtx_position);
	OUT.texcoord0 = vtx_texcoord0;
	
	return OUT;
}

void fshader(uniform sampler2D tex_0,
			uniform float3 k_acol,
			in float2 l_texcoord0:TEXCOORD0,
			out float4 o_color:COLOR0)
{
	float4 fullColor = tex2D(tex_0, l_texcoord0);
	float3 rgb = fullColor.xyz*k_acol;
	o_color = float4(rgb, fullColor.w);
}
myShader = Shader.load("shaders/directionalLight.sha")
render.setShaderInput('acol', 1.0,0.8,0.2)

render.setShader(myShader)

The assignment of a Vec3 with setShaderInput didn’t work either

Which version of Panda3D are you using?

That’s it. I recently installed 1.8.1 and thought i was using it but my IDE still loaded the version 1.7.2.
With the new version it runs (but doesn’t look good, just a black box :unamused: )

I tried to use the build in panda shader again and noticed that this worked good as well (with some small artifacts).

The third approach to get a good result was modifying the shadow example that comes with panda but there the shadow gets totally messed up when i try to implement it in my application.
At least i now know how the example of the Cg tutorial should work.

I’ll see tomorrow what i can do to solve this.




Keep in mind that your shader also relies on the model having a texture; if the model itself doesn’t have one, a call to setTexture will be required.

As for the shadows, shadow mapping algorithms tend to be sensitive to depth map precision. This can be finely adjusted by altering the near and far clipping planes of the shadow camera. (Most importantly, increasing the distance of the near clipping plane - the far distance is less important, but should still not be set to an extreme value.)

A texture is already applied to the model, i think for the tutorial shader i have to add the shader to the camera and pass the created shadow map to the model.
Can i use the same texture coordinates for 2 different polygons or will they then receive an identical shadow?

In the case of the build in shader i think only vertices where the normal is facing to the camera are affected by the shader.
This would be a problem. For my particular scenario the vertices that look away from the camera are the more interesting ones.