2
\$\begingroup\$

I implemented a directional light and a shadow map for that light based on learnopengl.com tutorials. And it is working great, but I would like to move the shadow map with camera/player, so I have shadows all over the scene.

What I am trying to do is update the "look at" matrix every frame, based in the camera position, but is not working properly. Here is relevant peace of code, witch I am using to update the shadow map position:

    glm::mat4 lightProjection = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, 1.0f, 7.5f);
    glm::mat4 lightView = glm::lookAt(light.position + camera.position, camera.position, glm::vec3(0.0f, 1.0f, 0.0f));

With the light at position x: -1.0f y: 4.0f z: -1.0f:

enter image description here

\$\endgroup\$
3
  • \$\begingroup\$ Looks to me like the shadows are being clipped, can you try increasing the dimensions of the shadows ortho matrix it should fix this and make it looks the shadows are all over the scene, also try visualising the depth buffer that'll help you see what's the light seeing. Just use the depthmap texture to draw to quad or directly supply it to ImGui::Image. Let me know if that is the effect you're looking for, I'm a bit confused about what you want to achieve \$\endgroup\$
    – Pikachuxxx
    Commented Jun 6, 2021 at 3:57
  • \$\begingroup\$ Idk if this is true, but I think the Last of Us they use a single sun to move around and rotate it for the shadows to change, the refrigerator scene where they meet bill has a bug like this where the shadows suddenly change direction, So If I assume you're trying to use a single sun for the whole game then use a model matrix for the light using the delta movement of the camera to move it. I think should always keep the scene in the frame of the it's View projection matrix. \$\endgroup\$
    – Pikachuxxx
    Commented Jun 6, 2021 at 4:27
  • \$\begingroup\$ @Pikachuxxx increasing the shadow dimensions is giving me similar results, basically the shadows start to disappear when I move towards the light position (like I am doing in the gif), if I move the camera in the opposite direction the shadows work as expected. I think the shadow map is getting out of the camera view when I move towards the light position. I will do what you said and draw the depth texture to make it easier to find what I am doingn wrong. \$\endgroup\$ Commented Jun 6, 2021 at 5:18

1 Answer 1

1
\$\begingroup\$

Calculate the view frustum boundary points, calculate the center of each of the near and far planes, and use vector add/subtract to define topleft, topright, bottomleft and bottomright.

Then, calculate the view frustum boundary points and construct an orthographic projection matrix that keeps all the points in view.

float ar = (float)SCR_WIDTH / (float)SCR_HEIGHT;
float fov = glm::radians(camera.Zoom);
float nearDist = 0.01f;
float farDist = 100.0f;
float Hnear = 2* tan(fov/2) * nearDist;
float Wnear = Hnear * ar;
float Hfar = 2* tan(fov/2) * farDist;
float Wfar = Hfar * ar; 
vec3 centerFar = camera.position + camera.front * farDist;

vec3 topLeftFar = centerFar + (camera.up * Hfar/2) - (camera.right * Wfar/2);
vec3 topRightFar = centerFar + (camera.up * Hfar/2) + (camera.right * Wfar/2);
vec3 bottomLeftFar = centerFar - (camera.up  * Hfar/2) - (camera.right * Wfar/2);
vec3 bottomRightFar = centerFar - (camera.up * Hfar/2) + (camera.right * Wfar/2);

vec3 centerNear = camera.position + camera.front * nearDist;

vec3 topLeftNear = centerNear + (camera.up * Hnear/2) - (camera.right * Wnear/2);
vec3 topRightNear = centerNear + (camera.up * Hnear/2) + (camera.right * Wnear/2);
vec3 bottomLeftNear = centerNear - (camera.up * Hnear/2) - (camera.right * Wnear/2);
vec3 bottomRightNear = centerNear - (camera.up * Hnear/2) + (camera.right * Wnear/2);

vec3 frustumCenter = (centerFar- centerNear)*0.5f;

mat4 lightView = glm::lookAt(normalize(light.position), vec3(0,0,0), vec3(0,0,1));

std::array<vec3, 8> frustumToLightView
{
    lightView * vec4(bottomRightNear, 1.0f),
    lightView * vec4(topRightNear, 1.0f),
    lightView * vec4(bottomLeftNear, 1.0f),
    lightView * glm::vec4(topLeftNear, 1.0f),
    lightView * vec4(bottomRightFar, 1.0f),
    lightView * vec4(topRightFar, 1.0f),
    lightView * vec4(bottomLeftFar, 1.0f),
    lightView * vec4(topLeftFar, 1.0f)
};

// find max and min points to define a ortho matrix around
vec3 min{ INFINITY, INFINITY, INFINITY };
vec3 max{ -INFINITY, -INFINITY, -INFINITY };
for (unsigned int i = 0; i < frustumToLightView.size(); i++)
{
    if (frustumToLightView[i].x < min.x)
        min.x = frustumToLightView[i].x;
    if (frustumToLightView[i].y < min.y)
        min.y = frustumToLightView[i].y;
    if (frustumToLightView[i].z < min.z)
        min.z = frustumToLightView[i].z;

    if (frustumToLightView[i].x > max.x)
        max.x = frustumToLightView[i].x;
    if (frustumToLightView[i].y > max.y)
        max.y = frustumToLightView[i].y;
    if (frustumToLightView[i].z > max.z)
        max.z = frustumToLightView[i].z;
}

float l = min.x;
float r = max.x;
float b = min.y;
float t = max.y;
// because max.z is positive and in NDC the positive z axis is 
// towards us so need to set it as the near plane flipped same for min.z.
float n = -max.z;
float f = -min.z;

// finally, set our ortho projection
// and create the light space view-projection matrix
mat4 lightProjection = ortho(l,r,b,t,n,f);
mat4 lightSpaceMatrix = lightProjection * lightView;

Sources:

http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/

https://community.khronos.org/t/calculating-tight-ortho-projection-matrix-for-shadow-mapping/105588

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .