Chapter 4 · build_shadow_mask
Finding the second surface
Key idea. A sample shows another surface when it lies off the nearest sample's triangle plane by more than one pixel's width at that distance. Measuring from the plane rather than comparing raw depths keeps a slanted floor one surface.
Deferred renderers with MSAA met this question first: which pixels need more than one shading sample? Lauritzen's 2010 SIGGRAPH course slides give the recipe: "Store G-buffer at sample frequency […] Only apply per-sample shading where necessary […] Want to find surface discontinuities […] Compare sample depths to depth derivatives". A depth derivative says how depth changes across the screen on the surface; extrapolated to another sample, it says what depth that sample would have if it showed the same surface. Praxis does the same with the nearest sample's triangle plane, which is what the derivative extrapolates:
if depth > 0.0 {
let point = vec2<f32>(pixel) + sample_offset(sample);
let position = position_at(camera.view_proj, point, depth, target_size);
let pixel_width = distance(
position,
position_at(camera.view_proj, point + vec2<f32>(1.0, 0.0), depth, target_size)
);
let plane_normal = octahedral_decode(textureLoad(surface_normals, pixel, sample).xy * 2.0 - 1.0);
let off_plane = vec4<f32>(
dot(position_at(camera.view_proj, vec2<f32>(pixel) + SAMPLE_OFFSETS[0], depths.x, target_size) - position, plane_normal),
);
let second = abs(off_plane) > vec4<f32>(pixel_width) & depths > vec4<f32>(0.0);
let bits = select(vec4<u32>(0u), vec4<u32>(1u, 2u, 4u, 8u), second);
second_samples = bits.x | bits.y | bits.z | bits.w;
Why a plane, and why the triangle's
A floor seen at a grazing angle changes depth quickly from one sample to the next, though every sample shows the same floor. A test on raw depth differences would call those pixels edges and trace them twice for nothing; the demo in chapter 5 has that test as a switch, and on its sloping ground it lists 91 of its 112 pixels. Measuring each sample's distance from the nearest sample's plane cancels the slope: a sample on the same flat surface lies on the plane, whatever its depth.
The plane is the triangle's own. The depth passes write two normals per sample into the Surface normals (Shadow Terminator Explained): the triangle's, and the one interpolated over it for shading. The depths lie on the triangle, so the test takes the first pair, .xy. The interpolated normal bends to smooth the shading and would tilt the plane away from where the samples really are.
Why one pixel's width
A fixed distance in metres would suit one view distance only: a millimetre is huge for a pixel near the camera and nothing for a pixel on the horizon. The threshold is instead the world distance between the nearest sample and the point one pixel to its right at the same depth, the width of a pixel where the surface is. A sample off the plane by less than that is the same surface bending, or a neighbouring triangle of it; a sample farther off is something else.
The background, and which sample speaks for the second surface
A sample at depth 0, the far plane under reverse-Z, shows the background, which nothing shadows and whose word has no bits, so it is never a second surface. Where several samples are off the plane, the pass traces from the farthest of them, the least depth. It makes the other samples' depths 2, past the near plane's 1, so they never win the minimum:
let candidates = select(vec4<f32>(2.0), depths, second);
let second_depth = min(min(candidates.x, candidates.y), min(candidates.z, candidates.w));
let second_sample = first_sample_of(candidates, second_depth);
Choosing the farthest is a rule for the rare pixel with three surfaces or more (chapter 8). With two, every off-plane sample shows the same surface, and any of them would do.