Command Palette

Search for a command to run...

Hectal
Course outline
Topic 12.4Advanced ~23 min · step 4 of 7

Noise and Shaping Functions

In one line

Noise is random numbers made smooth: hash a grid, blend the corners (value noise), add smaller copies (fBm). Shaping functions bend a number's curve. Together they make clouds, rust, and the paint's metal flake.

0/7 · 0%

Think of it like this

A sheet of rough paper under your finger is random, but not jumpy: each bump leans gently into the next. White-noise TV static is jumpy. Noise functions give you the paper, not the static.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

hash
A function that turns a position into a repeatable pseudo-random number.
value noise
Hashed numbers on a grid, blended smoothly between grid points.
fBm
Fractal Brownian motion: several layers of noise, each twice as detailed and half as strong, added together.
octave
One layer in an fBm sum.
shaping function
A function such as pow or smoothstep that reshapes a number's range without changing its limits.

Step by step

01Measure three noises

The first example draws hash, value noise and fBm into a 32 × 32 float picture each and prints min, max, mean and the biggest jump between neighbouring pixels. Hash jumps by 0.899; value noise by only 0.164: that is what smooth means.

02Watch octaves add up

One octave of fBm has a maximum of 0.5 (the first amplitude). Five octaves can reach 0.97, and their mean moves to 0.57.

03The negative bug

The last lines print floor and int for four x values. They agree for positive x and differ for the negative ones: cell 0 would cover -1 to 1.

04Flakes on a ball

The second example is the paint's idea on a sphere: a hash of the object-space cell, turned into a tilt, then a specular highlight. The ball turns, the flakes are fixed to it and glint in turn.

Try it yourself

  1. 1

    Fewer flakes

    Change uScale from 40 to 15 in the ball example. The cubes are 6.7 cm wide, so the flakes become larger, like glitter. Smaller cells give more, finer flakes.

  2. 2

    Octaves

    Change fbm(uv, 5) to fbm(uv, 1) in the on-screen shader. The right third becomes as smooth as the middle one, just dimmer.

Code & diagrams

Hash, value noise and fBm, in numbers and on screen New tab

Left: hash. Middle: value noise. Right: fBm with 5 octaves. The text lines come from 32 × 32 float pictures.

idle

Console

Run the scene to see its console.

Expected console output

hash noise:      min 0.00 max 0.97 mean 0.61 | biggest step between neighbouring pixels 0.899
value noise:     min 0.02 max 0.99 mean 0.62 | biggest step between neighbouring pixels 0.164
fbm, 1 octave:   min 0.01 max 0.49 mean 0.31
fbm, 5 octaves:  min 0.10 max 0.82 mean 0.57
x = -1.5 -> floor: -2 | int: -1
x = -0.5 -> floor: -1 | int:  0
x =  0.5 -> floor:  0 | int:  0
x =  1.5 -> floor:  1 | int:  1
Metal flake on a ball New tab

A plain ShaderMaterial with a hand-written light. The build topic does the same inside a MeshPhysicalMaterial so the scene's lights apply.

idle

Console

Run the scene to see its console.

Expected console output

shaders: 1 | draw calls: 1

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

A hash that uses sin on huge numbers

In your own shader, hash each pixel of a 32 × 32 picture with fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453) and compare p = pixel with p = pixel * 100000.0.

terminal
$ Count the distinct values
── what you'll see ──
scale 1.0 distinct values: 538 of 1024
scale 100000.0 distinct values: 7 of 1024
Measured on this page's software GPU. Other GPUs have other sin precision, so the same shader can look random on one and patterned on another.

Myth vs fact

Myth

A shader can call random().

Fact

GLSL has no random function. What looks random is always a function of position (and time), which is why the same pixel gives the same value every frame.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    fBm is often given a lacunarity (the frequency multiplier, 2.0 here) and a gain (the amplitude multiplier, 0.5). 2.0 and 0.5 give the classic look; gain 0.7 gives rougher noise.

  • ▸

    Rotating or offsetting each octave a little hides the grid directions that value noise shows. A cheaper trick is to use a different cell offset per octave.

Remember this

  1. 1

    The problem this solves: a shader has no random(). Every pixel runs the same code, and the same input must give the same output (or a pattern would flicker between frames). You need a function that looks random but is a fixed rule, and that is smooth where you want smoothness.

  2. 2

    Hash. A hash turns a position into a repeatable number from 0 to 1 with a few multiplications and fract calls. The same cell always gives the same number. Hash alone is static: neighbouring cells are unrelated (the first panel on screen).

  3. 3

    Value noise. Hash the four corners of the cell a point is in, then blend them with mix, using a smoothed fraction f * f * (3.0 - 2.0 * f) so there are no creases at cell edges (the middle panel). fBm (fractal Brownian motion) adds copies of value noise, each twice as detailed (p *= 2.0) and half as strong (amplitude *= 0.5): big clouds plus small clouds plus tiny grain (the right panel). More octaves cost more: every octave is another noise call per pixel.

  4. 4

    Simplex and gradient noise are smoother and have fewer grid artefacts than value noise, but need more code. three.js ships a CPU SimplexNoise (three/addons/math/SimplexNoise.js) for building geometry in JavaScript; for shaders, copy a well-known GLSL implementation with its licence. Value noise is enough for this course.

  5. 5

    Shaping functions bend a 0 to 1 number: pow(x, 2.0) pushes values down (more dark), sqrt pulls them up, smoothstep makes an S-curve, sin repeats. The curve you apply after noise decides the look: pow(noise, 3.0) makes rare bright spots out of smooth cloud.

  6. 6

    Metal flake is a hash used as a surface tilt. Cut space into small cubes with floor(position * scale), hash each cube to a random direction, and tilt the normal by it. Flakes whose tilt faces the light and the eye sparkle. They must be computed from object space, so they stay on the bike when it turns.

  7. 7

    Edge cases. int() cuts towards zero but floor() goes down, so noise built on int() has a double-wide cell around 0 (the first example prints it). Hashes built on sin(big number) lose their randomness when positions get huge, because float precision runs out; the hash here uses only fract and multiplications.

Explain it without notes

01

Why is value noise smooth when hash noise is not?

02

Why compute flakes in object space instead of screen or world space?

Practice

01

Make fBm with 3 octaves and a gain of 0.7.

02

Turn the value noise into rust: dark brown where the noise is above 0.6.

Done when you can

  • Build value noise from a hash

  • Add octaves to make fBm and say what each costs

  • Explain why int() is wrong for cells

  • Say why flakes use object space