Skip to content
Fundamental · ShapingBeginner

Step & smoothstep

Hard and soft thresholds, where every edge and mask starts.

How it works

A shape in a fragment shader is a decision every pixel makes on its own: am I inside or outside? step and smoothstep turn a continuous value, like a coordinate or a distance, into that decision.

step: a hard edge

step(edge, x) returns 0.0 when x is below edge and 1.0 otherwise. Fed a UV coordinate, it splits the screen in two:

float right = step(0.5, uv.x); // 0 on the left half, 1 on the right

Mind the argument order: the threshold comes first. Swapping the arguments flips the result, and so does 1.0 - step(...). Multiplying two steps keeps only the pixels where both are 1, which is how a band or a box starts:

float band = step(0.4, uv.y) * (1.0 - step(0.6, uv.y)); // 1 between 0.4 and 0.6

A step changes all at once, inside a single pixel. On an edge that isn’t perfectly horizontal or vertical that shows as jagged stairs (aliasing), and a moving edge flickers.

smoothstep: a soft edge

smoothstep(e0, e1, x) is 0 below e0, 1 above e1, and rises smoothly in between. Inside, it first remaps x to 0–1 with a clamped inverse lerp, then bends that line with the cubic 3t² − 2t³:

float t = clamp((x - e0) / (e1 - e0), 0.0, 1.0);
float s = t * t * (3.0 - 2.0 * t); // what smoothstep(e0, e1, x) returns

The curve is flat at both ends, so the transition blends into the regions around it with no visible kink. A narrow transition gives an anti-aliased edge; a wide one gives a glow or a fade. With p the centered, aspect-corrected coordinate from UV & aspect ratio:

float d = length(p);
float disc = 1.0 - smoothstep(0.49, 0.51, d); // a crisp disc of radius 0.5
float glow = 1.0 - smoothstep(0.0, 1.0, d);   // a soft falloff from the center

The GLSL specification leaves the result undefined when e0 is not smaller than e1, so a reversed edge that looks right on one GPU may break on another. For a falling edge, write 1.0 - smoothstep(e0, e1, x) rather than swapping the edges.

How narrow is narrow? A fixed width such as 0.02 is about one pixel at one screen size, and blurry or jagged at another. The robust answer measures one pixel in the units of d with screen-space derivatives (fwidth), which get their own fundamental later.

Further reading