Inverse Depth
The engine renders with inverse depth: the near plane has a device depth of 1 and the far plane a device depth of 0. This is a fixed convention, there is no setting or shader permutation to change it. It is relevant for custom shaders, custom render passes and C++ code that works with projection matrices or screen space positions.
| Value | |
|---|---|
| Device depth at the near plane | 1 |
| Device depth at the far plane | 0 |
| Closer to the camera | Larger device depth |
| Default depth clear value | 0 |
| Default depth comparison | Greater |
| Depth comparison of materials | GreaterEqual |
In C++ ezGraphicsUtils::GetNearPlaneDeviceDepth() and ezGraphicsUtils::GetFarPlaneDeviceDepth() return the values for the near and far plane.
Custom Shaders
Common/CameraConstantsAccess.h provides functions for working with device depth. Prefer these over hard-coded values and comparisons:
| Function | Description |
|---|---|
GetNearPlaneDeviceDepth() |
Returns 1. |
GetFarPlaneDeviceDepth() |
Returns 0. Write this for background or sky geometry. |
IsDeviceDepthCloser(a, b) |
a > b |
IsDeviceDepthFarther(a, b) |
a < b |
SelectClosestDeviceDepth(a, b) |
max(a, b) |
SelectFarthestDeviceDepth(a, b) |
min(a, b) |
ConvertDeviceDepthToLinearDepth(deviceDepth) |
Returns the camera-space Z value, not the distance from the camera. |
ConvertLinearDepthToDeviceDepth(linearDepth) |
Requires a finite and positive value for perspective projections. |
ReconstructCameraPositionFromDeviceDepth(ndcXY, deviceDepth) |
Works with all projections. |
ReconstructWorldPositionFromDeviceDepth(ndcXY, deviceDepth) |
Works with all projections. |
ReconstructPerspectiveCameraPositionFromDeviceDepth(ndcXY, deviceDepth) |
Assumes a centered perspective projection. |
ReconstructPerspectiveCameraPositionFromLinearDepth(ndcXY, linearDepth) |
Assumes a centered perspective projection. |
Take care of the following:
- Use
DepthTestFunc = CompareFunc_GreaterorCompareFunc_GreaterEqualfor regular geometry. See Shader Render State. - Configure comparison samplers for shadow maps with
Greater. - Increasing a device depth moves it closer to the camera, which matters for manual depth biases.
- Keep device depth and linear depth apart. To select the farther of two values, use
minfor device depth, butmaxfor linear depth. SeePipeline/DownscaleDepth.ezShaderfor an example. - Depth comparisons that are stored explicitly in a Visual Shader are used as they are. They don't get adjusted to this convention.
Depth Bias
The DepthBias, SlopeScaledDepthBias and DepthBiasClamp render states, as well as the respective members of ezGALRasterizerStateCreationDescription, are passed to the graphics API as they are. Positive values therefore pull a surface towards the camera.
The higher level options are defined by their physical effect and take care of the depth direction themselves:
| Option | Effect of positive values |
|---|---|
GetVertexDepthBias() in a material shader |
Pushes the surface away from the camera. |
DepthBias parameter of a mesh decal material |
Pulls the surface towards the camera. |
OcclusionDepthOffset of a lens flare |
Moves the occlusion sample away from the camera. |
SortingDepthOffset of a mesh |
Increases the sorting distance. Doesn't affect the depth buffer. |
ConstantBias and SlopeBias of shadow casting lights reduce shadow artifacts independently of the depth direction.
Cameras and Projection Matrices
ezCamera::SetCameraMode() takes physical distances with near < far, and GetNearPlane() and GetFarPlane() return those. Perspective cameras can use an infinite far distance, orthographic cameras require finite distances. ezCamera::GetProjectionMatrix() maps the near plane to device depth 1 and the far plane to 0.
The projection matrices passed to ezCamera::SetStereoProjection() must already use inverse depth and ezClipSpaceDepthRange::Default. Conventional matrices result in incorrect depth testing and position reconstruction. They are only used for a matching aspect ratio, other aspect ratios use a symmetric perspective projection instead.
The functions ezGraphicsUtils::CreatePerspectiveProjectionMatrix*() and CreateOrthographicProjectionMatrix() derive the depth direction from their Z plane arguments. To create a matrix for rendering directly, pass the far distance as fNearZ and the near distance as fFarZ:
const ezMat4 projection = ezGraphicsUtils::CreatePerspectiveProjectionMatrixFromFovY(fov, aspectRatio, farPlane, nearPlane);
Screen and World Space Conversion
ezGraphicsUtils::ConvertWorldPosToScreenPos()returnsz = 1at the near plane andz = 0at the far plane. Points outside of the frustum can produce values outside of this range, also for points behind the camera. To determine whether a point is in front of the camera, test its camera-space Z value, not the sign of the returned depth.ezGraphicsUtils::ConvertScreenPosToWorldPos()expects the same mapping. To create a picking ray at the near plane, usez = 1. The optional direction can only be computed with an inverse depth matrix. A depth that unprojects to infinity, such as0with an infinite far plane, returns a failure.ezViewData::ComputePickingRay()andComputeWorldSpacePos()use the near plane according to this convention.ezGraphicsUtils::ComputeInterpolatedFrustumPlane()requires an inverse depth matrix forNearToFar. A lerp factor of0selects the near plane.- The depth arguments of the
Draw2Dfunctions ofezDebugRendererare device depths. They only have an effect if a depth target is bound.