Dynamic Variable Rate Shading Using Motion Vectors
Dynamic Variable Rate Shading (VRS) based on motion vectors allows modern game engines to optimize GPU rendering performance by reducing shading frequency in areas with high motion where the human eye cannot perceive fine details. This article examines the practical techniques used to implement this pipeline, including tile-based velocity reduction, perceptual thresholding, directional shading rate mapping, edge-aware masking, and temporal smoothing.
Velocity Buffer Generation and Tile-Based Downsampling
Hardware-accelerated VRS (such as Tier 2 VRS in DirectX 12 and Vulkan) operates on discrete pixel blocks—typically 8x8 or 16x16 tiles. To determine the appropriate rate for each tile, the engine first extracts motion data from the screen-space velocity buffer (motion vector buffer) produced during the G-buffer or depth pre-pass.
Because modern velocity buffers store high-precision per-pixel vectors, a compute shader performs a reduction step over each tile. Rather than relying solely on the average velocity, implementations evaluate both the maximum velocity and the variance of motion within the tile. High variance often indicates rotational movement or silhouette edges, which require finer shading rates to avoid undersampling artifacts.
Perceptual Motion Thresholding
Once tile velocities are calculated, they are mapped against thresholds derived from human visual perception. When an object moves rapidly across the screen, motion blur and temporal persistence in the human eye reduce sensitivity to high-frequency surface detail.
Developers establish velocity boundaries that correspond to specific shading rates:
- Low velocity (0 to \(V_{\text{min}}\)): Shaded at full rate (\(1\times1\)) to preserve sharp geometric and material detail on stationary or slow-moving surfaces.
- Moderate velocity (\(V_{\text{min}}\) to \(V_{\text{mid}}\)): Scaled down to balanced rates such as \(2\times1\), \(1\times2\), or \(2\times2\).
- High velocity (\(> V_{\text{mid}}\)): Coarsely shaded at \(2\times4\), \(4\times2\), or \(4\times4\), drastically cutting pixel shader invocations where motion dominates visual perception.
Directional Shading Rate Mapping
Motion is rarely isotropic. When a surface moves primarily along a single axis (for example, horizontal camera panning or vertical falling objects), details blur along the motion vector while remaining sharp perpendicular to it.
Advanced implementations analyze the dominant direction of the motion vector:
- If \(|V_x| \gg |V_y|\), the engine assigns a \(2\times1\) or \(4\times1\) rate. This reduces horizontal shading frequency along the direction of blur while preserving vertical edge detail at full resolution.
- If \(|V_y| \gg |V_x|\), the engine applies a \(1\times2\) or \(1\times4\) rate, preserving horizontal clarity.
- If the motion is diagonal or multidirectional, isotropic rates like \(2\times2\) or \(4\times4\) are selected.
Edge and Depth Discontinuity Masking
Applying aggressive VRS purely based on velocity can cause severe aliasing or pixelation along geometry silhouettes, especially during disocclusions. To prevent this, motion-vector VRS is combined with depth and normal heuristics.
During the compute pass that writes the Shading Rate Image (SRI):
- Depth Gradients: The shader samples neighbor depth values within the tile. Large depth deltas signal a silhouette edge.
- Normal Variance: Significant differences in surface normals indicate geometric contours.
- Clamping: If an edge is detected within a tile, the shading rate is clamped back to \(1\times1\) (or \(2\times1\)), overriding the velocity-based reduction to ensure geometry borders remain crisp.
Temporal Hysteresis and Anti-Flicker Filtering
Shading rates that switch abruptly frame-to-frame cause noticeable flickering or "shading popping." To counteract this, engines employ temporal hysteresis.
The compute pass reads the previous frame's shading rate image using reprojected motion vectors. If a tile's newly calculated shading rate differs drastically from its historical rate, the engine dampens the transition, forcing the rate to step incrementally (e.g., moving from \(1\times1\) to \(2\times2\) via an intermediate step, or delaying the transition for a frame). Additionally, bias is usually given to finer rates: the system will quickly promote a tile to \(1\times1\) when motion decelerates, but slowly demote it to \(2\times2\) or \(4\times4\) as motion builds.
Shading Rate Image (SRI) Dispatch
The final result of these passes is written into an SRI—a low-resolution, single-channel image where each texel represents the discrete shading rate of a corresponding tile. The graphics pipeline then binds this texture prior to the main forward lighting or deferred shading passes, dynamically instructing the rasterizer how many pixels to group per pixel shader execution.