GLSL project texture

I’m looking for a minimal example of how to project a texture in panda with glsl.

In CG it looks like this (looking at a dumped auto-shader file):
-the vertex shader calculates ‘l_world_position’ using ‘trans_model_to_world’
-the fragment shader uses a 4x4 matrix called ‘texmat_1’ and the vertex-shader calculated ‘trans_model_to_world’ to get UVs

Are these inputs (or inputs like these) available to a glsl shader? Under what names?

Looking in the web, I’ve also found a tutorial that calculates the UVs in the vertex shader:

        vec4 posEye =  gl_ModelViewMatrix * gl_Vertex;
	 vec4 posWorld = InvViewMat * posEye;
	gl_TexCoord[0] = TexGenMat * posWorld;

where InvViewMat and TexGenMat are uniform mat4 suppled by the application, but I’m not sure how these are constructed and how does it fit into panda3d (I can only suspect these are the same inputs that make the cg code run).

Anyone willing to point me in the right direction?

You can use all Cg inputs in GLSL as well, currently, so you can simply use trans_model_to_world.

For the record, though, texmat_1 would be gl_TextureMatrix[1]. OpenGL doesn’t have a concept of a “model matrix” since it merges it as a “model view matrix” so you can’t access those individually via the default GLSL inputs.

In your code, TexGenMat probably refers to gl_TextureMatrix[n] and InvViewMat is render.getMat(, ie, the inverse of the absolute camera transformation. In that code, InvViewMat would correspond to trans_view_to_world. The idea behind that code is to first use the modelview transform to transform it into view space, then transform it back into world space from there. However, since that involves an unnecessary extra step, you might as well just use trans_model_to_world instead.

Also see: … ickref.pdf

It probably would make sense for us to start providing p3d_ModelMatrix and p3d_ViewMatrixInverse in the future.

Ok, thanks, that should get me started.

Should I also expect ‘l_world_position’ also to be present when calling projectTexture() in my script? Or is projectTexture() not really needed anymore and I could just as well provide a nodes position (x,y,z,1?)

l_world_position is just a variable name that the shader uses. It has no meaning with Panda.

If you use a shader, you don’t need projectTexture or gl_TextureMatrix; you can calculate everything in the shader instead of letting Panda do it on the GPU. If you go that route, you do need a special trick to transform the lookup coordinates from (-1, 1) to (0, 1) range. See the Advanced Shadows sample, specifically shadow-nosupport.sha to see how texture projection is done in a Cg shader (shadow mapping is pretty much identical to texture projection except for a comparison check and the fact that a depth texture is projected).

Here’s the code of shadow-nosupport.sha for your convenience:


void vshader(float4 vtx_position : POSITION,
             float2 vtx_texcoord0: TEXCOORD0,
             float3 vtx_normal: NORMAL,

             uniform float4x4 trans_model_to_clip_of_light,
             uniform float4x4 mat_modelproj,
             uniform float4 mspos_light,
             uniform float4 k_ambient,
             uniform float4 k_scale,
             uniform float4 k_push,

             out float4 l_position : POSITION,
             out float2 l_texcoord0 : TEXCOORD0,
             out float4 l_shadowcoord : TEXCOORD1,
             out float  l_smooth : TEXCOORD2,
             out float4 l_lightclip : TEXCOORD3

float4 position = vtx_position * k_scale;

// vertex position
l_position = mul(mat_modelproj, position);

// Pass through texture coordinate for main texture.
l_texcoord0 = vtx_texcoord0;

// Calculate the surface lighting factor.
l_smooth = saturate(dot(vtx_normal, normalize(mspos_light - position)));

// Calculate light-space clip position.
float4 pushed = position + float4(vtx_normal * k_push, 0);
l_lightclip = mul(trans_model_to_clip_of_light, pushed);

// Calculate shadow-map texture coordinates.
l_shadowcoord = l_lightclip * float4(0.5,0.5,0.5,1.0) + l_lightclip.w * float4(0.5,0.5,0.5,0.0);

void fshader(in float2 l_texcoord0 : TEXCOORD0,
             in float4 l_shadowcoord : TEXCOORD1,
             in float  l_smooth : TEXCOORD2,
             in float4 l_lightclip : TEXCOORD3,
             uniform sampler2D tex_0 : TEXUNIT0,
             uniform sampler2D k_Ldepthmap : TEXUNIT1,
             uniform float4 k_ambient,
             uniform float4 k_texDisable,
             out float4 o_color:COLOR)
  float3 circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0);
  float falloff = saturate(1.0 - dot(circleoffs, circleoffs));
  float4 baseColor = saturate(tex2D(tex_0, l_texcoord0) + k_texDisable);
  float4 proj = l_shadowcoord / l_shadowcoord.w;
  float mapval = tex2D(k_Ldepthmap,proj.xy);
  float shade = (mapval > proj.z);
  o_color = baseColor * ( falloff * shade * l_smooth + k_ambient.x );

I don’t mind if some of the work is done on the CPU and keeping projectTexture is a good thing if I ever need a fallback version at hand… but looks like I got stuck early on :confused:

If I try to use trans_model_to_world (uniform mat4 trans_model_to_world; ?) I get:

Assertion failed: Shader input trans_model_to_world is not present

In the other approach when I try to set render.getMat( as InvViewMat:

render.setShaderInput('InvViewMat', render.getMat(

I end up with:

AssertionError: Shader input InvViewMat is not a nodepath.

I got something ‘working’ passing just or the projector but it all looks just wrong. I’ll give it another go next week, can’t wrap my brain around all this :cry:

I suggest upgrading your version of Panda3D to a more recent one.

Failing that, you can create a dummy nodepath, use setMat, and then pass that to the shader.

I just checked in support for these variables (and similar ones), for what it’s worth. list