Directional light and shadow mapping view/projection matrices

I’m having trouble implementing shadow mapping for directional lights (lights with a light direction but no defined position; such as the sun).
I managed to get spotlights shadow mapping working in an earlier thread I made.

The problem I have is the construction of the view and the projection matrices which boogles my mind. Here’s what I use right now:

Mat4 viewMatrix = LookAt(Vec3(0.0f) + (directionalLight.mLightDirection * Z_FAR / 2.0f), -directionalLight.mLightDirection, Vec3(0.0f, 1.0f, 0.0f));
Mat4 lightVP    = CreateOrthographicMatrix(-Z_FAR, Z_FAR, -Z_FAR, Z_FAR, Z_NEAR, Z_FAR) * viewMatrix;

Z_NEAR is 0.1f and Z_FAR is 100.0f.

My problems:

  1. For glm::ortho, I understand the first four parameters define the bounding box but does the 5th and 6th argument have to be in the same dimensions or are they constant throughout the program?
  2. When constructing the view matrix, I run into issues when the camera is far off from the center, say 150 units +X and there are objects there that needs to be shadowed. How do I specify the position properly? If I use the camera position as the position to LookAt(), the shadows will appear to move around when the camera is moving aswell…
  3. I use (directionalLight.mLightDirection * Z_FAR / 2.0f) to position the lights view far away, but it is almost too far so I need a large bounding box and so loose alot of precision… what is a more proper value to offset the lights view position with?
  4. I suppose I need the centrum point in the bounding box as the position for the lights view matrix, but how do I define a reasonable bounding box using glm::ortho? The reason I use Z_FAR in all directions is because the camera could be looking in any direction and all withing the viewing range should be shadowed, but the precision in the shadowmap is really bad and the shadows looks very pixelated.

Any pointers to this problem?

Think of light’s orthographic frustum as a bounding box that encloses all objects visible by the camera, plus objects not visible but potentially casting shadows. For the simplicity let’s disregard the latter.
So to find this frustum:

  • find all objects that are inside the current camera frustum
  • find minimal aa bounding box that encloses them all
  • transform corners of that bounding box to the light’s space (using light’s view matrix)
  • find aa bounding box in light’s space of the transformed (now obb) bounding box
  • this aa bounding box is your directional light’s orthographic frustum.

Note that actual translation component in light view matrix doesn’t really matter as you’ll only get different Z values for the frustum but the boundaries will be the same in world space. For the convenience, when building light view matrix, you can assume the light “position” is at the center of the bounding box enclosing all visible objects.

If I keep the position in the center of the box, won’t the shadows be skewed due to the light being too close-up with the objects?

There is no position in directional light. Think of position and target vectors you pass to LookAt as representing the light direction vector:

direction = target - positiont

Shadows won’t be skewed since you use orthographic projection to render the shadow map
So:


position = centerOfBBox;
target = position + lightDirection;
Mat4 lightViewMatrix = LookAt(position, target, Vec3(0.0f, 1.0f, 0.0f));

Thank you, it looks better now, but I still have a problem.

Just before the camera leaves the extent of the shadows of the models, the shadows sometimes gets wierd artifacts, like very stretched shadows, see below in pic 2; the floating sphere gets a very elongated shadow at the edge of the visibility of the shadows

[ATTACH=CONFIG]578[/ATTACH][ATTACH=CONFIG]579[/ATTACH]

I have the view/projection like you posted;

Mat4 viewMatrix = LookAt(lighting.mCameraPosition, lighting.mCameraPosition + glm::normalize(directionalLight.mLightDirection), Vec3(0.0f, 1.0f, 0.0f));
Mat4 lightVP = CreateOrthographicMatrix(lighting.mCameraPosition.x - 25.0f, lighting.mCameraPosition.x + 25.0f, lighting.mCameraPosition.y - 25.0f, lighting.mCameraPosition.y + 25.0f, lighting.mCameraPosition.z + 25.0f, lighting.mCameraPosition.z - 25.0f) * viewMatrix;

My fragment shader looks like this, if the error is there…?

    const std::string gDirLightFragmentShader =
    "#version 430                                                                                                                   
 \
                                                                                                                                    
 \
    layout(std140) uniform;                                                                                                         
 \
                                                                                                                                    
 \
    uniform UnifDirLight                                                                                                            
 \
    {                                                                                                                               
 \
        mat4 mVPMatrix;                                                                                                             
 \
        vec4 mLightColor;                                                                                                           
 \
        vec4 mLightDir;                                                                                                             
 \
        vec4 mGamma;                                                                                                                
 \
        vec2 mScreenSize;                                                                                                           
 \
    } UnifDirLightPass;                                                                                                             
 \
                                                                                                                                    
 \
    layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     
 \
    layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       
 \
    layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      
 \
    layout (binding = 6) uniform sampler2DShadow unifShadowTexture;                                                                 
 \
                                                                                                                                    
 \
    out vec4 fragColor;                                                                                                             
 \
                                                                                                                                    
 \
    void main()                                                                                                                     
 \
    {                                                                                                                               
 \
        vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             
 \
                                                                                                                                    
 \
        vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 
 \
        vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        
 \
        vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  
 \
                                                                                                                                    
 \
        vec4 lightClipPos = UnifDirLightPass.mVPMatrix * vec4(worldPos, 1.0);                                                       
 \
        vec3 projCoords   = lightClipPos.xyz / lightClipPos.w;                                                                      
 \
        projCoords.z      = projCoords.z - 0.005;                                                                                   
 \
                                                                                                                                    
 \
        float visibilty  = texture(unifShadowTexture, projCoords);                                                                  
 \
                                                                                                                                    
 \
        float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               
 \
                                                                                                                                    
 \
        fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                    
 \
    }              

EDIT: Another issue I found is the shadows gets clipped on objects at a certain distance when moving the camera around - must be caused by the orthographic bounds somehow?
[ATTACH=CONFIG]580[/ATTACH]