What Is gl_FragDepth in GLSL Fragment Shaders?

In OpenGL Shading Language (GLSL), gl_FragDepth is a built-in output variable that allows fragment shaders to manually set or override the depth value written to the depth buffer for a given fragment. By default, the GPU automatically computes and assigns a fragment's depth using the interpolated z-coordinate from vertex processing. Manually writing to gl_FragDepth gives developers fine-grained control over occlusion and z-testing, enabling advanced rendering techniques such as raymarched signed distance fields, spherical impostors, and custom depth bias effects, though it carries important performance implications regarding early depth optimizations.

Default Depth Computation vs. Manual Overrides

During standard rasterization, the fixed-function pipeline interpolates the clip-space coordinates of a primitive's vertices across the surface of each generated fragment. The resulting normalized device coordinate (NDC) depth is mapped to the viewport's depth range (typically \([0.0, 1.0]\)) and exposed to the fragment shader as the read-only variable gl_FragCoord.z. If the shader does not write to gl_FragDepth, the hardware automatically writes gl_FragCoord.z into the depth buffer when depth testing is enabled.

When a shader explicitly writes a value to gl_FragDepth, it overrides this automatic assignment:

#version 330 core

out vec4 FragColor;

void main()
{
    // Compute custom fragment color
    FragColor = vec4(1.0, 0.5, 0.2, 1.0);

    // Explicitly write a modified depth value
    gl_FragDepth = 0.5; 
}

The assigned value must be a floating-point scalar, generally clamped between \(0.0\) and \(1.0\), corresponding to the near and far clipping planes of the active depth buffer.

Common Use Cases for gl_FragDepth

Manual depth control is essential in several specialized rendering pipelines:

Performance Considerations and Early-Z Testing

Modern GPUs optimize rendering throughput using Early Depth Testing (Early-Z), where depth and stencil tests occur before the fragment shader executes. If a fragment is determined to be occluded by previously rendered geometry, the GPU discards it immediately, avoiding the computational cost of running complex lighting, texturing, or math operations.

Writing to gl_FragDepth disrupts this optimization. Because the final depth value depends on calculations inside the shader, the GPU cannot determine fragment visibility beforehand and must disable standard Early-Z testing. This forces the fragment shader to execute for all rasterized fragments, increasing overdraw costs and potentially degrading framerates.

Preserving Performance with Conservative Depth

To mitigate the performance impact of disabling Early-Z, GLSL (version 4.20 and newer, or via the GL_ARB_conservative_depth extension) provides depth layout qualifiers. These qualifiers inform the GPU about how gl_FragDepth will be modified relative to gl_FragCoord.z, allowing the driver to preserve partial Early-Z culling:

#version 430 core

// Inform the GPU that the custom depth is always greater than or equal to gl_FragCoord.z
layout (depth_greater) out float gl_FragDepth;

out vec4 FragColor;

void main()
{
    FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    gl_FragDepth = gl_FragCoord.z + 0.05;
}

Available layout qualifiers include:

Using these qualifiers allows the GPU hardware to perform conservative tests against the depth buffer before shader invocation, recovering much of the performance lost when manual depth writes are required.