ShaderX6: Stable Rendering of Cascaded Shadow Maps (Michal Valient) has a good description.
Basically, pick a world-space point as your “quantization origin”, project it to shadow space, and then subtract from it that shadow space position rounded to the nearest texel. This “correction” offset can be appended to the shadow matrix (used for shadow projection). Essentially this keeps the quantization origin on the corner of a shadow texel, effectively making your shadow map march along in one shadow texel increments.
Beware getting your quantization origin too far from your shadow map though (floating point precision issue).
Haven’t read his presentation, but looks like there’s a mode where shadow texel sizes can be multiples of the base shadow map resolution.
where should I apply this fix (I guess it must be fragment shader) ?
No, you can just quantize your light-space frustum (built on the CPU) using this offset, with your shader remaining oblivious. Basically, just use the offset to scoot your light-space frustum over a tiny bit in XY in light space.
Hi, I know it’s been about 15 years, but I’m stuck with shadow swimming too in CSM
I follow learnopengl CSM and from that I have this setup:
std::vector<glm::vec4> GetFrustomCornersWS(const glm::mat4& projection,glm::mat4& view){
const auto inversProjView = glm::inverse(projection*view);
std::vector<glm::vec4> corners;
for (unsigned int x = 0;x<2;x++) {
for (unsigned int y = 0;y<2;y++) {
for (unsigned int z = 0;z<2;z++) {
const glm::vec4 point = inversProjView*glm::vec4(2.0f*x-1.0f,2.0f*y-1.0f,2.0f*z-1.0f,1.0f);
corners.push_back(point/point.w);
}
}
}
return corners;
}
std::vector<glm::vec4> Corners;
glm::vec3 center = glm::vec3(0.0);
void UpdateCenter(){
center = glm::vec3(0.0f,0.0f,0.0f);
for (const auto& v : Corners) {
center+=glm::vec3(v);
}
center/= Corners.size();
}