I'm trying to implement shadow maps for Spotlight's, but alas I can't get them to work. I have verified that my fragment shader is getting the shadow map texture. I can sample from it in the traditional sense and display it to the screen, so I know its there.

My problem

  • Actually getting the right coordinates and comparing that sample against the current depth value (No shadows appear)

If I'm understanding the process correctly, I have to transform the fragment from the space relative to the camera into the space occupied by the light when the shadow map was built.

I'm working with a deferred shading system, so I can easily get the current fragment's position in space by just sampling from a Position Map texture.

Here's how I'm trying to determine if a pixel is in shadow or not:

Fragment Shader:

in vec3 EyePos;
in mat4 LightMVP;
out vec4 fragColor;                           

uniform SpotLight spotLight;
uniform vec2 ScreenSize;

uniform sampler2D PositionMap;
uniform sampler2D ColorMap;
uniform sampler2D NormalMap;
uniform sampler2D SpecularMap;
uniform sampler2D ShadowMap;

// Takes in transformed position, samples shadow map, outputs shadow factor float
float CalcShadowFactor(vec4 LightSpacePos)                                                  
    vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w;                                  
    vec2 UVCoords;                                                                          
    UVCoords.x = 0.5 * ProjCoords.x + 0.5;                                                  
    UVCoords.y = 0.5 * ProjCoords.y + 0.5;                                                  
    float z = 0.5 * ProjCoords.z + 0.5;                                                     
    float Depth = texture(ShadowMap, UVCoords).x;   

    if (Depth < z + 0.00001)                                                                 
        return 0.0;                                                                         
         return 1.0;                                                                         

// Returns the texture coordinate
vec2 CalcTexCoord()
    return gl_FragCoord.xy / ScreenSize;

void main(void)
    vec2 TexCoord = CalcTexCoord();                   // Used for sampling from input texture maps

    vec4 WorldPos = texture2D(PositionMap, TexCoord); // Position in space

    // ... omitted texture sampling for diffuse, normals, specular, etc ... //

    vec4 LightSpace = (LightMVP) * WorldPos4;   

    float ShadowFactor = CalcShadowFactor(LightSpace);    

    fragColor = vec4(Color, 1.0) * CalcSpotLight(WorldPos.xyz, Normal, Specular, ShadowFactor);

The matrices lightV and lightP are the view and projection matrices used by the light when it takes a shadow map pass of the scene from its perspective.

The Vertex Shader:

#version 330

in vec4 vertex;

uniform mat4 mMatrix;
uniform mat4 pMatrix;
uniform mat4 vMatrix;
uniform mat4 lightV;
uniform mat4 lightP;
uniform vec3 eye;

out vec3 EyePos;
out mat4 LightMVP;

void main()
    EyePos = eye;       
    mat4 worldMVP = pMatrix * vMatrix * mMatrix;    
    LightMVP = lightP * lightV * mMatrix;
    gl_Position = worldMVP * vertex;

The shadow map texture (visualized)

enter image description here

The rendered scene

enter image description here


What am I doing wrong? Does any of this math seem off?

Does it matter if the shadow map texture is of a different resolution and aspect ratio than the camera? In this case it is exactly the same.

If nothing seems wrong, how else would I debug this then? I've tried just outputting a vec4 with the shadow factor, but all I get is bright white (as it isn't sampling right)

If it helps, I've been following the tutorials on OGLDev, so my deferred shading system is similar to theirs, and my shadow map is also like theirs (however they never did a tutorial for using both at the same time)


I managed to brute-force a solution, however I don't understand why it works, and I'm not certain if it is even the correct solution.

For calculating my LightMVP mat4, I mutliply the camera's projection matrix by the inverse light view matrix, which then gets passed into my fragment shader and multiplied by the world position, where the world position is derived from the texture Position Map.

LightMVP = (pMatrix) * inverse(lightV);
vec4 LightSpace = (LightMVP) * WorldPos;

Is there anything wrong with this approach?

  • \$\begingroup\$ I updated the shaders in my post. For simplicity sake I put all my matrix operations in the fragment shader, however nothing changes when I multiply the light matrices together and output it to the fragment shader. \$\endgroup\$ – Yattabyte Sep 23 '15 at 21:59
  • \$\begingroup\$ May you show the scene with and without shadows? The problem might come from another part than this shadow map. \$\endgroup\$ – Aracthor Sep 24 '15 at 1:25
  • \$\begingroup\$ That is the scene without shadows. Any darkness is just the standard spot light mechanism for not filling in light outside of its cone. If I set the shadow factor to 0, nothing renders (as expected, everything becomes shadow), and if set to 1 then it looks the same as it does now. \$\endgroup\$ – Yattabyte Sep 24 '15 at 3:05
  • \$\begingroup\$ the scene is hard to see with only your spot lights... Could you add a small ambient light to see every object? \$\endgroup\$ – Aracthor Sep 24 '15 at 4:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.