dFdxFine in GLSL for Un-aliased Normal Gradients
This article explores how the GLSL derivative function
dFdxFine is utilized to compute accurate, un-aliased normal
gradients across high-frequency geometry. In modern GPU fragment
pipelines, screen-space derivatives frequently introduce blocky
artifacts and specular aliasing on dense geometric detail. By generating
fine-grained differences instead of coarse quad-averaged values,
dFdxFine preserves local surface variations, stabilizes
normal reconstruction, and suppresses high-frequency rendering
artifacts.
Modern GPUs execute fragment shaders concurrently within 2x2 pixel
groupings called quads. Standard screen-space derivative functions, such
as dFdx, approximate partial derivatives by taking finite
differences between neighboring fragments within this quad. However, the
OpenGL and Vulkan specifications allow the driver to implement standard
dFdx as dFdxCoarse, meaning a single
difference is calculated across the entire quad and replicated across
all four pixels.
When applied to high-frequency geometry—such as dense triangle meshes, procedural height fields, or sharp geometric edges—this coarse approximation causes severe spatial aliasing. Because four adjacent fragments share identical derivative vectors, derived surface properties show noticeable 2x2 pixel stepping patterns. When computing analytical normals directly from screen-space position data via cross products, coarse derivatives produce a flat, faceted appearance across curved or high-frequency details. This artificial faceting results in unstable specular highlights that flicker during camera movement.
The dFdxFine function eliminates this behavior by
enforcing independent finite differences for each horizontal pair within
the 2x2 quad. Rather than distributing a single calculated slope across
the whole quad, dFdxFine calculates:
- The difference between fragment (0,0) and fragment (1,0) for the top row.
- The difference between fragment (0,1) and fragment (1,1) for the bottom row.
Combined with dFdyFine, this approach yields per-row and
per-column gradients that accurately reflect local sub-quad curvature.
When reconstructing geometric normals dynamically:
vec3 dPdx = dFdxFine(vPosition);
vec3 dPdy = dFdyFine(vPosition);
vec3 normal = normalize(cross(dPdx, dPdy));This ensures that the normal gradient aligns directly with the geometric change occurring at that exact pixel boundary.
In high-frequency displacement and bump mapping,
dFdxFine is equally critical. Procedural noise functions
and detailed normal maps frequently oscillate at or near the Nyquist
frequency of the display buffer. Evaluating these gradients with coarse
derivatives amplifies discontinuities, whereas dFdxFine
provides the exact slope of the parameter across adjacent pixels. This
localized precision prevents normal gradients from snapping abruptly
between quads, preserving smooth shading transitions.
While dFdxFine can introduce minor compute overhead
compared to coarse derivatives on select architectures, its use is
essential whenever geometry frequency exceeds quad resolution. It
replaces uniform quad approximations with distinct sample-to-sample
deltas, serving as the foundational mechanism for artifact-free,
dynamically generated normal fields on intricate 3D surfaces.