How Does textureGrad Work in GLSL?
The textureGrad function in GLSL provides explicit
control over texture sampling by allowing developers to manually specify
screen-space partial derivatives (\(\partial
P/\partial x\) and \(\partial
P/\partial y\)) instead of relying on automatic hardware
evaluations. This capability resolves critical visual artifacts—such as
mipmap selection errors along texture coordinate discontinuities—and
permits texture sampling within non-uniform control flow where automatic
derivative calculation produces undefined behavior.
Automatic Derivatives vs. Explicit Gradients
Under standard execution, fragment shaders evaluate functions like
texture() by computing implicit screen-space derivatives
across \(2 \times 2\) pixel quads. The
GPU measures the rate of change of texture coordinates between adjacent
pixels using functions analogous to dFdx() and
dFdy(). These partial derivatives dictate the level of
detail (LOD) and the appropriate mipmap level required to minimize
aliasing while preserving sharpness.
When texture coordinates change abruptly—such as across UV seams, wrapped coordinates, or atlas boundaries—implicit derivative calculations compute an artificially massive rate of change. The hardware interprets this jump as extreme minification and selects the lowest-resolution mipmap, causing visible blurred lines along the seam.
Syntax and Operation of textureGrad
The textureGrad function bypasses implicit quad
comparisons by accepting user-provided gradient vectors:
vec4 textureGrad(
sampler2D sampler,
vec2 P,
vec2 dPdx,
vec2 dPdy
);sampler: The texture sampler to query.P: The texture coordinate to sample at the current fragment.dPdx: The explicit partial derivative of the texture coordinate with respect to screen-space \(x\).dPdy: The explicit partial derivative of the texture coordinate with respect to screen-space \(y\).
By supplying custom vectors for dPdx and
dPdy, the hardware derives the footprint of the pixel in
texture space directly from your calculations, ignoring the coordinates
evaluated in neighboring fragments of the \(2
\times 2\) quad.
Primary Use Cases
Fixing UV Discontinuities and Seams
When wrapping textures around spheres, cylinders, or computing
continuous fractals, coordinates often wrap from \(1.0\) back to \(0.0\). Evaluating derivatives across the
seam yields a derivative magnitude near \(1.0\) instead of \(\approx 0.0\). Computing gradients prior to
wrapping or filtering out the wrap step with fract()
preserves continuous gradients:
vec2 uv = rawUV;
vec2 dx = dFdx(uv);
vec2 dy = dFdy(uv);
// Apply wrapping or atlas clamping
vec2 wrappedUV = fract(uv);
// Sample using the pre-wrap derivatives
vec4 color = textureGrad(u_Texture, wrappedUV, dx, dy);Dynamic Branching and Non-Uniform Control Flow
Standard GLSL texture lookups require uniform execution across all
four pixels in a quad. If fragments within the same quad execute
divergent branches, hardware derivatives become undefined.
textureGrad enables safe texture lookups inside non-uniform
conditional branches by computing the derivatives before entering the
branch and passing them explicitly into the sampling call.