CG SET study materials
CG SET study resources

Decal Flicker at Two Kilometres and a VR Near Plane

CG SET · Put your knowledge to work in context.

All case studies

Decal Flicker at Two Kilometres and a VR Near Plane

A forward renderer uses a 24-bit fixed-point depth buffer with near = 0.1 m and far = 20,000 m. Road decals (lane markings, authored 2 mm above the asphalt) flicker between 1.5 km and 4 km; cockpit geometry at 0.3–1 m is rock solid; distant terrain is clean because it is drawn in a separate far pass with near = 50 m. The pipeline writes depth in a prepass and runs lighting with an equal-depth test. The team is adding VR, which needs near = 0.05 m for hand geometry, and the hardware exposes D32_FLOAT but the engine currently assumes a conventional [0,1] projection and a LESS depth test. Two weeks are available. Artists refuse to raise the decal offset above 5 mm because larger offsets open visible gaps on banked corners.

1. Compute the depth resolution at 1.5 km and 4 km, and use it to decide whether the artists' offset can ever work.

Read the worked answer

For a standard projection mapped to [0,1], the resolution in view-space Z is

δz ≈ z²(f − n) / (f · n · 2^24)

with n = 0.1, f = 20,000, so f·n = 2,000 and (f − n) ≈ 20,000:

  • z = 1,500 m: 2.25e6 × 2e4 / (2,000 × 1.678e7) = 4.5e10 / 3.355e10 ≈ 1.3 m
  • z = 4,000 m: 1.6e7 × 2e4 / 3.355e10 ≈ 9.5 m

At the cockpit, z = 0.5 m gives ≈ 1.5e-4 m, which is why near geometry is stable.

Interpretation. The 5 mm decal offset is between 260× and 1,900× smaller than the buffer's resolution at those distances. No art-side offset can fix this: neighbouring depth values in the buffer differ by metres, so the decal and the road quantise to the same depth value on some frames and to different values on others as the projection moves - exactly the observed flicker. Raising the offset to a metre would make decals float visibly above the road and clip through kerbs.

The conclusion is that the decal flicker is a projection precision defect, not an authoring defect; the artists' constraint is reasonable and should be accepted.

2. Evaluate reversed-Z with a float depth buffer against the alternatives, and explain the specific engine changes it requires.

Read the worked answer

Why reversed-Z wins. With a float depth buffer, precision is finest near 0. Mapping near → 1 and far → 0 (d = n/z, infinite or finite far) places the geometrically distant surface near zero, where float precision is highest. The resulting error is constant relative to distance: δz ≈ z · 2^−24, so ≈ 0.24 mm at 4 km and ≈ 0.06 mm at 1 km - three to four orders of magnitude better than fixed point, and no separate far pass is needed. It also lets near drop to 0.05 m or below for VR, because precision no longer degrades as near shrinks.

Alternatives. Raising near to 0.5 m gains a factor of 5 (≈1.9 m at 4 km at best) - still useless for decals and incompatible with VR hands. Logarithmic depth covers an enormous range but forces a fragment-shader depth write, killing early-Z and the prepass. A third frustum adds passes and artist-facing complexity.

Required changes.

  1. Projection: negate/flip the Z row so w = z and depth = n/z; make far infinite for a small extra gain.
  2. Clear depth to 0.0 and switch the comparison to GREATER; the prepass EQUAL test keeps working.
  3. Flip the sign of polygon offset / slope-scaled bias everywhere, including decal and shadow helpers.
  4. Audit every shader that writes gl_FragDepth, and every place that reconstructs view Z from depth (SSAO, soft particles, fog, depth of field) - the inversion must be applied consistently or these effects silently break.
  5. Re-tune shadow comparison and bias values, which now operate on a flipped distribution.

3. Sketch a two-week migration and validation plan that keeps artists working, and name the failure modes you would test for.

Read the worked answer

Week 1 - mechanism behind a flag. Day 1–2: add a build flag that flips the projection, clear value, and depth comparison. Day 3: convert the depth-dependent helpers (reconstruction, bias signs, decal and shadow paths) and unit-test them. Day 4–5: run the three heaviest passes - shadow, SSAO, soft particles - against reference images at both flag states; the differences should be limited to the intended precision gain.

Week 2 - content and VR. Day 6–8: switch near to 0.05 m and delete the far-pass workaround for terrain; confirm the single frustum holds. Day 9–10: full-level visual regression with artists, focused on decals on banked corners, particle soft-depth, and thin geometry. Day 11–12: performance and platform matrix (the mobile path may be a separate driver/format - verify D32_FLOAT support and fallback). Day 13–14: ship behind a config toggle and keep the fixed-point path for one release.

Failure modes to test explicitly:

  • Depth-reconstruction sign errors, which show as inverted or doubled SSAO and glowing soft particles rather than obvious breakage
  • Polygon offset now applied in the wrong direction, producing shadow acne or peter-panning
  • Third-party middleware that assumes LESS / clear-to-1
  • Precision of the far plane when far becomes infinite: culling and clip-space epsilon tests must be revisited
  • A numeric harness: render a depth ramp cube and assert monotone decreasing depth plus measured separation of 5 mm at 4 km - this catches regressions without relying on eyes.