I am trying to implement cascaded shadow mapping. Before I work with the cascades part, I want to implement n=1 cascades scenario. I am trying to calculate the shadow frustum to encompass my camera view frustum. My initial approach was with making an AABB, but it doesn't work well when rotating (the shadows still shimmer). I am trying to get the bounding sphere approach going. This was my attempt:
Matrix4f calculateOrtho(Matrix4f camProj, Matrix4f camView, ref Matrix4f shadowView /* = lookAtMatrix(Vector3f(0.0f, 1.0f, 0.0f), Vector3f(0.0f, 0.0f, 0.0f),
Vector3f(1.0f, 0.0f, 0.0f)) */
)
{
Vector4f[8] corners;
// frustum corners defined in Vulkan NDC space
corners[0] = Vector4f(-1.0f, -1.0f, 0.0f, 1.0f);
corners[1] = Vector4f(1.0f, -1.0f, 0.0f, 1.0f);
corners[2] = Vector4f(1.0f, 1.0f, 0.0f, 1.0f);
corners[3] = Vector4f(-1.0f, 1.0f, 0.0f, 1.0f);
corners[4] = Vector4f(-1.0f, -1.0f, 1.0f, 1.0f);
corners[5] = Vector4f(1.0f, -1.0f, 1.0f, 1.0f);
corners[6] = Vector4f(1.0f, 1.0f, 1.0f, 1.0f);
corners[7] = Vector4f(-1.0f, 1.0f, 1.0f, 1.0f);
for (int a = 0; a < 8; a++)
{
// transform frustum corners into world space
corners[a] = corners[a] * (camProj * camView).inverse();
corners[a] /= corners[a].w;
// transform world space frustum corners into shadow light space
corners[a] = corners[a] * shadowView;
}
// calculate light space frustum center
Vector3f frustumCenter = Vector3f(0, 0, 0);
for (int i = 0; i < 8; i++)
frustumCenter += corners[i].xyz;
frustumCenter *= 1.0f / 8.0f;
// calculate lightspace radius of bounding sphere
float radius = 0.0f;
for (int i = 0; i < 8; i++)
radius = max(radius, (frustumCenter - corners[i].xyz).length());
float shadowmapSize = 4096.0f;
float unitsPerShadowmapTexel = (2.0f * radius) / shadowmapSize;
// align frustum center to multiply of texel size
frustumCenter.x = floor(frustumCenter.x / unitsPerShadowmapTexel) * unitsPerShadowmapTexel;
frustumCenter.y = floor(frustumCenter.y / unitsPerShadowmapTexel) * unitsPerShadowmapTexel;
return orthoMatrixVulkan(frustumCenter.x - radius, frustumCenter.x + radius,
frustumCenter.y - radius, frustumCenter.y + radius,
frustumCenter.z - radius * 3.0f, frustumCenter.z + radius * 3.0f);
}
It almost works… but only when I am near the start of the coordinate system. The shimmer is minimal or nonexistent there. But when I go further away from the (0,0,0) point, the shimmer gets more and more noticeable.
Am I implementing the bounding sphere approach incorrectly? Or is this just floating point inaccuracy, is there some way to prevent it? I noticed my radius value is not always constant, which could be the cause of shimmering, but I don't know why would the radius be different frame-to-frame.