Temporal Accumulation — the Grain Fix

The cloud surface, before & after · real frames off the live GPU · slow, legible camera moves · the denoiser the only difference

The clouds had a fine grainy sparkle crawling over their surface — like TV static on the vapour. It comes from two things we do to keep them fast: we render at half resolution and jitter each ray a little. Cheap, but grainy.

The fix is temporal accumulation — instead of trusting one noisy frame, we blend each frame with the last few. The jitter is different every frame, so averaging a handful cancels the sparkle and leaves the real shape. Same trick Godot's clouds use; it's why their stills look creamy.

These clips are shot from above, flying over the cloud sea — the vantage where this cloud configuration reads (there's an honest note at the bottom about why it reads that way and not from the ground). Every move is slow on purpose (~8–10 second loops) so you can actually read what's happening. Denoiser OFF on the left, ON on the right — same camera path, the grain the only difference.

Grain (surface sparkle, lower = smoother)
0.539 0.389
below Godot's own 0.404 — at the reference bar
The darks (the shadows that give form)
unchanged
denoised without flattening shape or lifting the floor
Cost on the GPU
~0 ms
one extra half-res pass — effectively free

Orbit

circling the deck, slow

A slow arc around the cloud tops. On the left the lit faces crawl with sandy speckle; on the right that static is gone and the crowns read as smooth vapour — and the shadowed folds keep their depth.

DENOISER OFFthe raw grain
DENOISER ONtemporal accumulation

Truck

slide across the tops

Sliding sideways over the deck — a pure translation, the hard case for this kind of denoiser. Watch the edges where the sky shows through: no smearing, no ghost-trails, the grain stays gone.

DENOISER ONpure translation — no ghosting

Boom

rise & look down

Rising over the sea and tilting down — the classic "flying above the clouds" reveal. Slow enough to read the form; the surface stays creamy the whole way up.

DENOISER ONrise-and-look-down over the sea

From the ground

the honest truth

Here's the part we're not hiding. This is a real player standing on the ground (eye height ~0), tilting up. In this cloud configuration the sky overhead is nearly empty — the clouds are dense up high and sparse underneath, so from directly below there's little to see. These clouds are a fly-over deck, gorgeous from above and thin from where a player actually stands.

GROUND PLAYERlooking up — mostly empty sky

⚠ The gate this opens

The grain fix is real and done. But the clip above surfaces a bigger truth: this cloud configuration doesn't read from the ground, and our game is a ground game. That's not a knob-tweak — it's a hard requirement on the cloud system's altitude/density (currently parked with the colour/weather work).

The gate: before clouds are ever called "done," the first test isn't the fly-over — it's a player standing on real terrain, looking up, and the sky having something worth looking at. (Grounded in the numbers: this lab config runs the deck from ~1,500 to ~25,000 units up, dense high and sparse low — a high fly-over deck, not a ground-readable ceiling.)

How the grain fix works — in plain terms

  1. Where was this pixel last frame? For each pixel we know the cloud's 3-D position, so we re-project it through last frame's camera to find the same spot in the previous picture.
  2. Is that history trustworthy? If last frame couldn't see that spot — off-screen, behind us, or an edge moved across it — we throw the old value away and start fresh. That's what stops smearing.
  3. Blend. Otherwise we mix 70% smoothed history with 30% new frame. The jitter differs every frame, so over a handful the grain averages out and the true shape stays.

Ported line-faithful from Godot's SunshineClouds2 (SunshineCloudsCompute.glsl · accumulation_decay = 0.7).

Captured at 960×540, 30 fps, ~8–10 s loops, from the live three.js port (module=5 · atmo off), denoiser on by default. Try it at the live lab — add ?accum=0 to switch the denoiser off and see the raw grain. Temporal accumulation fixes the grain only; colour, weather, and the cloud's ground-readability are a separate, deliberately parked job.