4

I'm trying to stabilise the shadows in my 3D renderer. I'm using CSMs.

Here's the code I've got, making no attempt to stabilise. The size of the projection in world space should at least be constant though:

void SkyLight::update() {
    // direction is the direction that the light is facing
    vec3 tangent = sq::make_tangent(direction);

    for (int i = 0; i < 4; i++) {
        // .first is the far plane, .second is a struct of 8 vec3 making a world space camera frustum
        const std::pair<float, sq::Frustum>& csm = camera->csmArr[i];

        // calculates the bounding box centre of the frustum
        vec3 frusCentre = sq::calc_frusCentre(csm.second);

        mat4 viewMat = glm::lookAt(frusCentre-direction, frusCentre, tangent);
        mat4 projMat = glm::ortho(-csm.first, csm.first, -csm.first, csm.first, -csm.first, csm.first);
        matArr[i] = projMat * viewMat;
    }
}

That works. But, the shadows flicker and swim like mad. So, here's an attempt at stabilising, by trying to snap them to texel-sized increments, as recommended everywhere but seemingly never explained:

void SkyLight::update() {
    vec3 tangent = sq::make_tangent(direction);

    for (int i = 0; i < 4; i++) {
        const std::pair<float, sq::Frustum>& csm = camera->csmArr[i];

        vec3 frusCentre = sq::calc_frusCentre(csm.second);

        double qStep = csm.first / 1024.0; // shadow texture size
        frusCentre.x = std::round(frusCentre.x / qStep) * qStep;
        frusCentre.y = std::round(frusCentre.y / qStep) * qStep;
        frusCentre.z = std::round(frusCentre.z / qStep) * qStep;

        mat4 viewMat = glm::lookAt(frusCentre-direction, frusCentre, tangent);
        mat4 projMat = glm::ortho(-csm.first, csm.first, -csm.first, csm.first, -csm.first, csm.first);
        matArr[i] = projMat * viewMat;
    }
}

This makes a difference, in that the shadows now swim slowly, rather than bouncing too fast to notice any pattern. However, I'm pretty sure that's just by chance, and that I'm not at all snapping to the right thing, or even snapping the right thing.

Jagoly
  • 939
  • 1
  • 8
  • 32

1 Answers1

4

To fix this, I need to do the snapping in light space, not world space:

viewMat[3][0] -= glm::mod(viewMat[3][0], 2.f * split / texSize);
viewMat[3][1] -= glm::mod(viewMat[3][1], 2.f * split / texSize);
viewMat[3][2] -= glm::mod(viewMat[3][2], 2.f * split / texSize);

Old (Wrong) answer:

So, I revisited this today and managed to solve it in about 10 minutes :D

Just round frusCentre like this:

frusCentre -= glm::mod(frusCentre, 2.f * csm.first / 1024.f);

or, more generally:

frusCentre -= glm::mod(frusCentre, 2.f * split / texSize);

EDIT: No, that's no good... I'll keep trying.

Jagoly
  • 939
  • 1
  • 8
  • 32