openglglslshadowopengl-3deferred-rendering

Directional shadow mapping in deferred shading


I'm implementing directional shadow mapping in deferred shading.

First, I render a depth map from light view (orthogonal projection).

Result:
result

I intend to do VSM so above buffer is R32G32 storing depth and depth * depth.

Then for a full-screen shading pass for shadow (after a lighting pass), I write the following pixel shader:

    #version 330

    in vec2 texCoord; // screen coordinate
    out vec3 fragColor; // output color on the screen

    uniform mat4 lightViewProjMat; // lightView * lightProjection (ortho)

    uniform sampler2D sceneTexture; // lit scene with one directional light
    uniform sampler2D shadowMapTexture;
    uniform sampler2D scenePosTexture; // store fragment's 3D position

    void main() {
      vec3 fragPos = texture(scenePosTexture, texCoord).xyz; // get 3D position of pixel
      vec4 fragPosLightSpace = lightViewProjMat * vec4(fragPos, 1.0); // project it to light-space view: lightView * lightProjection

      // projective texture mapping
      vec3 coord = fragPosLightSpace.xyz / fragPosLightSpace.w;
      coord = coord * 0.5 + 0.5;

      float lightViewDepth; // depth value in the depth buffer - the maximum depth that light can see
      float currentDepth; // depth of screen pixel, maybe not visible to the light, that's how shadow mapping works
      vec2 moments; // depth and depth * depth for later variance shadow mapping

      moments = texture(shadowMapTexture, coord.xy).xy;
      lightViewDepth = moments.x;
      currentDepth = fragPosLightSpace.z;

      float lit_factor = 0;
      if (currentDepth <= lightViewDepth)
        lit_factor = 1; // pixel is visible to the light
      else
        lit_factor = 0; // the light doesn't see this pixel

      // I don't do VSM yet, just want to see black or full-color pixels
      fragColor = texture(sceneTexture, texCoord).rgb * lit_factor;
}

The rendered result is a black screen, but if I hard coded the lit_factor to be 1, result is:

result

Basically that's how the sceneTexture looks like.

So I think either my depth value is wrong, which is unlikely, or my projection (light space projection in above shader / projective texture mapping) is wrong. Could you validate it for me?

My shadow map generation code is:

// vertex shader
#version 330 compatibility

uniform mat4 lightViewMat; // lightView
uniform mat4 lightViewProjMat; // lightView * lightProj

in vec3 in_vertex;
out float depth;

void main() {
  vec4 vert = vec4(in_vertex, 1.0);
  depth = (lightViewMat * vert).z / (500 * 0.2); // 500 is far value, this line tunes the depth precision
  gl_Position = lightViewProjMat * vert;
}

// pixel shader
#version 330

in float depth;
out vec2 out_depth;

void main() {
  out_depth = vec2(depth, depth * depth);
}

Solution

  • This is the depth you store in the shadow map:

    depth = (lightViewMat * vert).z / (500 * 0.2);
    

    This is the depth you compare the read back value to:

    vec4 fragPosLightSpace = lightViewProjMat * vec4(fragPos, 1.0);
    currentDepth = fragPosLightSpace.z;
    

    If fragPos is in world space then I assume lightViewMat * vert == fragPos. You are compressing depth by dividing by 500 * 0.2, but that does not equal to fragPosLightSpace.z.

    Hint: Write out the value of currentDepth in one channel and the value from the shadow map in another channel, you can then compare them visually or in RenderDoc or similar.