3

Ok. So, I've been messing around with shadows in my game engine for the last week. I've mostly implemented cascading shadow maps (CSM), but I'm having a bit of a problem with shadowing that I just can't seem to solve.

The only light in this scene is a directional light (sun), pointing {-0.1 -0.25 -0.65}. I calculate 4 sets of frustum bounds for the four splits of my CSMs with this code:

// each projection matrix calculated with same near plane, different far
Frustum make_worldFrustum(const glm::mat4& _invProjView) {
    Frustum fr; glm::vec4 temp;
    temp = _invProjView * glm::vec4(-1, -1, -1, 1);
    fr.xyz = glm::vec3(temp) / temp.w;
    temp = _invProjView * glm::vec4(-1, -1,  1, 1);
    fr.xyZ = glm::vec3(temp) / temp.w;
    ...etc 6 more times for ndc cube
    return fr;
}

For the light, I get a view matrix like this:

glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});

I then create each ortho matrix from the bounds of each frustum:

lightMatVec.clear();
for (auto& frus : cam.frusVec) {
    glm::vec3 arr[8] {
        glm::vec3(viewMat * glm::vec4(frus.xyz, 1)),
        glm::vec3(viewMat * glm::vec4(frus.xyZ, 1)),
        etc...
    };

    glm::vec3 minO = {INFINITY, INFINITY, INFINITY};
    glm::vec3 maxO = {-INFINITY, -INFINITY, -INFINITY};
    for (auto& vec : arr) {
        minO = glm::min(minO, vec);
        maxO = glm::max(maxO, vec);
    }

    glm::mat4 projMat = glm::ortho(minO.x, maxO.x, minO.y, maxO.y, minO.z, maxO.z);
    lightMatVec.push_back(projMat * viewMat);
}

I have a 4 layer TEXTURE_2D_ARRAY bound to 4 framebuffers that I draw the scene into with a very simple vertex shader (frag disabled or punchthrough alpha).

I then draw the final scene. The vertex shader outputs four shadow texcoords:

out vec3 slShadcrd[4];
// stuff
for (int i = 0; i < 4; i++) {
    vec4 sc = WorldBlock.skylMatArr[i] * vec4(world_pos, 1);
    slShadcrd[i] = sc.xyz / sc.w * 0.5f + 0.5f;
}

And a fragment shader, which determines the split to use with:

int csmIndex = 0;
for (uint i = 0u; i < CameraBlock.csmCnt; i++) {
    if (-view_pos.z > CameraBlock.csmSplits[i]) index++;
    else break;
}

And samples the shadow map array with this function:

float sample_shadow(vec3 _sc, int _csmIndex, sampler2DArrayShadow _tex) {
    return texture(_tex, vec4(_sc.xy, _csmIndex, _sc.z)).r;
}

And, this is the scene I get (with each split slightly tinted and the 4 depth layers overlayed): Looking good Great! Looks good.

But, if I turn the camera slightly to the right: Not looking so good Then shadows start disappearing (and depending on the angle, appearing where they shouldn't be).

I have GL_DEPTH_CLAMP enabled, so that isn't the issue. I'm culling front faces, but turning that off doesn't make a difference to this issue.

What am I missing? I feel like it's an issue with one of my projections, but they all look right to me. Thanks!

EDIT: All four of the the light's frustums drawn. They are all there, but only z is changing relative to the camera (see comment below):light frustums

EDIT: Probably more useful, this is how the frustums look when I only update them once, when the camera is at (0,0,0) and pointing forwards (0,1,0). Also I drew them with depth testing this time. frustums from somewhere else

IMPORTANT EDIT: It seems that this issue is directly related to the light's view matrix, currently:

glm::mat4 viewMat = glm::lookAt(cam.pos, cam.pos + lightDir, {0,0,1});

Changing the values for eye and target seems to affect the buggered shadows. But I don't know what I should actually be setting this to? Should be easy for someone with a better understanding than me :D

Jagoly
  • 939
  • 1
  • 8
  • 32
  • I don't see anything obvious in the code. Maybe draw the frustum of the cascades to see if they look correct (the color on the ground doesn't give enough info.) – Jerem Mar 04 '15 at 09:02
  • (By "draw the frustum", I mean just a wire frame box.) – Jerem Mar 04 '15 at 09:09
  • Which frustum do you mean to draw? I just setup code to draw the frustums of the final shadow matrices, and they they all look identical from the camera's POV, but do indeed have different values. Is this because the only values changing relative to the camera are the Z values? Is that how it should be? – Jagoly Mar 04 '15 at 10:44
  • Also, I just drew the camera's view frustums out, and they just form an outline around the viewport, which seems correct. – Jagoly Mar 04 '15 at 10:57

1 Answers1

4

Solved it! It was indeed an issue with the light's view matrix! All I had to do was replace camPos with the centre point of each frustum! Meaning that each split's light matrix needed a different view matrix. So I just create each view matrix like this...

glm::mat4 viewMat = glm::lookAt(frusCentre, frusCentre+lightDir, {0,0,1});

And get frusCentre simply...

glm::vec3 calc_frusCentre(const Frustum& _frus) {
    glm::vec3 min(INFINITY, INFINITY, INFINITY);
    glm::vec3 max(-INFINITY, -INFINITY, -INFINITY);
    for (auto& vec : {_frus.xyz, _frus.xyZ, _frus.xYz, _frus.xYZ,
                      _frus.Xyz, _frus.XyZ, _frus.XYz, _frus.XYZ}) {
        min = glm::min(min, vec);
        max = glm::max(max, vec);
    }
    return (min + max) / 2.f;
}

And bam! Everything works spectacularly!

EDIT (Last one!): What I had was not quite right. The view matrix should actually be:

glm::lookAt(frusCentre-lightDir, frusCentre, {0,0,1});
Jagoly
  • 939
  • 1
  • 8
  • 32
  • 1
    Interesting... I have never considered using `INFINITY` to initialize `min` before. I was always afraid it would not be supported on all systems, so instead I have used `FLT_MAX`. However, `INFINITY` is a nicer solution because math involving it will produce `NAN`, making it obvious that something's uninitialized. _I just checked and `INFINITY` is defined as `FLT_MAX` on systems that do not support the Infinity and Quiet NaN floating-point bit patterns, which makes it perfectly usable on all systems; **+1** for making me learn that ;)_ – Andon M. Coleman Mar 05 '15 at 13:14
  • Considering how much I've learned from you, that makes me feel pretty great :D – Jagoly Mar 05 '15 at 14:31