Post 099

Turning the Wind Up to Eighteen

The WaterSurface node now drives a real Tessendorf FFT ocean: a JONSWAP spectrum, four wave cascades, choppy crests, whitecap foam, and a displaced surface that depth-sorts against your scene. Set wind to 1 and get a pond; set it to 18 and get a storm.

The WaterSurface node has had a wind speed pin since spring. It accepted any number you liked, documented itself beautifully, and changed nothing, because behind it sat an FFT pipeline that allocated every texture it needed and then never dispatched a single pass. The two showcase patches, calm pond and storm ocean, rendered byte-identical images. You could not tell one metre per second from eighteen with a microscope.

Now you can tell from across the room.

A spectrum with opinions

Type a wind speed and the engine bakes a JONSWAP wave spectrum for it, the same empirical model oceanographers fitted to the North Sea in the seventies. Fetch and depth pins feed the same equations, so a small lake at 5 m/s genuinely carries shorter, steeper waves than open ocean at the same wind. Four cascades (2048 m swell down to 32 m ripples) evolve on the GPU every frame through a proper fast Fourier transform, sixteen butterfly stages per cascade, and come out the other end as a displacement field with sharp Tessendorf crests. The chop pin slides them from sinusoidal rollers to near-breaking peaks, and where the wave Jacobian says the surface is folding over itself, whitecap foam appears.

The surface itself is a camera-following grid that displaces in the vertex shader and shades through the same PBR path as every mesh in Lux. It writes real depth, which means the sky stays behind it, the fog attenuates it by distance, and a sphere dropped into the scene floats half-submerged with waves washing past it, in calm water, sitting perfectly still.

Archaeology corner

Code that has never run preserves its bugs the way amber preserves insects. The butterfly lookup table was stored sideways relative to the texture that read it. Its twiddle formula produced the same output for both halves of every butterfly pair, which is a bold reinterpretation of the Fourier transform. The lookup texture was declared filterable in a format no device we ship on can filter, so the very first bind group would have failed validation. Three distinct ways the pipeline could not have worked, all invisible, because the dispatch that would have revealed them was never recorded. The new tests check the GPU output against a CPU Fourier transform texel by texel, so the next bug of this species lives for about one keystroke.

What it buys you

An ocean with a wind knob. Calm and rough are now different shapes, not different stories; the sunset patch renders a sun-glitter path that scatters across the swell, and the foam threshold pin decides how angry the sea looks at any given wind. All four environment systems, sky, cloud, fog, water, now end in pixels. That sentence took three releases to become true and we intend to enjoy it.

← All posts