GLSL dFdx vs dFdy: Pixel Gradients Explained
In GLSL, dFdx and dFdy are built-in
derivative functions used to compute the rate of change of a given
variable across adjacent pixels in screen space. While both evaluate
gradients across the GPU's hardware-level \(2
\times 2\) pixel quads, dFdx measures variation
strictly along the horizontal (X) axis, whereas dFdy
measures variation along the vertical (Y) axis. Understanding how these
functions sample neighboring fragment values allows developers to
generate procedural patterns, compute surface normals dynamically, and
perform analytical anti-aliasing.
How Fragment Derivatives Work
GPUs process fragments in parallel groups called "quads," which are \(2 \times 2\) grids of adjacent pixels. Because all four fragments in a quad execute the same fragment shader simultaneously, the GPU can compare the value of any variable across neighboring pixels:
dFdx(p): Computes the partial derivative of expressionpwith respect to the horizontal screen coordinate (\(x\)). It subtracts the value ofpin the left pixel from the value in the right pixel within the quad: \[\frac{\partial p}{\partial x} \approx p(x + 1, y) - p(x, y)\]dFdy(p): Computes the partial derivative of expressionpwith respect to the vertical screen coordinate (\(y\)). It subtracts the value ofpin the bottom/top pixel from the adjacent vertical pixel within the quad: \[\frac{\partial p}{\partial y} \approx p(x, y + 1) - p(x, y)\]
Depending on the hardware and the target API (such as OpenGL vs.
Vulkan), the positive direction of the vertical axis (\(y\)) can vary between pointing upward or
downward in window coordinates, directly affecting the sign of the value
returned by dFdy.
Key Differences
| Feature | dFdx |
dFdy |
|---|---|---|
| Axis of Calculation | Horizontal screen axis (\(X\)) | Vertical screen axis (\(Y\)) |
| Comparison Direction | Left-to-right adjacent pixels | Top-to-bottom / bottom-to-top adjacent pixels |
| Primary Sensitivity | Sensitive to horizontal changes | Sensitive to vertical changes |
| Orientation Dependence | Invariant across standard APIs | Sign may flip depending on clip space/framebuffer conventions |
Common Applications
1. Calculating Flat Face Normals
When a 3D mesh lacks per-vertex normal attributes, you can calculate the surface normal entirely within the fragment shader by taking the cross product of the screen-space derivatives of the world- or view-space position:
vec3 normal = normalize(cross(dFdx(v_worldPosition), dFdy(v_worldPosition)));Because dFdx supplies a tangent vector along the
horizontal axis and dFdy supplies a bitangent vector along
the vertical axis, their cross product yields the geometric surface
normal perpendicular to both.
2. Procedural Anti-Aliasing
When generating procedural patterns (like stripes, grids, or
checkerboards), sharp color boundaries cause aliasing artifacts.
Combining dFdx and dFdy with the GLSL function
fwidth calculates the total rate of change over a single
pixel footprint:
// fwidth(v) is equivalent to: abs(dFdx(v)) + abs(dFdy(v))
float delta = fwidth(v_uv.x);
float edge = smoothstep(0.5 - delta, 0.5 + delta, v_uv.x);3. Manual Mipmap Selection
Texture sampling functions like texture() implicitly use
dFdx and dFdy on the supplied texture
coordinates to determine how quickly a texture is shrinking or
stretching across pixels. If derivatives change rapidly—meaning the
texture coordinates jump across a small screen distance—the GPU selects
a lower-resolution mipmap level to reduce aliasing.
Coarse vs. Fine Derivatives
Modern GLSL versions (GLSL 4.5+ or via the
GL_AMD_shader_trinary_minmax /
GL_ARB_derivative_control extensions) provide explicit
variants:
dFdxCoarse/dFdyCoarse: Computes one derivative per \(2 \times 2\) quad, sharing the result among all four fragments to save processing overhead.dFdxFine/dFdyFine: Computes unique derivatives for each pixel row or column within the quad, providing higher-precision results at a slight computational cost.
Calling standard dFdx or dFdy allows the
driver and GPU implementation to choose between coarse and fine
approximations based on internal performance heuristics.