Understanding gl_FragCoord.w and View-Space Depth in GLSL
In OpenGL and GLSL, the gl_FragCoord.w component
provides direct access to the reciprocal of the clip-space \(w\) coordinate (\(1/w_c\)), which is directly proportional to
the reciprocal of the fragment's view-space depth. This article explains
how the graphics pipeline computes gl_FragCoord.w, details
the mathematical connection between clip-space \(w\) and view-space coordinates in
perspective projections, and demonstrates how to use this relationship
to reconstruct linear view-space depth efficiently without inverting the
projection matrix.
The Coordinate Pipeline: From View Space to Clip Space
In a standard 3D rendering pipeline, vertices are transformed from model space into world space, then into view (eye) space using the view matrix. In view space, the camera is located at the origin looking down the negative Z-axis (by OpenGL convention), meaning that the depth of a visible point is represented by a negative Z value:
\[z_{view} < 0\]
To prepare the scene for perspective projection, the view-space position is multiplied by the projection matrix to yield clip-space coordinates:
\[\begin{bmatrix} x_c \\ y_c \\ z_c \\ w_c \end{bmatrix} = \mathbf{P} \begin{bmatrix} x_{view} \\ y_{view} \\ z_{view} \\ 1.0 \end{bmatrix}\]
A standard OpenGL perspective projection matrix has the following structure in its bottom rows:
\[\mathbf{P} = \begin{bmatrix} \dots & \dots & \dots & \dots \\ \dots & \dots & \dots & \dots \\ 0 & 0 & A & B \\ 0 & 0 & -1 & 0 \end{bmatrix}\]
Because the fourth row is \(\begin{bmatrix} 0 & 0 & -1 & 0 \end{bmatrix}\), the resulting clip-space \(w\)-component is derived strictly from the view-space \(z\)-component:
\[w_c = -z_{view}\]
If the camera is configured to look down the positive Z-axis, this row might be \(\begin{bmatrix} 0 & 0 & 1 & 0 \end{bmatrix}\), yielding \(w_c = z_{view}\). In either case, \(w_c\) directly stores the distance of the vertex along the viewing axis.
Rasterization and gl_FragCoord.w
Before primitives reach the fragment shader, perspective division is performed by dividing the clip-space coordinates by \(w_c\) to produce Normalized Device Coordinates (NDC):
\[\mathbf{p}_{ndc} = \left( \frac{x_c}{w_c}, \frac{y_c}{w_c}, \frac{z_c}{w_c} \right)\]
During the rasterization stage, vertex attributes are interpolated across the surface of the primitive. Because screen-space interpolation is non-linear in perspective projections, the hardware interpolates values multiplied by \(1/w_c\) and continuously tracks \(1/w_c\) at each pixel.
According to the OpenGL Shading Language (GLSL) specification, the
built-in variable gl_FragCoord contains the window-space
coordinates of the fragment:
gl_FragCoord.x: Window-relative X coordinate.gl_FragCoord.y: Window-relative Y coordinate.gl_FragCoord.z: Fragment depth mapped to the \([0.0, 1.0]\) depth range.gl_FragCoord.w: The interpolated reciprocal of clip-space \(w\), specifically evaluated as \(\frac{1}{w_c}\).
Mathematical Relationship to View-Space Depth
Substituting the standard perspective projection relationship (\(w_c = -z_{view}\)) into the definition of
gl_FragCoord.w yields:
\[\text{gl\_FragCoord.w} = \frac{1}{w_c} = \frac{1}{-z_{view}}\]
This means gl_FragCoord.w is precisely the reciprocal of
the distance from the camera plane to the fragment.
To recover the actual view-space \(z\) coordinate in a fragment shader, you
take the reciprocal of gl_FragCoord.w:
\[-z_{view} = \frac{1.0}{\text{gl\_FragCoord.w}}\]
\[z_{view} = -\frac{1.0}{\text{gl\_FragCoord.w}}\]
If you define linear depth as a positive distance extending forward from the camera (\(d = -z_{view}\)):
\[d = \frac{1.0}{\text{gl\_FragCoord.w}}\]
Practical Application in Shaders
Reconstructing linear depth from the standard depth buffer value
(gl_FragCoord.z) requires non-linear algebra involving the
near (\(n\)) and far (\(f\)) clipping planes:
// Reconstructing linear depth from gl_FragCoord.z
float ndcZ = gl_FragCoord.z * 2.0 - 1.0;
float linearDepth = (2.0 * near * far) / (far + near - ndcZ * (far - near));Because gl_FragCoord.w already contains \(\frac{1}{w_c}\), retrieving the linear
camera-to-fragment distance along the viewing axis requires only a
single division:
// Direct view-space depth extraction
float linearDepth = 1.0 / gl_FragCoord.w;
vec3 viewSpacePosition = vec3(viewRay.xy * linearDepth, -linearDepth);Orthographic Projection Caveat
The equivalence of \(w_c\) to view-space depth holds true only for perspective projections. In an orthographic projection, parallel lines do not converge, and the fourth row of the projection matrix is \(\begin{bmatrix} 0 & 0 & 0 & 1 \end{bmatrix}\). Consequently, \(w_c = 1.0\) for all vertices, resulting in:
\[\text{gl\_FragCoord.w} = 1.0\]
In orthographic mode, gl_FragCoord.w provides no depth
information, and linear depth must instead be derived directly from
gl_FragCoord.z.