Shadow Mapping with Deferred Renderer (OpenGL 4.1, GLSL)

887 views Asked by At

I've read several articles here at stackoverflow and in some books. But I can't find the error in my code. I have an deferred renderer and saved albedo, depth and normals (including spec) inside the g-buffer. Moreover created an shadow map from a light source from top of the scene.

It looks like this:

Albedo (R8G8B8A8): Albedo

Normal (R8G8B8A8): Normal

Linear Depth (F32): enter image description here

Shadow map from light source (linear depth) (500 meter above camera): enter image description here

Ok. My shader code of the deferred with shadow mapping look like this: I have uploaded row major matrices.

Vertex Shader:

layout(row_major) uniform UVSViewMatrix
{
    mat4 m_ScreenMatrix;
};

layout(location = 0) in vec3 VertexPosition;

smooth out vec2 PSTexCoord;

void main()
{
    vec4 Position = vec4(VertexPosition.xyz, 1.0f);

    PSTexCoord = Position.xy;

    gl_Position = Position * m_ScreenMatrix;
}

Fragment Shader:

#version 410

// -----------------------------------------------------------------------------
// Input from engine
// -----------------------------------------------------------------------------
layout(row_major) uniform UPSCameraProperties
{
    mat4 m_ProjectionMatrix;
    mat4 m_CameraView;
    vec3 m_CameraPosition;
    vec3 m_CameraDirection;
};

layout(row_major) uniform UPSLightProperties
{
    mat4 m_LightProjection;
    mat4 m_LightView;
    vec4 m_LightPosition;
    vec4 m_LightAmbientIntensity;
    vec4 m_LightDiffuseIntensity;
    vec4 m_LightSpecularIntensity;
};

uniform sampler2D PSTextureAlbedo;
uniform sampler2D PSTextureNormalSpecular;
uniform sampler2D PSTextureDepth;
uniform sampler2D PSTextureShadowMap;

// -----------------------------------------------------------------------------
// Input from vertex shader
// ----------------------------------------------------------------- ------------
smooth in vec2 PSTexCoord;

// -----------------------------------------------------------------------------
// Output to systembuffer
// -----------------------------------------------------------------------------
layout (location = 0) out vec4 PSOutput;

// -----------------------------------------------------------------------------
// Functions
// -----------------------------------------------------------------------------
vec3 GetViewSpacePositionFromDepth(float _Depth, vec2 _ScreenPosition, mat4 _InvertedProjectionMatrix)
{
    // -----------------------------------------------------------------------------
    // Information from:
    // http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/
    // -----------------------------------------------------------------------------
    vec4 ScreenPosition;

    ScreenPosition.x = _ScreenPosition.x * 2.0f - 1.0f;
    ScreenPosition.y = _ScreenPosition.y * 2.0f - 1.0f;
    ScreenPosition.z = _Depth * 2.0f - 1.0f;
    ScreenPosition.w = 1.0f;

    // -----------------------------------------------------------------------------
    // Transform by the inverse projection matrix
    // -----------------------------------------------------------------------------
    vec4 VSPosition = ScreenPosition * _InvertedProjectionMatrix;

    // -----------------------------------------------------------------------------
    // Divide by w to get the view-space position
    // -----------------------------------------------------------------------------
    return (VSPosition.xyz / VSPosition.w);
}

// -----------------------------------------------------------------------------

float GetShadowAtPosition(vec3 _WSPosition)
{
    // -----------------------------------------------------------------------------
    // Set worls space coord into light projection by multiply with light
    // view and projection matrix;
    // -----------------------------------------------------------------------------
    vec4 LSPosition = vec4(_WSPosition, 1.0f) * m_LightView * m_LightProjection;

    // -----------------------------------------------------------------------------
    // Divide xyz by w to get the position in light view's clip space.
    // -----------------------------------------------------------------------------
    LSPosition.xyz /= LSPosition.w;

    // -----------------------------------------------------------------------------
    // Get uv texcoords for this position
    // -----------------------------------------------------------------------------
    vec3 ShadowCoord = LSPosition.xyz * 0.5f + 0.5f;

    // -----------------------------------------------------------------------------
    // Get final depth at this texcoord and compare it with the real
    // position z value (do a manual depth test)
    // -----------------------------------------------------------------------------
    float DepthValue = texture( PSTextureShadowMap, vec2(ShadowCoord.x, ShadowCoord.y) ).r;

    float Shadow = 1.0f;

    if (ShadowCoord.z > DepthValue)
    {
        Shadow = 0.3f;
    }

    return Shadow;
}

// -----------------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------------
void main() 
{
    // -----------------------------------------------------------------------------
    // Get informations from g-buffer
    // -----------------------------------------------------------------------------
    vec2 TexCoord = vec2(PSTexCoord.s, 1.0f - PSTexCoord.t);

    vec4  AlbedoColor      = texture(PSTextureAlbedo        , TexCoord);
    vec4  NormalSpec       = texture(PSTextureNormalSpecular, TexCoord);
    float Depth            = texture(PSTextureDepth         , TexCoord).r;
    vec3  VSPosition       = GetViewSpacePositionFromDepth(Depth, TexCoord, inverse(m_ProjectionMatrix));
    vec3  Normal           = normalize(NormalSpec.xyz);
    float SpecularExponent = NormalSpec.w;

    vec4  WSPosition = vec4(VSPosition, 1.0f) * inverse(m_CameraView);

    // -----------------------------------------------------------------------------
    // Compute lighting (Light Accumulation)
    // -----------------------------------------------------------------------------
    vec3 CameraPosition  = m_CameraPosition.xyz;
    vec3 LightPosition   = m_LightPosition.xyz;
    vec3 EyeDirection    = WSPosition.xyz - CameraPosition;
    vec3 LightDirection  = normalize(LightPosition - WSPosition.xyz);
    vec3 LightReflection = normalize(-reflect(LightDirection, Normal));

    vec4 AmbientColor  = m_LightAmbientIntensity;
    vec4 DiffuseColor  = clamp(m_LightDiffuseIntensity * max(dot(Normal, LightDirection), 0.0f), 0.0f, 1.0f);
    vec4 SpecularColor = clamp(m_LightSpecularIntensity * pow(max(dot(LightReflection, EyeDirection), 0.0f), SpecularExponent), 0.0f, 1.0f);

    float Shadow = GetShadowAtPosition(WSPosition.xyz);

    // -----------------------------------------------------------------------------
    // Final result
    // -----------------------------------------------------------------------------
    PSOutput = vec4((AlbedoColor * (AmbientColor + DiffuseColor) * Shadow).xyz, 1.0f);
}

At the end my result look like this: Result always with shadow

Did anyone can see my mistake?

Some measurement results:

  • ShadowCoord.xy is always 0.5, 0.5

  • ShadowCoord.z seams to be 1.0f

Here are some variable values:

   LightProjection
    (
     1.29903805f, 0.0f,         0.0,          0.0f,
     0.0f,        1.73205066f,  0.0f,         0.0f,
     0.0f,        0.0f,        -1.00024426f, -1.0f,
     0.0f,        0.0f,        -1.00024426f,  0.0f
     );

   LightView
    (
     1.0f, 0.0f,     0.0f, 0.0f,
     0.0f, 1.0f,     0.0f, 0.0f,
     0.0f, 0.0f,     1.0f, 0.0f,
     0.0f, 0.0f, -1500.0f, 1.0f
     );

   CameraProjection
    (
     1.29903805f, 0.0f,         0.0f,         0.0f,
     0.0f,        1.73205066f,  0.0f,         0.0f,
     0.0f,        0.0f,        -1.00024426f, -1.0f,
     0.0f,        0.0f,        -1.00024426f,  0.0f
     );

   CameraView
    (
     1.0f, 0.0f,    0.0f, 0.0f,
     0.0f, 1.0f,    0.0f, 0.0f,
     0.0f, 0.0f,    1.0f, 0.0f,
     0.0f, 0.0f, -1000.0f, 1.0f
     );
1

There are 1 answers

4
jozxyqk On BEST ANSWER

One issue I can see is a difference between linear and hyperbolic depth. It looks like your "Linear Depth (F32)" G-buffer depth is actually hyperbolic, which is fine because the code expects this. However the light's depth is in fact linear but is then compared against the clip space depth after perspective divide.

It'd probably be easiest to just make the light buffer's depth hyperbolic (gl_FragCoord.z), but changing to compare against eye-space Z should work too:

float ShadowCoordZ = -(vec4(_WSPosition, 1.0f) * m_LightView).z;

The bit about "ShadowCoord.xy is always 0.5, 0.5" is confusing. The G-buffer texture coord seems to work. I can't really tell with the shadow, but do the lighting equations work? If so maybe something's wrong with the matrices? I'd also pass in the inverse of your matrices as uniforms so time isn't spent computing it in the shader.