Cellular noise (Worley / Voronoi)
The distance to the nearest of many scattered points, for cells, scales, cracks and stained glass.
How it works
Scatter points across the plane and color every pixel by its distance to the nearest point. Close to a point it is dark, and it brightens toward the borders where two points are equally near. The borders form a Voronoi diagram, the pattern of cells you see in giraffe skin, dried mud, foam and stained glass. Steven Worley described this as a texture basis in 1996, so the noise is also called Worley noise.
One point per cell
Checking every point for every pixel would be far too slow. The trick is to place exactly one point in each grid cell,
at a random offset inside it from a hashA function that scrambles its input into a value that looks random but is always the same for the same input. Shaders use it for per-pixel and per-cell randomness. Hash A function that scrambles its input into a value that looks random but is always the same for the same input. Shaders use it for per-pixel and per-cell randomness.h = fract(sin(dot(p, k)) × big) of the cell. Then the nearest point must be in the
pixel’s own cell or one of its eight neighbors, so each pixel checks nine points:
vec2 hash22(vec2 cell) {
uvec2 q = uvec2(ivec2(cell));
// the integer hash from the hash fundamental
uint h = mix32(q.x ^ mix32(q.y));
return vec2(h >> 16, h & 0xffffu) / 65536.0; // two values in 0..1 from one hash
}
vec2 cellular(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float f1 = 8.0; // nearest distance so far
float f2 = 8.0; // second nearest
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 o = vec2(float(x), float(y)); // which neighbor
vec2 point = o + hash22(i + o); // its point, relative to this cell
float d = length(point - f);
if (d < f1) {
f2 = f1;
f1 = d;
} else if (d < f2) {
f2 = d;
}
}
}
return vec2(f1, f2);
}
Everything is measured relative to the pixel’s cell ( Vector A group of two to four numbers handled as one value (vec2, vec3, vec4). The same type holds a position, a direction, a color or a UV.f and o), so the numbers stay small wherever the pattern is.
length from the vectorA group of two to four numbers handled as one value (vec2, vec3, vec4). The same type holds a position, a direction, a color or a UV.
With the point anywhere in its cell, a point two cells away can occasionally be nearer than all nine checked, which
shows as a rare short straight seam. Scaling the offset down (0.5 + 0.8 * (hash22(i + o) - 0.5)) keeps points away
from the cell borders and makes it far rarer, at the price of a slightly more regular pattern.
The second-nearest point falls outside the nine cells much more often than the nearest one does, so f2, and the
F2 − F1 walls below, show these seams far more often than F1. For looks built on F2, scale the offset down as above or
search a 5×5 block (25 cells) instead of 3×3.
F1, F2 and their combinations
f1 is the distance to the nearest point, f2 the distance to the second nearest. Each gives a different look:
- F1 is dark spots on bright borders: cells, bubbles, scales.
1.0 - f1inverts it into round bumps. - F2 on its own has a more angular, faceted look, like crystals or crumpled paper.
- F2 − F1 is zero exactly on the borders between cells and grows toward the middle. A
smoothstepon it draws the cell walls:1.0 - smoothstep(0.0, 0.05, f2 - f1).
F2 − F1 is a cheap stand-in for the true distance to the border, not the same thing: lines drawn from it get thinner and thicker along their length. The exact distance to the border takes a second pass over the neighbors, measuring the distance to the bisecting line between the nearest point and each other one; the Voronoi edges article in the sources walks through it. Use the cheap version for organic patterns and the exact one for crisp, even outlines.
Cell ids
Remember which cell the nearest point came from and you get an id per region, the base of stained glass and mosaics:
vec2 nearest = vec2(0.0);
// inside the loop, when a new nearest is found:
// nearest = i + o;
// a random color per cell
vec3 tint = vec3(hash22(nearest), hash22(nearest + 7.0).x);
Every pixel of a cell gets the same id, so the color is flat inside it and changes at the borders.
Moving and variants
Animate the points by swinging each one with its own phase: point = o + 0.5 + 0.4 * sin(time + 6.2831853 * hash22(i + o)). Each coordinate stays between 0.1 and 0.9, inside the cell, so the nine-cell search misses as rarely as with the
scaled-down offset above. Using another distance changes the cell shapes: Manhattan distance (abs(d.x) + abs(d.y)) and
Chebyshev distance (max(abs(d.x), abs(d.y))) give cells with straight horizontal, vertical and diagonal walls, a more
mechanical look. The 3D version checks 27 cells and gives the same patterns on the surface of any mesh without UVs.