How Does Smoothstep Work in GLSL?
The smoothstep function in GLSL performs smooth cubic
Hermite interpolation between two boundary values, clamping inputs
outside the range and eliminating sharp slope transitions. Unlike
standard linear interpolation, smoothstep enforces a zero
derivative at both endpoints, creating an ease-in, ease-out S-curve that
transitions continuously in both value and first derivative. This
mathematical property prevents visual creases in procedural textures,
lighting models, and shader animations.
Input Normalization and Clamping
The standard signature of the function is
smoothstep(edge0, edge1, x). Before evaluating any
polynomial curve, the function maps the input parameter \(x\) from the arbitrary interval \([\text{edge0}, \text{edge1}]\) into a
normalized unit interval \([0.0,
1.0]\).
To prevent extrapolation beyond the target bounds, the normalized value is clamped:
\[t = \text{clamp}\left(\frac{x - \text{edge0}}{\text{edge1} - \text{edge0}}, 0.0, 1.0\right)\]
If \(x \le \text{edge0}\), \(t\) resolves to \(0.0\). If \(x \ge \text{edge1}\), \(t\) resolves to \(1.0\). For all values in between, \(t\) represents the linear progress across the domain.
The Cubic Hermite Polynomial
Once the value is normalized into the range \([0.0, 1.0]\), GLSL applies a specific third-order polynomial known as a cubic Hermite interpolator:
\[S(t) = 3t^2 - 2t^3 = t^2(3 - 2t)\]
In GLSL source implementations, this is often computed directly as
t * t * (3.0 - 2.0 * t).
Evaluating this function at the unit boundaries yields:
- \(S(0) = 3(0)^2 - 2(0)^3 = 0.0\)
- \(S(1) = 3(1)^2 - 2(1)^3 = 1.0\)
Why Zero Derivatives Ensure Smoothness
The defining advantage of cubic Hermite interpolation over linear
interpolation (mix) lies in its rate of change. Taking the
first derivative of \(S(t)\) with
respect to \(t\) gives:
\[S'(t) = \frac{d}{dt}(3t^2 - 2t^3) = 6t - 6t^2 = 6t(1 - t)\]
Evaluating the first derivative at the boundaries reveals why the transition is smooth:
- \(S'(0) = 6(0)(1 - 0) = 0\)
- \(S'(1) = 6(1)(1 - 1) = 0\)
Because the derivative is zero at both \(t = 0.0\) and \(t = 1.0\), the slope of the curve matches the flat slope of the clamped regions outside the interval. This guarantees \(C^1\) continuity (a continuous first derivative) across the entire domain, preventing the visible Mach bands and angular seams that occur when linearly interpolating clamped data.
Higher-Order Continuity: Smootherstep
While smoothstep provides \(C^1\) continuity, its second derivative
\(S''(t) = 6 - 12t\) evaluates
to \(6\) at \(t = 0\) and \(-6\) at \(t =
1\), causing an instantaneous jump in acceleration at the
boundaries.
For scenarios requiring \(C^2\)
continuity (continuous second derivative), such as high-order surface
normal generation and Perlin noise, Ken Perlin introduced the quintic
polynomial known as smootherstep:
\[Q(t) = 6t^5 - 15t^4 + 10t^3 = t^3(t(6t - 15) + 10)\]
The first and second derivatives of \(Q(t)\) are zero at both \(t = 0.0\) and \(t = 1.0\), providing an even gentler transition that eliminates subtle curvature artifacts in graphics rendering.