Skip to content
Fundamental · NoiseIntermediate

Value noise

Put a random value at every grid corner and blend between them, for smooth randomness that varies across space.

How it works

A hash gives each input an unrelated value, so neighboring pixels jump from dark to light at random. Most natural things are not like that: a cloud, a stain or a rock surface varies smoothly from point to point and only looks random at a larger scale. Noise is randomness with that property. Value noise is its simplest form: hash the corners of a grid and blend between them.

Corners and blending

Split the position into a cell and a position inside it, the same floor and fract split used for tiling. Each of the cell’s four corners gets a random value from the hash. The pixel’s value is a blend of the four, weighted by how close it is to each:

float valueNoise(vec2 p) {
    vec2 i = floor(p); // the cell's bottom-left corner
    vec2 f = fract(p); // where the pixel is inside the cell, 0..1

    float a = hash21(i);                   // bottom left
    float b = hash21(i + vec2(1.0, 0.0));  // bottom right
    float c = hash21(i + vec2(0.0, 1.0));  // top left
    float d = hash21(i + vec2(1.0, 1.0));  // top right

    vec2 u = f * f * (3.0 - 2.0 * f);      // smoothstep fade, per axis
    return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

The two inner mix calls blend along x on the bottom and top edges, and the outer one blends those two results along y. This is bilinear interpolation, built from mix. hash21 is the sine hash from the hash fundamental; any hash that maps a cell to 0..1 works. Neighboring cells share their corners, so the result is continuous across cell borders: a pixel on the border gets the same two corner values from either side.

The scale of p sets the size of the features. valueNoise(uv * 8.0) has about eight blobs across the screen; uv * 40.0 makes it fine grained.

Why the fade curve

Blending with f directly is linear interpolation. It is continuous, but its slope jumps at every cell border, and the eye picks up those creases as a faint grid of straight lines and diamond shapes. Passing f through f * f * (3.0 - 2.0 * f) first, the same cubic that smoothstep uses, makes the slope zero at both ends of the cell, so neighboring cells meet without a kink.

The cubic’s second derivative still jumps at the borders. That is invisible in a flat color, but it shows once the noise is used as a height and lit, because lighting depends on the slope and the slope’s rate of change. The quintic fade fixes that, at a couple more multiplications:

vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); // 6f⁵ − 15f⁴ + 10f³

Use the cubic for colors and masks, and the quintic when the noise becomes geometry or a normal.

What it looks like, and its limits

Value noise ranges from 0 to 1, though values near the ends are rare because they need four corners near the same end. It looks soft and slightly blocky: the bright and dark spots sit on the grid corners, and if you look for it you can see the grid’s alignment. That is acceptable for a dissolve mask, a flicker or anything that moves, and it is the cheapest noise there is: four hashes and three mixes.

When the grid shows, the usual fix is gradient noise, the next fundamental, which hides the lattice better for a little more math. Adding several scaled copies of either noise gives the richer detail of fBm.

Using it

Noise is a value from 0 to 1, so it plugs into everything from the shaping fundamentals. Threshold it with smoothstep for blotches, compare it with a moving value for a reveal, or mix two colors with it:

float n = valueNoise(uv * 6.0);
// a soft edge that sweeps across as progress rises
float mask = smoothstep(progress - 0.05, progress + 0.05, n);
vec3 color = mix(vec3(0.1, 0.3, 0.1), vec3(0.4, 0.6, 0.2), n);

Animate it by moving the input (uv * 6.0 + vec2(time * 0.2, 0.0) scrolls it), or by blending between two noise fields over time. Adding time to only one axis of a 2D noise just slides the pattern; for noise that changes in place you need a 3D noise with time as the third axis, which works the same way with eight corners instead of four.

Further reading