astral
The sky and the weather of *Pastel Astral Dream* — everything above and between the islands. Serves shots 1, 2, 4, 5, 11 and 13.
@user::pad.astral
The sky and the weather of Pastel Astral Dream — everything above and between the islands. Serves shots 1, 2, 4, 5, 11 and 13.
| what | how it is drawn |
|---|---|
| the void, the starfield, the star showers, the pale ringed body with its turning rings, the nebula band | pad_sky, a sky-domain shader on the scene's Skybox |
| sugar snow — falling in act one, rising in act three | pad_snow, two volumes that ride the film's eye: a 2.2 m one for the macros and a 1500 m one for the wides |
| the nebula front and its crossing | pad_nebula, a slab of churning additive puffs flown along X by film.frontX(t) |
| the column of melt leaving the mesa | pad_column, coarse (reads from 4 km) and fine (shot 11 flies through it) |
| the sugar moths | pad_moth, a population with flight, a wingbeat and an updraught |
How it is built
Every population is one draw. The mesh is a lattice of degenerate quads — all
four vertices of a quad at one point — and the vertex stage hashes each quad's
world cell into a grain, a puff, a droplet or a moth, then places, animates and
billboards it. Nothing is simulated and nothing accumulates: a piece's position
is a closed-form function of the film's clock, which is what makes the whole sky
a pure function of t the way film.apply requires, and what lets the whole
astral cost a handful of material-property pushes per frame.
A piece's identity comes from the world cell it stands in rather than from the vertex, which is what lets a volume be snapped along with the camera without the field appearing to slide: a snap of one whole cell lands every piece on a cell that already looked like that.
The shaders read film_t + (v.time - uniforms.time) rather than film_t alone,
so the previous-frame evaluation the engine runs to take a motion vector lands
one frame back and the matter reports the velocity it is actually travelling at.
Driving it
build() and attach(film) are what the film calls. Beyond those:
local astral = require("@user::pad.astral")
astral.pin(33) -- hold the sky at one moment, whatever clock the world is on
astral.pin(nil) -- follow the film again
astral.paint() -- re-push @user::pad.palette into every astral material
pin exists because this world is authored by several sessions at once and any
of them seeking moves every slice — a frame taken to judge the sky is otherwise
a frame of whatever moment somebody else was looking at.
Where the numbers live
cell in POPS and cell in the matching material must agree: the shader
recovers a piece's identity with floor(world_position / cell), so the lattice
and the shader have to agree on where the cells are. Everything else that shapes
a population — its size, speed, density, fade distances — is on the material, and
every colour comes from @user::pad.palette through paint().
Scoped to this part · feeds back into the world's score.