need help with shaders

I need a little bit of help with a few shaders. This is the problem. I have a simulation with several spotlights that are all moving in an environment. I can’t use the AutoShader for that because that takes too much performance, so I have a shader that does the lighting. The shader works okay:

//Cg
/* 
spotlight_mycar.sha
*/
void vshader(
	 in float4 vtx_color : ATTR3,
	 out float4 l_color : COLOR0,
	 in float4 vtx_texcoord : ATTR8,
	 out float4 l_texcoord : TEXCOORD0,
	 uniform float4x4 trans_model_to_view,
	 out float4 l_eye_position : TEXCOORD1,
	 uniform float4x4 tpose_view_to_model,
	 out float4 l_eye_normal : TEXCOORD2,
	 in float3 vtx_normal : ATTR2,
	 in float4 vtx_position : ATTR0,
	 out float4 l_position : POSITION,
	 uniform float4x4 mat_modelproj
) {
	 l_position = mul(mat_modelproj, vtx_position);
	 l_eye_position = mul(trans_model_to_view, vtx_position);
	 l_eye_normal.xyz = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));
	 l_eye_normal.w = 0;
	 l_texcoord = vtx_texcoord;
	 l_color = vtx_color;
}

void fshader(
	 in float4 l_eye_position : TEXCOORD1,
	 in float4 l_eye_normal : TEXCOORD2,
	 in float4 l_texcoord : TEXCOORD0,
	 uniform sampler2D tex_0,
	 uniform float4x4 attr_light0,
	 uniform float4x4 attr_light1,
	 uniform float4 attr_lspec1,
	 out float4 o_color : COLOR0,
	 in float4 l_color : COLOR0,
	 uniform float4 attr_color,
	 uniform float4 attr_ambient,
	 uniform float4 attr_colorscale
) {
	 float4 result;
	 float4 texcoord0 = l_texcoord;
	 // Fetch all textures.
	 float4 tex0 = tex2D(tex_0, texcoord0.xy);
	 // Correct the surface normal for interpolation effects
	 l_eye_normal.xyz = normalize(l_eye_normal.xyz);
	 // Begin view-space light calculations
	 float ldist,lattenv,langle,lshad;
	 float4 lcolor,lspec,lpoint,latten,ldir,leye;
	 float3 lvec,lhalf;
	 float4 tot_diffuse = float4(0,0,0,0);
	 tot_diffuse += attr_ambient;
	 // Spot Light 0
	 lcolor = attr_light0[0];
	 lspec  = lcolor;
	 latten = attr_light0[1];
	 ldir   = attr_light0[2];
	 lpoint = attr_light0[3];
	 lvec   = lpoint.xyz - l_eye_position.xyz;
	 ldist  = length(lvec);
	 lvec /= ldist;
	 langle = saturate(dot(ldir.xyz, lvec));
	 lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);
	 lattenv *= pow(langle, latten.w);
	 if (langle < ldir.w) lattenv = 0;
	 lcolor *= lattenv * saturate(dot(l_eye_normal.xyz, lvec));
	 tot_diffuse += lcolor;
	 // Directional Light 1
	 lcolor = attr_light1[0];
	 lspec  = attr_lspec1;
	 lvec   = attr_light1[3].xyz;
	 lcolor *= saturate(dot(l_eye_normal.xyz, lvec.xyz));
	 tot_diffuse += lcolor;
	 // Begin view-space light summation
	 result = float4(0,0,0,0);
	 //result += tot_diffuse * attr_color;
	 result += tot_diffuse * l_color;
	 result = saturate(result);
	 // End view-space light calculations
	 result.a = 1;
	 result *= attr_colorscale;
	 result.rgba *= tex0.rgba;
	 o_color = result * 1.000001;
}

I load the shader and assign it to a number of nodepaths so that they are properly lit:

    shader = Shader.load("spotlight_mycar.sha", Shader.SL_Cg)
    if self.segs != None:
        for obj in self.segs:
            obj.set_shader(shader)

However, when more spotlights are created, then they replace attr_light0 in the shader. I could solve that by adding attr_light1, attr_light2 etc. but I don’t know in advance how many spotlights there will be. So what I would like to have is a mechanism where I can make the shader work for specific spotlights, for example by using something like:

self.rheadlight = base.camera.attachNewNode( Spotlight( “spotlight” ) )
obj.set_shader_input(‘attr_light0’, self.rheadlight )

Is there a way to assign a specific spotlight to the shader because now it just grabs the first spotlight it can find?

For Cg, there are the alight_x, dlight_x, plight_x, slight_x matrices. Replace “x” with the name of the shader input in which the light NodePath is passed. These are explained in the manual:
panda3d.org/manual/index.ph … der_Inputs

I do recommend to use GLSL instead, where this is a bit simpler and more straightforward to extract the parameters.

On a sidenote, the performance of the shader generator has significantly improved in the latest development versions of Panda3D. Are you still experiencing performance issues with it?

Yes I’m still experiencing performance issues, even with a GTX1080 PGU: there are serious drops in framerate from 60 Hz to 10 for a few seconds when I work in the autoShader mode when a few spotlights are activated. That’s why I do the lights with a shader in which all lights, ambient, dirtectional, spotlights and pointlights are handled. This works fine with a good framerate, except that I want to have control over which lights are used by the shader.

I have tried the following:
step 1 - make a DirectionalLight:
self.DirectionalLight = DirectionalLight(‘directionallight’)

self.DirectionalLightNP = render.attachNewNode(self.DirectionalLight)
step 2:
I set the shader to an nodepath with obj.set_shader(shader);
and
obj_set_shader_input(‘dlight0’, self.directionalLightNP);

step 3:
in fshader->
uniform float4x4 dlight_dlight0;

lcolor = dlight_dlight0[0];
lvec = dlight_dlight0[3].xyz;
lcolor *= saturate(dot(l_eye_normal.xyz, lvec.xyz));
tot_diffuse += lcolor;

But I get this error:
TypeError: NodePath.set_shader() argument 1 must be shader, not NoneType. So this means that the shader now contains errors, probably because self.directionalLightNP is not of type uniform float4x4?

That means the shader failed to compile. Could you share the shader compilation errors?

I get this shader information:


so, it sais uniform mat4x4 dlight_dlight0: Keyword ‘to’ or ‘rel’ expected

and this is the part of the shader where a directional light is referred to:


It is defined as a uniform float4x4 dlight_dlight0

In the python code, just before loading the shader and setting it to a nodepath, I did:
dlight0 = self.directionalLightNP

I have also tried setShaderInput(‘dlight0’, self.directionalLightNP ) but that results in ‘dlight_dlight0 unreferenced within shader…’

OK, sorry, the actual shader input is named dlight_dlight0_rel_view. Note that for the dlight_ input, it should be “lvec = dlight_dlight0[2].xyz;” and not [3].

Yes, that worked, in the sense that it does not give compile errors.

However, if I want to do the same for spotlights (and pointlights) the lighting does not work.
I have added this to the fshader:
uniform float4x4 slight_slightMT_rel_view,

and then later:
lcolor = slight_slightMT_rel_view[0];
lspec = lcolor;
latten = slight_slightMT_rel_view[1];
ldir = slight_slightMT_rel_view[2];
lpoint = slight_slightMT_rel_view[3];
lvec = lpoint.xyz - l_eye_position.xyz;
ldist = length(lvec);
lvec /= ldist;
langle = saturate(dot(ldir.xyz, lvec));
lattenv = 1/(latten.x + latten.yldist + latten.zldist*ldist);
lattenv *= pow(langle, latten.w);
if (langle < ldir.w) lattenv = 0;
lcolor *= lattenv * saturate(dot(l_eye_normal.xyz, lvec));
tot_diffuse += lcolor;

and in the python code:
obj.set_shader_input(‘slightMT’, self.rheadlight)

The shader generates no error, but the spotlight is not visible in the rendering. On the other hand, if I use:
uniform float4x4 attr_light0,

and use attr_light0 instead of light_slightMT_rel_view in the code then the spotlight does work.

They are structured differently. It should be:

lcolor = slight_slightMT_rel_view[0];
lspec  = lcolor;
lpoint = slight_slightMT_rel_view[2];
ldir   = slight_slightMT_rel_view[3];
latten = satten_slightMT;

satten_X is a separate float4 uniform input, but you could choose to simply hardcode the attenuation values as a constant, which will be more efficient. (Also, you could get lspec from slight_slightMT_rel_view[1]; but it is deprecated to have a specular color that differs from the light color.)

I suppose we could add the equivalent of attr_light that uses the new layout but takes the light from a named shader input rather than a numbered LightAttrib light. The only reason this has not been done so far is due to lack of interest, since NVIDIA is no longer supporting Cg and the GLSL light interface is so much nicer.

Thank you very much. I know I am asking a lot, but as a last question, how does that work with pointlights? I suppose there’s also a plight_plight0_rel_view variant.

No problem. With point lights, the structure is:

lcolor = plight_X_rel_view[0];
lspec  = lcolor; //plight_X_rel_view[1];
lpoint = plight_X_rel_view[2];
latten = plight_X_rel_view[3];

And directional lights, for completeness’ sake:

lcolor = dlight_X_rel_view[0];
lspec  = lcolor; //dlight_X_rel_view[1];
lvec   = dlight_X_rel_view[2];

For named ambient lights, there is the float4 alight_X input, but it is easier and more efficient to just use attr_ambient which is a summation of all the ambient lights’ contribution, or to just pass a custom float4.