Noise Functions
Procedural noise needs to be "random but continuous" — neighboring pixels should be similar, with smooth large-scale variation. Value Noise is the most fundamental way to achieve this:
How value noise works
Divide the plane into a regular grid. Assign a pseudo-random value to each grid corner (integer coordinate). The current pixel falls somewhere inside a grid cell — take the four corner values and interpolate between them with a smooth curve. That is value noise.
floor(st) extracts the grid corner (integer part). fract(st) gives the pixel's position inside the cell (0 to 1).
Why use ff(3-2f) instead of linear interpolation
Linear interpolation with f directly produces visible creases at grid boundaries because the derivative is discontinuous there.
f * f * (3.0 - 2.0 * f) is the smooth step polynomial — it has a zero derivative at both 0 and 1, so the transition is smooth with no visible seams. This is the core quality difference between noisy-looking and smooth value noise.
Three noise types side by side
- Left panel: Random noise (
random(pos * 10.0)) — pixels are fully independent, looks like TV static - Middle panel: Value noise (
valueNoise(pos)) — smooth, continuous blobs where neighboring pixels are correlated - Right panel: Gradient noise (
perlinNoise(pos)) — more natural than value noise, more uniform contrast
Try changing it
| Change | Effect |
|---|---|
pos * 5.0 → pos * 10.0 | Double frequency, smaller patches |
pos * 5.0 → pos * 2.0 | Half frequency, larger patches |
u_time * 0.5 → u_time * 2.0 | Faster scrolling |
Change f*f*(3.0-2.0*f) back to f | Value noise shows visible creases at grid edges |
Exercise
In the valueNoise function, try changing vec2 u = smoothstep(0.0, 1.0, f); to the equivalent polynomial f * f * (3.0 - 2.0 * f) and verify the result looks the same.
Answer Breakdown
smoothstep(0.0, 1.0, f) and f * f * (3.0 - 2.0 * f) are mathematically identical — smoothstep uses this polynomial internally. Both produce a zero derivative at f=0 and f=1, giving a seam-free transition.
Writing the polynomial directly makes it explicit that a smooth interpolation is happening, without relying on how the GPU implements smoothstep. In practice both forms work equally well.
Try stacking two octaves manually: valueNoise(pos) * 0.5 + valueNoise(pos * 2.0) * 0.25 to see a hand-built FBM effect.