Search code examples
c++opengl3dshadowshadow-mapping

Shadow Map Positioning and Resolution


I'm currently learning C++ and OpenGL and was wondering if anyone could walk me through what is exactly happening with the below code. It currently calculates the positioning and resolution of a shadow map within a 3D environment.

The code currently works, just looking to get a grasp on things.

//Vertex Shader Essentials.
Position = ProjectionMatrix * ViewMatrix * WorldMatrix * vec4 (VertexPosition, 1);
Normal = (ViewMatrix * WorldMatrix * vec4 (VertexNormal, 0)).xyz;
EyeSpaceLightPosition = ViewMatrix * LightPosition;
EyeSpacePosition = ViewMatrix * WorldMatrix * vec4 (VertexPosition, 1);
STCoords = VertexST;

//What is this block of code currently doing?
ShadowCoord = ProjectionMatrix * ShadowMatrix * WorldMatrix * vec4 (VertexPosition, 1);
ShadowCoord = ShadowCoord / ShadowCoord.w;  
ShadowCoord = (ShadowCoord + vec4 (1.0, 1.0, 1.0, 1.0)) * vec4 (1.0/2.0, 1.0/2.0, 1.0/2.0, 1.0);

//Alters the Shadow Map Resolution. 
//    Please Note - c is a slider that I control in the program execution.
float rounding = (c + 2.1) * 100.0;
ShadowCoord.x = (floor (ShadowCoord.x * rounding)) / rounding;
ShadowCoord.y = (floor (ShadowCoord.y * rounding)) / rounding;
ShadowCoord.z = (floor (ShadowCoord.z * rounding)) / rounding;


gl_Position = Position;

Solution

  • ShadowCoord = ProjectionMatrix * ShadowMatrix * WorldMatrix * vec4 (VertexPosition, 1);
    

    This calculates the position of this vertex within the eye space of the light. What you're recomputing is what the Position = ProjectionMatrix * ViewMatrix * WorldMatrix * vec4 (VertexPosition, 1); line must have produced back when you were rendering to the shadow buffer.

    ShadowCoord = ShadowCoord / ShadowCoord.w;  
    

    This applies a perspective projection, figuring out where your shadow coordinate should fall on the light's view plane.

    Think about it like this: from the light's point of view the coordinate at (1, 1, 1) should appear on the same spot as the one at (2, 2, 2). For both of those you should sample the same 2d location on the depth buffer. Dividing by w achieves that.

    ShadowCoord = (ShadowCoord + vec4 (1.0, 1.0, 1.0, 1.0)) * vec4 (1.0/2.0, 1.0/2.0, 1.0/2.0, 1.0);
    

    This also is about sampling at the right spot. The projection above has the thing in the centre of the light's view — the thing at e.g. (0, 0, 1) — end up at (0, 0). But (0, 0) is the bottom left of the light map, not the centre. This line ensures that the lightmap is taken to cover the area from (-1, -1) across to (1, 1) in the light's projection space.

    ... so, in total, the code is about mapping from 3d vectors that describe the vector from the light to the point in the light's space, to 2d vectors that describe where the point falls on the light's view plane — the plane that was rendered to produce the depth map.