TAA Velocity Vectors in GLSL Vertex Shaders
Temporal Anti-Aliasing (TAA) velocity vectors—often referred to as motion vectors—serve as the mathematical link between past and present rendering frames. When calculated in GLSL vertex shaders, these 2D screen-space vectors determine the precise direction and distance a vertex has moved from the previous frame to the current one. By outputting this motion data, the rendering engine can accurately reproject and sample historical color buffers, eliminating edge aliasing, specular crawling, and geometric shimmering on moving geometry without introducing severe ghosting artifacts.
The Core Problem TAA Solves
Standard supersampling techniques are computationally expensive because they require rendering a scene multiple times per frame at sub-pixel offsets. TAA achieves equivalent or superior edge smoothing at a fraction of the cost by spreading the sampling process over time. In each frame, the camera projection matrix is slightly shifted using a sub-pixel jitter pattern (such as a Halton sequence). The engine then blends the current jittered frame with previously rendered frames to accumulate samples.
However, if an object or the camera moves, a pixel at coordinate \((x, y)\) in the current frame does not correspond to the same physical world point at coordinate \((x, y)\) in the previous frame. Without tracking this movement, blending frames results in heavy motion blur, smearing, and ghosting trails behind moving objects.
How Velocity Vectors Function in the Vertex Shader
The vertex shader is the most efficient stage to calculate motion data because it has direct access to both current and historical transformation matrices before rasterization:
Dual Transformations: For every vertex, the shader applies two separate matrix transformations:
- The current position using the current
Model-View-Projection (MVP) matrix:
\[\text{Pos}_{\text{current}} = \text{MVP}_{\text{current}} \times \text{Vertex}_{\text{current}}\] - The previous position using the previous frame's
MVP matrix:
\[\text{Pos}_{\text{previous}} = \text{MVP}_{\text{previous}} \times \text{Vertex}_{\text{previous}}\]
- The current position using the current
Model-View-Projection (MVP) matrix:
Perspective Division and Screen Mapping: Both positions are converted from homogeneous clip space to normalized device coordinates (NDC) by dividing by their respective \(w\) components.
Delta Calculation: The velocity vector is the difference between these two points:
\[\mathbf{v} = \text{NDC}_{\text{current}} - \text{NDC}_{\text{previous}}\]De-jittering: Because TAA applies a sub-pixel jitter to the current projection matrix, the vertex shader must subtract this jitter offset from the velocity vector to ensure that the vector represents purely physical motion, not synthetic camera jitter.
The resulting 2D vector is passed as a varying attribute to the fragment shader, which writes it to a dedicated Velocity Buffer (or G-Buffer target).
Key Advantages of Vertex-Stage Generation
Calculating velocity vectors in the vertex shader provides distinct advantages:
- Skeletal Animation and Morph Targets: Rigid-body transform matrices cannot account for internal surface deformation. By computing velocities per-vertex, the shader captures vertex-level changes driven by skinning weights, blend shapes, or wind simulation shaders before passing the interpolated values to fragments.
- GPU Efficiency: Processing motion per-vertex leverages GPU hardware interpolation across polygons. This significantly reduces computation compared to evaluating world positions or matrix reconstructions entirely within full-screen fragment passes.
Historical Reprojection and Neighborhood Clamping
During the final TAA resolve pass, the pipeline uses the generated velocity vectors to look backward in time. For every screen pixel, the engine reads the velocity vector to fetch the corresponding color from the previous frame's history buffer.
Once reprojected, the algorithm performs neighborhood clamping or clipping—comparing the historical color against the bounding box of neighboring pixels in the current frame. If the velocity vector accurately points to the previous surface location, the history color is validated and blended with the current frame. This mechanism eliminates aliasing on fast-moving meshes while keeping edges sharp and temporally stable.