Intrinsics1 min read

fwidth

Computes the sum of absolute derivatives.

Reading Time
1 min
Word Count
174
Sections
12
Try It Live

Test fwidth in a live shader

Open the playground, start from a visual preset, and wire fwidth into the fragment stage to see how it behaves with real values.

Open Playground

Live Demo

The fwidth function returns the sum of the absolute partial derivatives in x and y, computed as abs(ddx(x)) + abs(ddy(x)). This is useful for anti-aliasing and filter width calculations.

Signature

bwsl
fwidth :: (T x) -> T {...}

Where T can be float, float2, float3, or float4.

Parameters

ParameterTypeDescription
xTThe value to compute filter width for

Return Value

Returns abs(ddx(x)) + abs(ddy(x)).

Example

bwsl
pipeline AntialiasedGrid {
@fragment_only
fragment {
// Draw anti-aliased grid lines
float2 uv = input.uv * 10.0; // 10x10 grid
float2 grid = abs(fract(uv) - 0.5);
// Use fwidth for consistent line width regardless of angle
float2 fw = fwidth(uv);
float2 lines = smoothstep(0.5 - fw, 0.5, grid);
float gridPattern = min(lines.x, lines.y);
output.color = float4(float3(gridPattern), 1.0);
}
}

Common Use Cases

Anti-Aliased Lines

bwsl
// Consistent line width
float lineWidth = 0.02;
float lineDist = abs(uv.y - 0.5);
float aa = fwidth(uv.y);
float line = 1.0 - smoothstep(lineWidth - aa, lineWidth + aa, lineDist);

SDF Anti-Aliasing

bwsl
// Smooth SDF edges
float sdf = signedDistance(position);
float edgeWidth = fwidth(sdf) * 0.5;
float shape = 1.0 - smoothstep(-edgeWidth, edgeWidth, sdf);

Procedural Textures

bwsl
// Anti-aliased checkerboard
float2 checker = step(0.5, fract(uv * 4.0));
float pattern = abs(checker.x - checker.y);
float filtered = pattern * (1.0 - fwidth(uv.x * 4.0));

Text Rendering

bwsl
// Distance field text
float dist = sample(sdfFont, uv).r;
float width = fwidth(dist);
float alpha = smoothstep(0.5 - width, 0.5 + width, dist);

Wireframe Overlay

bwsl
// Barycentric wireframe
float3 bary = input.barycentric;
float3 fw = fwidth(bary);
float3 edges = smoothstep(float3(0.0), fw * wireWidth, bary);
float wire = 1.0 - min(min(edges.x, edges.y), edges.z);

Why fwidth?

fwidth gives a screen-space measure of how fast a value is changing. This makes it perfect for determining how wide to make anti-aliased transitions—the faster a value changes, the wider the transition should be.

Alternative Calculation

For gradient-based calculations, you might use length(float2(ddx(x), ddy(x))) instead, which gives the Euclidean magnitude rather than the Manhattan distance.

Compiled Output

When compiled to GLSL:

glsl
fwidth(x)

When compiled to HLSL:

hlsl
fwidth(x)

When compiled to SPIR-V:

Uses OpFwidth instruction.

See Also