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 expression p with respect to the horizontal screen coordinate (\(x\)). It subtracts the value of p in 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 expression p with respect to the vertical screen coordinate (\(y\)). It subtracts the value of p in 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.