Step and Smoothstep
Hard edges and soft transitions
The Step Function
In the world of shaders, we often need to make binary decisions. Is this pixel on or off? Inside or outside? Above or below a threshold? The step function gives us exactly this power.
step(edge, x) returns 0 if x is less than the edge value, and 1 if x is greater than or equal to the edge. It is the simplest possible decision: everything below the threshold gets zero, everything above gets one.
Interactive: Adjust the edge threshold
Returns 0 when x < 0.50, returns 1 otherwise
The mathematical definition is almost too simple to write down:
This sharp cutoff is incredibly useful. Want to paint the left half of your screen black and the right half white? step(0.5, uv.x) does exactly that. Want to show only pixels above a certain brightness? Step is your tool.
Drawing Lines with Step
Here is where step becomes powerful. By combining two step functions, you can isolate a range of values. If you want to highlight only the pixels where x is between 0.4 and 0.6, you can write:
float line = step(0.4, uv.x) - step(0.6, uv.x);The first step turns on everything above 0.4. The second step turns on everything above 0.6. Subtracting them leaves only the middle band.
Interactive: Draw a vertical line
step(0.47, uv.x) - step(0.53, uv.x)
This technique forms the basis of countless shader effects. Grid lines, stripes, patterns, borders: all built from clever combinations of step.
The Problem with Hard Edges
There is a catch. Step creates perfectly sharp transitions, but perfect sharpness does not look good on a pixel grid. Look closely at a step-based edge and you will see jagged, aliased pixels. This is because our mathematical edge falls at arbitrary positions relative to the pixel centers.
The solution is to soften the transition, letting it span more than a single pixel. This is where smoothstep enters.
Smoothstep: The Smooth Transition
smoothstep(edge0, edge1, x) is one of the most beloved functions in shader programming. It creates a smooth, gradual transition from 0 to 1 as x moves from edge0 to edge1.
Interactive: Adjust the smoothstep edges
Smooth transition from 0 to 1 between 0.30 and 0.70
The transition is not linear. It uses a special S-shaped curve called a Hermite interpolation:
This formula might look arbitrary, but it has two beautiful properties: the curve starts with zero slope and ends with zero slope. The transition eases in and eases out, creating a natural, organic feel.
Comparing Step and Smoothstep
The difference becomes clear when you see both functions side by side. Step is a cliff. Smoothstep is a gentle hill.
Step (blue) vs Smoothstep (green)
In code, the only difference is specifying a width for the transition:
// Hard edge
float hard = step(0.5, uv.x);
// Soft edge spanning 0.45 to 0.55
float soft = smoothstep(0.45, 0.55, uv.x);The wider the gap between edge0 and edge1, the softer the transition. A narrow gap approaches the sharpness of step. A wide gap creates a gentle gradient.
Anti-Aliasing in Practice
Anti-aliasing is the art of hiding pixel boundaries. When we draw a shape with hard edges, those edges look jagged because pixels are either fully on or fully off. Smoothstep lets us partially fill edge pixels based on how much of the shape actually covers them.
Hard edge (left) vs anti-aliased edge (right)
Look at the edge quality: smoothstep creates smoother boundaries
The standard technique is to make the transition width roughly one pixel wide. The built-in derivative functions fwidth help calculate this, but the principle is simple: blur the edge just enough to hide the pixel grid.
When to Use Which
Use step when you need:
- Crisp, intentionally pixelated edges
- Boolean logic in your shader
- Maximum performance (step is marginally faster)
Use smoothstep when you need:
- Smooth, anti-aliased edges
- Soft transitions for artistic effect
- Blending between states
Most of the time, you will reach for smoothstep. Its smooth curves simply look better on screen.
Key Takeaways
step(edge, x)returns 0 below the edge, 1 above: a hard binary cutoff- Combining steps creates bands:
step(a, x) - step(b, x)isolates a range smoothstep(edge0, edge1, x)creates smooth S-curve transitions- The smoothstep formula has zero derivatives at its endpoints, creating natural easing
- Smoothstep is essential for anti-aliasing and avoiding jagged edges