Fix shadow shimmering

Shadow shimmering is a visual artefact where the outlines of shadow
mapped objects don't stay stable when the camera is moved or rotated.
The reason is that as the shadow map's origin moves, the objects
rendered to the shadow map have temporal aliasing around their edges.

The solution is to only move the shadow map in texel-sized increments.
Because the shadow map's projection is orthographic, moving the shadow
map origin in texel increments ensures that objects that aren't moving
don't show any temporal aliasing, as the position of the samples of the
object in worldspace stay the same.
1008-fix
AbigailBuccaneer 2018-05-04 10:21:31 +01:00
parent fdf67b8217
commit abeb7fceb2
1 changed files with 17 additions and 3 deletions

View File

@ -3819,9 +3819,23 @@ void CEngine::RenderShadowMap()
Math::Vector pos = m_lookatPt + 0.25f * dist * dir;
pos.x = round(pos.x);
pos.y = round(pos.y);
pos.z = round(pos.z);
{
// To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized
// increments. To do this we transform the position to a space where the light's forward/right/up
// axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter).
Math::Matrix lightRotation;
Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp);
pos = Math::MatrixVectorMultiply(lightRotation, pos);
// ...then we round to the nearest worldUnitsPerTexel:
const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x;
pos /= worldUnitsPerTexel;
pos.x = round(pos.x);
pos.y = round(pos.y);
pos.z = round(pos.z);
pos *= worldUnitsPerTexel;
// ...and convert back to world space.
pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos);
}
Math::Vector lookAt = pos - lightDir;