How to get correctly shadow Matrix

Hello, I am implementing shadow mapping in Panda3D by GLSL.
I got problems with the shadowMatrix, the matrix trans world space position to light space position.

Of course I read the sample program written in Cg, it simply pass light camera to the shader and use the shadowMatrix like this:

uniform float4x4 trans_model_to_clip_of_light

Since that is no similar way in GLSL, I calculate the shadowMatrix and pass it to shaders.
Here is how I calculate shadowMatrix.

shadowViewMatrix = self.shadowCam.getMat()
shadowProjectionMatrix = self.shadowCam.node().getLens().getProjectionMat()
self.shadowMatrix = shadowProjectionMatrix * shadowViewMatrix

In fragment shader, I reconstruct fragment view space position from depth texture TexDepthStencil.

float fDepth = texture(TexDepthStencil, fTexCoord).a;
vec4 tmp = p3d_ProjectionMatrixInverse * vec4((fTexCoord.x * 2 - 1.0), (fTexCoord.y * 2 - 1.0), (fDepth * 2 - 1.0), 1.0);
vec3 fPos_view = tmp.xyz / tmp.w;

Then multiple inverse view matrix p3d_ViewMatrixInverse to get the fragment world space position.

vec4 fPos_world = p3d_ViewMatrixInverse * vec4(fPos_view, 1.0);

Multiple shadowMatrix, do perspective divide, and normalize in [0.0, 1.0].

tmp = DirectionalLight.shadowMatrix * fPos_world;
vec3 fPos_light = tmp.xyz / tmp.w;
fPos_light = fPos_light * 0.5 + 0.5;

Finally compare the fragment light space fPos_light depth and shadowMap depth.

float fDepth_light = texture(DirectionalLight.shadowMap, fPos_light.xy).r;
float bias = 0.005;
float shadow = fPos_light.z > fDepth_light + bias? 1.0 : 0.0;

I checked the shadowViewMatrix and shadowProjectionMatrix, they seem ‘correctly’ LookAt Matrix and Ortho Matrix. Sadly, the result doesnot correct. I’d like to know whether my shadowMatrix is right? :unamused:

If my shadowMatrix is right, what’s wrong in my calculation of fragment light space position?

I think its perfectly possible to use this in GLSL, just like that:

uniform mat4 trans_model_to_clip_of_light;

Which saves your whole effort :slight_smile:

For your conversion, be sure to set “gl-coordinate-system default”. Otherwise you will have to convert your matrices from pandas coordinate system to OpenGL’s, which is why your code is not working.

Woo, it does not mention in offical manual and I thought tran_x_to_y is Cg only! It will simplify many things.
Thank you tobspr! Really helpful.

I modify my code in fragment shader.
Same thing that reconstruct world space position from depth.

float fDepth = texture(TexDepthStencil, fTexCoord).a;
vec4 tmp = p3d_ProjectionMatrixInverse * vec4((fTexCoord.x * 2 - 1.0), (fTexCoord.y * 2 - 1.0), (fDepth * 2 - 1.0), 1.0);
vec3 fPos_view = tmp.xyz / tmp.w;
vec4 fPos_world = p3d_ViewMatrixInverse * vec4(fPos_view, 1.0);

Multiple the trans_world_to_clip_of_light matrix to get the light clip space postion.

vec4 fPos_light = trans_world_to_clip_of_light * fPos_world;

Convert it to NDC.

fPos_light.xyz = fPos_light.xyz * 0.5 + 0.5;

Compare the depth and calculate shadow.

float fDepth_light = texture(DirectionalLight.shadowMap, fPos_light.xy).r;
float bias = 0.005;
float shadow = fPos_light.z < fDepth_light + bias? 1.0 : 0.0;

Finally I get a beautiful crytek sponza with shadow. :smiley:

Thank you again tobspr! But I still have some problems if you dont mind. :unamused:

1. I tried gl-coordinate-system default in my own deferred shading implement. I visualized my normal texture and it looks ‘strange’. While if I am not setting gl-coordinate-system default, and swap Green Blue channel, I got a good looking normal texture exactly same as normal mapping and other game’s normal buffer. So I do my calculation base on this normal texture and everthing is fine. I want to know what difference between Panda3D coordinate and OpenGL coordinate, Y-up Z-up left-handed right-handed…so that I can make it correct.

2. Any suggestion about shadow aliasing? I am going to try PCSS + CSM. I read a blog about GTA5. GTA5 render a full screen quad to get the shadow coef of every pixels, then perform a depth aware blur on the shadow coef texture. What’s your suggestion?

If you are using 1.10, getting the shadow matrix in GLSL is a lot easier; it can be accessed through p3d_LightSource[0].shadowMatrix (or customInput.shadowMatrix if you used setShaderInput instead of setLight), which does not require you to multiply it with a bias matrix. Check out this sample:

UPDATE: Please do not use shadowMatrix, but use shadowViewMatrix instead, which transforms from view-space vertex coordinates instead of model-space coordinates, which is more efficient.

Also, to get a tiny bit of shadow smoothing for free, you should really use a sampler2DShadow (sampling which does the depth compare for you). This is also demonstrated in the sample program above.