# blast_puff
Soft, domain-warped-fBm sprite used for the blast's fire (mode 0, additive) and lit smoke and dust
(mode 1, alpha). Each particle is a camera-facing card (`zero_billboard`) cut to a disc and turned
by its seed. Two-octave `fx_noise` fBm warps the coordinates, a four-octave fBm and a finer
two-octave fBm erode the disc from the rim, and the domain drifts with the particle's life so the
edge curls as it ages. Per-particle colour, life, seed and glow arrive on `data0`/`data1`. The
sprite is depth-faded against the scene (soft particles) over 1 unit wherever a surface lies behind
it; over the sky it keeps its full opacity.
- Mode 0 (fire) is brightest in the dense middle: the colour runs from 0.55 of the lane colour at
the thin edge to 1.25 of it plus a warm `(0.5, 0.32, 0.12)` boost scaled by glow at the core. It
returns the colour multiplied by its coverage, for additive blending.
- Mode 1 (smoke and dust) lights a sphere normal bumped by the noise: wrapped sun light, half the
ambient light, a rim catching the sun, self-shadowing from the noise, and an orange fire
underlight `(1.0, 0.4, 0.09)` that grows with the square of glow on the underside and core.
- A mode above 1.5 draws a debug ramp from blue to orange over world height 0 to 14.
The `wall_blast` graph (`blast/wall_blast.procGraph`) builds two materials from it: `fire_mat` (mode
0, additive) for the fireball and the white flash cards, and `smoke_mat` (mode 1, transparent) for
the smoke column and cap, the smoke trailing behind flying rock, the dust puffs where rock lands and
the dust ring rolling along the ground. The graph sizes each card from the particle's size and life
and writes `data1` as (life, a random value, a glow ramp over life, a constant erode bias).
The shader includes `@builtin::shaderModules.billboard`, `fx_noise` and `zero::scene_depth`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `mode` | float | 0 | Look selector, range 0 to 1: below 0.5 fire, 0.5 to 1.5 smoke and dust. |
| `vertexBoundsDilation` | float | 34 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | Colour; HDR for fire. |
| `data0.a` | Peak opacity. |
| `data1.x` | Life, 0 at birth to 1 at death; drifts the noise domain. |
| `data1.y` | Seed: turns the card and offsets the noise. |
| `data1.z` | Glow 0 to 1: the fire core boost in mode 0 and the fire underlight in mode 1. |
| `data1.w` | Erode bias, subtracted from the shape so a larger value thins the puff. |
# blast_shock
Ground shockwave disc: refracting ring edge plus a hot leading glare, driven by lanes (life, radius,
strength, seed). It draws on a flat disc lying on the ground. Near the disc's rim a thin band bends
the scene behind it: the shader reads the scene colour through `zero_scene_color_refract`, pushed
radially outward from the centre (with the vertical push scaled by 0.35 for the foreshortened
ground), by 46 times the band's amount (its profile times the life fade times strength). The band
has a steep outer front and a long soft trail toward the centre, and its width wobbles with a
three-octave `fx_noise` fBm around the circle. A warm white glare `(1.0, 0.86, 0.66)` sits on the
leading edge. The ring is held at a thickness in metres, so it thins relative to the disc as the
front runs out, and the whole effect fades with the square of the remaining life.
The `wall_blast` graph (`blast/wall_blast.procGraph`) draws two fronts with it through its
`shock_mat` material (transparent blend): one at strength 1.0 that grows to a radius of 30, and a
second at strength 0.55, starting 0.12 s later, that grows to 24. Each lives 1.5 s; the graph scales
a unit plane to the front's diameter and writes `data0` as (life, radius, strength, random seed).
The shader includes `fx_noise` and `zero::scene_color`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `unused` | float | 0 | The one entry properties.yaml lists (range 0 to 1). Every value the shader reads comes from the lanes below. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 to 1. |
| `data0.y` | Radius of the front in metres; sets the ring's width relative to the disc. |
| `data0.z` | Strength: scales the bend and the glare. |
| `data0.w` | Seed for the ripple noise. |
# blast_spark
Additive streak or dot for sparks and embers; velocity-aligned via `zero_billboard_streak`. The
instance's +Y runs along its velocity and its Y scale is stretched by speed, so a fast spark draws
as a streak and a slow ember as a dot. The fragment shapes a capsule with a bright core and a soft
tail, and flickers it with `0.72 + 0.28 * sin(time * rate + seed * 91)`. It returns the HDR colour
times coverage with the coverage as alpha.
The `wall_blast` graph (`blast/wall_blast.procGraph`) uses it through `spark_mat` (additive) for the
embers and sparks thrown from the blast. The graph turns each particle onto its velocity, stretches
its Y scale from 1 to 9 times its size over speeds 0 to 32, colours it from an HDR ramp that cools
from yellow-white to dull red over life, and writes `data1` as (life, a random seed, a flicker rate
of 45, 0).
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `unused` | float | 0 | The one entry properties.yaml lists (range 0 to 1). Every value the shader reads comes from the lanes below. |
| `vertexBoundsDilation` | float | 0.1 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour. |
| `data0.a` | Intensity, multiplied into the colour. |
| `data1.x` | Life 0 to 1, as the header states and the graph writes it. |
| `data1.y` | Seed: the flicker's phase. |
| `data1.z` | Flicker rate, multiplied by time inside the flicker sine. |
| `data1.w` | 0. |
# blast_stone
Procedural weathered stone (surface/PBR) for the wall and everything it breaks into. Per-instance
lanes: tint, seed, heat (emissive glow), soot. The pattern is computed from the mesh UV scaled by
5.5 and offset by the seed, so every block reads differently. A height field of `fx_noise` fBm plus
inverted worley cells gives pitting, and its finite-difference slope bends the normal. The base is
warm sandstone, mixed between `(0.27, 0.21, 0.16)` and `(0.42, 0.34, 0.26)` by the tint lane, with
broad fBm shading, fine speckle, darker crack lines, a grimy course toward the low end of the UV,
and near-black soot by the soot lane. Roughness is 0.93 (0.85 along cracks). A hot block glows
orange `(2.6, 0.75, 0.12)` through a vein pattern, scaled by the square of heat.
The `wall_blast` graph (`blast/wall_blast.procGraph`) uses it through `stone_mat` on unit boxes
scaled to blocks, with shadows on: the wall blocks that stand, the blocks the blast takes (held in
place until the detonation), and the flying rubble. Standing blocks carry heat 0 and soot 0; rubble
carries a heat ramp from 0.6 at birth to 0 and a soot ramp from 0.1 to 0.85 over its life.
The shader includes `fx_noise`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `unused` | float | 0 | The one entry properties.yaml lists (range 0 to 1). Every value the shader reads comes from the lanes below. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Per-block tint 0 to 1, between the darker and lighter sandstone. |
| `data0.y` | Per-block seed: offsets the noise pattern. |
| `data0.z` | Heat 0 to 1: orange emissive glow, scaled by heat squared. |
| `data0.w` | Char and soot 0 to 1: blends the albedo toward near-black. |
# wall_blast
A charge blowing a breach through a stone block wall, on a 9.6 s loop. The wall stands whole, a
fireball bursts from its face a second in, the blocks around the breach are thrown out as
tumbling, glowing debris with a spray of sparks, a shock ring runs out across the ground, and dust
and smoke roll out and settle. Over the first fifth of a second of the next loop the breach knits
back in.
Everything is a function of the graph clock modulo the loop, so it repeats with no solver ever
restarting. The wall runs along x at z = 8 in the generator's own space, and its blocks and
debris are drawn with the `blast_stone` shader, the fire, smoke and dust with `blast_puff`, the
sparks with `blast_spark` and the shock ring with `blast_shock`.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wall_blast", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
| Output | What it draws |
|---|---|
| `stone` | The wall's blocks, the breach's blocks while they stand, and the debris |
| `fire` | The fireball at the wall face |
| `sparks` | Sparks thrown out with the debris |
| `shock` | The shock ring on the ground |
| `smoke` | Smoke rising from the breach |
| `dust` | Dust rolling out across the ground |
| `light` | The light of the blast |
The timeline has no end and plays at 60 fps.
# impact
A round striking a hard surface: a flash, a spray of sparks thrown back off it
that fall and cool, a burst of dust and grit that billows and settles, and a
flicker of light on the surface.
Fire it where the round lands, with the effect's -Z turned along the normal of
the surface it struck, and let it go when it ends:
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local function hitEffect(position, normal)
local e = entity.spawn("impact", { position = position, temporary = true })
e:lookAt({ position[1] + normal[1], position[2] + normal[2], position[3] + normal[3] })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "hold" })
e.component.get("Generator").events.onFinished:connect(function()
e:despawn()
end)
return e
end
```
| Input | Default | What it does |
|---|---|---|
| `size` | 1 | How big the strike is: 1 a rifle round on concrete, 3 a heavy round, 0.4 a pistol. |
| `light` | 1 | Strength of the light it throws, as a multiple of the default. |
Set them with the Generator's `params`, e.g. `params = { size = 3 }`. It plays
66 frames at 60 fps (1.1 s). Every play with the same params plays the frames
the first one cooked.
# muzzleFlash
The bloom at a barrel the instant a weapon fires: a ragged star of light and a
tongue of flame down the bore, gone within three frames; a spray of burning
powder that streaks out, falls and cools; a few puffs of smoke that swell, rise
and thin; and a light that throws the flash on whatever stands near.
The bore runs along the effect's -Z. Stand it on a child of the weapon at the
muzzle, so it follows the weapon, and play it from the start on each shot:
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local function mountFlash(weapon, muzzleOffset)
local muzzle = entity.spawn("muzzle", { parent = weapon.id, position = muzzleOffset })
muzzle.component.add("Generator", { graph = graph, playOn = "manual", ending = "hold" })
local flash = muzzle.component.get("Generator")
return function()
flash:seek(0)
flash:play()
end
end
```
| Input | Default | What it does |
|---|---|---|
| `size` | 0.28 | Width of the flash in metres: a pistol at 0.12, a rifle at 0.28, a tank gun at 1.2. Every layer scales with it. |
| `light` | 1 | Strength of the light it throws, as a multiple of the default. |
It plays 54 frames at 60 fps (0.9 s). Played again, it plays the frames its
first play cooked, so a weapon firing on full auto cooks it once.
# fxFlash
The bloom of a discharge, drawn procedurally on a card for additive blending. With `shape` 0 it is a
ragged star facing the camera (`zero_billboard`): five to seven spikes of hashed length and turn
around a white-hot core, each spike brightest at its root and fading to its tip. With `shape` above
0.5 it is a tongue of flame on a card turned onto the element's Y axis (`zero_billboard_streak`),
running from the middle of the card (the muzzle) to its tip along +Y, wavering at its edges and
cooling toward the tip, on the half of the card between its middle and its +Y end. The petals are
re-seeded every 1/30 s of world time, so a flash held for more than one frame flickers. The colour
is the element's `data0` colour over the shape, plus a white `(1.0, 0.95, 0.85)` core scaled by the
colour's brightest channel.
The combat graphs beside it in `combat/` draw with it as `fxFlash`, additive. The
`impact` graph draws two stars (`shape` 0) at the point of impact for three frames. The
`muzzleFlash` graph draws three stars turned apart by their seeds (`shape` 0) and one tongue of
flame down the bore (`shape` 1). Both colour the flash from an HDR ramp that falls to black over its
short life. The `bench_mixed` scene places both graphs and the `main` scene places `muzzleFlash`.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour. |
| `shape` | float | 0.0 | 0 draws the camera-facing star; above 0.5 draws the tongue along the element's Y axis. |
| `vertexBoundsDilation` | float | 5 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | The element's colour in linear HDR. |
| `data1.x` | Seed that turns and re-sizes the petals; the shader adds a step every 1/30 s. |
# fxSmoke
A puff of smoke on a card facing the camera (`zero_billboard`), for blending over the frame. It
draws a soft, lumpy disc of three-octave value noise whose edge is pushed in and out by the noise,
and as the puff ages the noise thins it from its wisps inward. It is lit by the ambient light plus
the sun, wrapped around a sphere normal under the card so the side toward the sun is brighter. The
colour is the element's colour times that light, and the alpha is the density times the element's
opacity times `gain`.
The combat graphs beside it in `combat/` draw with it as `fxSmoke`, transparent. The
`impact` graph uses it for the burst of dust and grit off the struck surface (grit colour `(0.58,
0.53, 0.46)`, opacity falling from 0.55 to 0 over life), and the `muzzleFlash` graph for the puffs
of smoke leaving the bore (grey `(0.56, 0.54, 0.52)`, opacity falling from 0.42 to 0). Both swell
the card as it ages. The `bench_mixed` scene places both graphs and the `main` scene places
`muzzleFlash`.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output alpha. |
| `vertexBoundsDilation` | float | 5 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | Smoke colour, multiplied by the lighting. |
| `data1.x` | Seed: picks the puff's lumps. |
| `data1.y` | Life, 0 at birth to 1 at death: thins the puff and drifts its noise. |
| `data1.z` | Opacity. |
# fxSpark
A spark: a card turned onto its element's motion and stretched along it (`zero_billboard_streak`),
drawn as a white-hot core fading to its colour at the rim and tapering to a point at both ends. The
width across the streak follows `1 - along^2`, so the card is widest at its middle; pixels outside
that width are discarded. The body carries the element's colour, and a white core is added on the
axis, scaled by the colour's brightest channel (capped at 4) times 0.35. The result times `gain` is
added to the frame.
The combat graphs beside it in `combat/` draw with it as `fxSpark`, additive. The
`impact` graph uses it for the sparks thrown back off the struck surface, the `muzzleFlash` graph
for the burning powder streaking out of the bore, and the `tracer` graph for the round's white-hot
streak and the embers it sheds. Each graph turns the particle onto its velocity, lengthens its Y
scale, and colours it from an HDR ramp that cools over its life. The `bench_mixed` scene places all
three graphs and the `main` scene places `muzzleFlash`.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour. |
| `vertexBoundsDilation` | float | 0.3 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | The element's colour in linear HDR. |
# tracer
The glowing streak of a round in flight: a white-hot streak leaving the muzzle
along -Z and burning out `distance` metres on, shedding embers that fall and
cool behind it.
Fire it at the muzzle with its -Z turned toward what the round hits, and
`distance` the range to it:
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local function tracer(from, to)
local dx, dy, dz = to[1] - from[1], to[2] - from[2], to[3] - from[3]
local e = entity.spawn("tracer", { position = from, temporary = true })
e:lookAt(to)
e.component.add("Generator", {
graph = graph, playOn = "load", ending = "hold",
params = { distance = math.sqrt(dx * dx + dy * dy + dz * dz) },
})
e.component.get("Generator").events.onFinished:connect(function()
e:despawn()
end)
return e
end
```
| Input | Default | What it does |
|---|---|---|
| `distance` | 40 | How far the round flies before it burns out, in metres: the range to what it hits. |
| `speed` | 240 | How fast it flies, in metres per second. |
| `width` | 0.04 | Width of the streak, in metres. |
The streak runs back from the round for as far as it flies in a sixtieth of a
second, and never behind the muzzle. It plays 45 frames at 60 fps (0.75 s).
Each `distance` is its own set of frames: plays at one range share what the
first one cooked.
# dragon_breath
A dragon's fire breath sweeping a courtyard: a jet of flame from the mouth, rolling dark smoke born
where the flames die, and embers that drift and settle. The sweep is a function of time alone
(yaw = 0.72 sin(2 pi t / 14), from world +Z toward +X), so the effect loops forever, and the
Procgen Effects Showcase world's courtyard shader `flagstone_scorch` evaluates the same sweep to scorch the stones where the jet
has just passed. The mouth stands at (0, 2.4, -9) in the generator's own space.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("dragon_breath", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
| Output | What it draws |
|---|---|
| `flame` | The flame jet, additive, `dragon_flame` shader |
| `smoke` | The smoke the flames leave, `dragon_smoke` shader |
| `embers` | Embers drifting down, additive, `dragon_ember` shader |
In the Procgen Effects Showcase world's `fx_breath` scene, the `DragonBreathDirector` component on the same entity keeps
the ground's scorch and a set of flickering lights in step with this graph's clock. The timeline
has no end and plays at 60 fps.
# dragon_ember
Glowing ember mote (additive billboard) for the `dragon_breath` graph. `data0 = (life, seed, size,
0)`. Flickers, cools from yellow-orange to dull red and fades as it settles. Each mote is a
camera-facing card (`zero_billboard`) drawn as a soft disc. The flicker is `0.55 + 0.45 * sin(time *
rate + seed * 40)` with a per-mote rate between 14 and 34 from the seed. The colour runs from `(6.0,
2.4, 0.5)` to `(1.2, 0.12, 0.01)` as heat `1 - 1.3 * life` falls; the mote fades in over the first 5
percent of its life and out between 0.6 and 1. It returns the colour times its coverage, for
additive blending.
The `dragon_breath` graph (`dragon/dragon_breath.procGraph`) draws its `embers` output with it,
additive: embers shot out along the jet and kicked up where the flame hits the courtyard floor, on
cards that shrink from 1.0 to 0.6 of their size over life. In the Procgen Effects Showcase world, the `fx_breath` scene plays that graph.
All of its inputs arrive per particle on `data0`; properties.yaml holds `properties: []`.
The shader includes `@builtin::shaderModules.billboard`.
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 to 1: drives the fades and the cooling. |
| `data0.y` | Seed: the flicker's rate and phase. |
| `data0.z` | Particle size, written by the graph. |
| `data0.w` | 0. |
## Culling
`vertexBoundsDilation` (float, default 0.1): how far the vertex hook moves a
vertex past the mesh, in world units. The culling bounds grow by it, so a card turned to face the
camera stays inside them where it reaches past the mesh it was drawn from.
# dragon_flame
Additive camera-facing flame puff for the `dragon_breath` graph. `data0 = (life, seed, size, 0)`.
Noise-carved blob that churns, erodes from the outside in and cools from yellow-white through orange
to dull red over its life. Tuned for thousands of overlapping puffs (each carries a slice of the
light).
Each puff is a `zero_billboard` card turned by its seed and spun over its life in a direction the
seed picks. A domain-warped four-octave value noise, scrolling with time, carves the blob's edge,
and the threshold rises with life (`life * 0.85`) so the puff erodes from the rim inward. A faster
inner churn layer mixes hotter and cooler tongues through the puff. Temperature combines youth, the
dense core and both noise layers, then maps through a five-stop ramp from `(0.22, 0.012, 0)` to
`(3.9, 2.3, 0.75)`. The puff fades in over the first 20 to 30 percent of its life (born thin at the
nozzle) and out between 0.55 and 1, and its coverage is scaled by 0.27 (times a churn term) so each
of the many stacked puffs carries a slice of the light.
The `dragon_breath` graph (`dragon/dragon_breath.procGraph`) draws its `flame` output with it,
additive: a fast bright jet and a slower, wider billow, on cards that grow from 0.55 to 2.6 of their
size over life. In the Procgen Effects Showcase world, the `fx_breath` scene plays that graph. All of its inputs arrive per particle on
`data0`; properties.yaml holds `properties: []`.
The shader includes `@builtin::shaderModules.billboard`.
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 to 1: drives the spin, erosion, fades and cooling. |
| `data0.y` | Seed: rotation, spin direction and rate, and the noise offset. |
| `data0.z` | Particle size, written by the graph. |
| `data0.w` | 0. |
## Culling
`vertexBoundsDilation` (float, default 2.5): how far the vertex hook moves a
vertex past the mesh, in world units. The culling bounds grow by it, so a card turned to face the
camera stays inside them where it reaches past the mesh it was drawn from.
# dragon_smoke
Alpha-blended rolling black smoke puff for the `dragon_breath` graph. `data0 = (life, seed, size,
0)`. Sooty and near-black, underlit orange by the fire only while young.
Each puff is a `zero_billboard` card turned by its seed and slowly rotated over its life. A
domain-warped four-octave value noise drifting with time breaks up a soft disc. The puff fades in
over the first 18 percent of its life and out between 0.45 and 1, at a peak opacity of 0.62. Its
colour is a near-black `(0.010, 0.009, 0.009)` lifted by noise into dark grey as it ages, plus an
orange underglow `(1.6, 0.4, 0.08)`, weighted across the card by `0.5 - 0.5 * p.y` and toward its
thin edge, that decays with `exp(-9 * life)` squared, so only young smoke catches the fire.
The `dragon_breath` graph (`dragon/dragon_breath.procGraph`) draws its `smoke` output with it,
transparent: smoke born where flame particles die, rolled by curl noise and drifting on a constant
breeze, on cards that grow from 0.8 to 2.8 of their size over life. In the Procgen Effects Showcase world, the `fx_breath` scene plays that
graph. All of its inputs arrive per particle on `data0`; properties.yaml holds `properties: []`.
The shader includes `@builtin::shaderModules.billboard`.
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 to 1: rotation, fades, greying and the underglow's decay. |
| `data0.y` | Seed: rotation and noise offset. |
| `data0.z` | Particle size, written by the graph. |
| `data0.w` | 0. |
## Culling
`vertexBoundsDilation` (float, default 2.5): how far the vertex hook moves a
vertex past the mesh, in world units. The culling bounds grow by it, so a card turned to face the
camera stays inside them where it reaches past the mesh it was drawn from.
# launch
A heavy-lift rocket's liftoff on one 36 s loop that drives the vehicle, the exhaust, the ground
clouds, the sparks, the heat haze and the light together:
| Graph time (mod 36) | What happens |
|---|---|
| 0 to 3 s | Quiet: the vehicle stands on the mount |
| 3 to 8 s | Countdown: deluge steam bleeds from the trench mouths |
| 10 s | Ignition: a flash, and the engines spool up to full thrust |
| 12 s | The hold-downs release and the vehicle climbs on a pillar of fire |
| 31.5 s | Engine cutoff, with the vehicle 1.3 km up, then the loop starts over |
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("launch", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
Its outputs are the vehicle in three materials (`hullWhite` its white body, `hullFoam` its orange
foam-covered core, `hullDark` its dark bands and engines), the exhaust flame (`flameCards`), smoke
and steam clouds (`smokeCards`, `steamCards`), spray off the deluge (`splashCards`), sparks
(`sparkStreaks`), heat haze (`hazeCards`) and two lights (`lightFloor`, `lightPlume`). The timeline has no end and plays at 60 fps. In the Procgen Effects Showcase world, the `fx_launch` scene frames
it with the `LaunchView` component.
# rk_haze_mat
Heat-shimmer material for the rocket launch. The `launch` graph beside it (`launch.procGraph`)
resolves it by name and draws its `hazeCards` output with it: one card per particle of a
500-particle simulation fed from where the flame hits (36 per second at full thrust, living 1.2 to
2.4 s) and from the plume column (14 per second at full thrust, living 0.9 to 1.8 s). Each card
grows from 0.7 to 1.4 times its size over its life, and its lane-0 colour comes from a ramp whose
alpha rises to 0.9, falls to 0.5 and ends at 0; the shader reads that alpha as its strength. The
graph's comment states that a `.material` asset is used here because the draw order matters for the
haze. The `launch_fx` Generator entity runs the graph.
The description in mat.yaml states that it is drawn before every other transparent surface, so it
bends only the opaque scene and the flame and smoke drawn over it stay intact.
Shader: `rk_haze` (`rk_haze.shader`). Its header: a large, transparent, camera-facing card that
draws nothing of its own; it re-reads the scene colour behind it through a small, drifting noise
offset, so the air above the flame trench and around the plume wobbles. The card's alpha follows how
much the bent read differs from the straight one, so it is visible at edges in the opaque scene and
blends away over flat sky or ground.
Render state (the `render` block of mat.yaml): `queue: 2600`, `type: transparent` (alpha blend),
`cull: none`, `depth_write: false`.
## Properties
- `strength: 16.0`: scale of the noise offset, in pixels, applied to the scene-colour read (default
38.0).
- `edge_gain` keeps its default, 6.0: multiplier that turns the colour difference into alpha.
# rk_flame
Rocket exhaust flame: an additive, camera-facing card (`zero_billboard`) whose shape is eaten away
by animated fBm, so a stack of them reads as one turbulent, white-hot plume. Each card is a disc
rotated by its own random angle, and a four-octave value noise scrolls through it with time at a
per-card speed so the surface churns. The young flame is nearly a solid disc; as life rises the
noise erodes it more (erosion from 0.25 to 0.95), until the old flame shreds. A hot inner bloom
fades out over the card's life. The colour is the lane colour times the shape and bloom, its opacity
and `gain`, returned with alpha 1 for additive blending.
The `launch` graph (`launch/launch.procGraph`) draws two outputs with it, additive: `flameCards`
(`gain` 1.0), the exhaust from the six nozzles, each a tight blinding core wrapped in a wider
turbulent sheath, plus the ignition flash in the trench, and `splashCards` (`gain` 0.85), the fire
splashing sideways where the plume hits the ground. Both colour the cards from an HDR ramp that runs
from white-hot `(20, 18, 14)` to a dark red that fades to nothing, and grow the cards over life. In the Procgen Effects Showcase world, the
`fx_launch` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (minimum 0). |
| `vertexBoundsDilation` | float | 14 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour, already walked along the particle's life by the graph. |
| `data0.a` | Opacity, multiplied into the colour. |
| `data1.x` | Life, 0 to 1: sets the erosion and fades the inner bloom. |
| `data1.y` | Per-particle random 0 to 1: the card's rotation, noise offset and churn speed. |
| `data1.z` | Named a stretch or turbulence hint in the shader header; the `launch` graph writes 0 here for its flame cards. |
# rk_haze
Heat shimmer. A large, transparent, camera-facing card (`zero_billboard`) that draws the scene
behind it again through a small, drifting offset, so the air above the flame trench and around the
plume visibly wobbles. Two value-noise fields, sampled from the card's world position and rising
with time, give the offset; it is scaled by `strength`, by the lane's strength and by a soft round
mask, and read through `zero_scene_color_refract`. The scene colour holds the opaque scene only, so
the card is opaque only where bending that scene changes the picture: the difference between the
bent and the straight read, times `edge_gain`, sets the alpha. Over flat sky or ground the card is
invisible.
The `rk_haze_mat` material uses it with `strength` 16, in the transparent queue at 2600, with
culling off and depth writes off; its description states it is drawn before every other transparent
surface so it bends only the opaque scene. The `launch` graph (`launch/launch.procGraph`) draws its
`hazeCards` output with that material: shimmer cards born where the exhaust hits the ground and
trailing the climbing vehicle, their lane strength rising to 0.9 early in life and then fading out.
In the Procgen Effects Showcase world, the `fx_launch` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard` and `zero::scene_color`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `strength` | float | 38.0 | Scale of the refraction offset (minimum 0). |
| `edge_gain` | float | 6.0 | Multiplies the colour change the bend causes to give the card's alpha (minimum 0). |
| `vertexBoundsDilation` | float | 22 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.a` | Strength 0 to 1, walked along the particle's life. |
| `data1.y` | Per-particle random value: offsets the noise. |
# rk_smoke
Billowing smoke or steam puff: an alpha-blended, camera-facing card (`zero_billboard`). A soft body
is broken up by two layers of four-octave value noise, so overlapping puffs merge into one rolling
mass. Each card is rotated by its random value and turns slowly over life. The density's gradient
gives a lumpy normal, lit by the sun direction projected into the card's plane and un-rotated so the
light stays fixed on screen. The lighting uses fixed values: a cool sky fill `(0.20, 0.22, 0.28)`
plus the sun's normalised colour, scaled by `sun_gain` and `brightness`, with thick middles shadowed
by their own mass. A warm exhaust underglow `(3.0, 1.05, 0.28)`, scaled by the lane's heat and
`glow_gain`, is strongest on the underside and where the puff is thin. The alpha is the density
times the lane opacity and `opacity`, faded against the scene behind it over `depth_fade` and faded
in over `ground_fade` above world height 0.
The `launch` graph (`launch/launch.procGraph`) draws two outputs with it, transparent: `smokeCards`,
the smoke born where the exhaust hits the ground, pushed out of the trench mouths and trailing the
climbing vehicle (`sun_gain` 0.8, `depth_fade` 2.5, `ground_fade` 0.6, heat 1.0 at birth falling to
0 by half its life), and `steamCards`, the white deluge steam during the countdown (`sun_gain` 0.8,
`glow_gain` 0.8, `depth_fade` 2.0, `ground_fade` 0.5, heat 0.7 falling to 0 by 0.4 of its life). In the Procgen Effects Showcase world, the
`fx_launch` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard` and `zero::scene_depth`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `opacity` | float | 1.0 | Multiplies the alpha (minimum 0). |
| `sun_gain` | float | 1.0 | Multiplies the sun term (minimum 0). |
| `glow_gain` | float | 1.0 | Multiplies the exhaust underglow (minimum 0). |
| `depth_fade` | float | 1.5 | Distance over which the puff fades into the scene behind it (minimum 0). |
| `ground_fade` | float | 0.35 | World height over which the puff fades in above height 0 (minimum 0.01). |
| `brightness` | float | 1.0 | Multiplies the lit colour (minimum 0). |
| `vertexBoundsDilation` | float | 15 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | Tint. |
| `data0.a` | Opacity; also scales the underglow. |
| `data1.x` | Life, 0 to 1: the card's slow turn. |
| `data1.y` | Random 0 to 1: rotation and noise offset. |
| `data1.z` | Heat 0 to 1: how much exhaust light the puff catches. |
# rk_spark
Spark and burning-debris streak. The card's Y axis lies along the particle's motion
(`zero_billboard_streak`) and the graph stretches it by speed, so the fragment draws a thin line
along the card, brightest at the head (+Y) and fading toward the tail, plus a bright spot at the
head. The colour is the lane colour times that shape, the lane opacity and `gain`, returned with
alpha 1 for additive blending.
The `launch` graph (`launch/launch.procGraph`) draws its `sparkStreaks` output with it (additive,
`gain` 1.4): sparks and burning chunks thrown from where the exhaust hits the ground, bouncing off
the scene, stretched from 1.5 to 9 times their size over speeds 0 to 30, and coloured from an HDR
ramp that cools from `(12, 9, 4.5)` to dark red over life. In the Procgen Effects Showcase world, the `fx_launch` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (minimum 0). |
| `vertexBoundsDilation` | float | 0.3 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour. |
| `data0.a` | Opacity, multiplied into the colour. |
# arcane_rift
A rift tearing open in the ground: a glowing mouth along a crack in the earth, cracks branching
out from it, rubble heaving up, sparks and streaks climbing out of it, motes drifting above and a
plume rising from it. It is centred on the generator's origin with its long axis along x and the
ground at y = 0.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("arcane_rift", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
| Input | Default | What it does |
|---|---|---|
| `intensity` | 1 | Brightness of everything that glows: the mouth, cracks, sparks, streaks, motes and plume (0 to 4). |
| Output | What it draws |
|---|---|
| `mouth` | The glowing opening, `rift_core` shader |
| `cracks` | The cracks branching out across the ground, `rift_crack` shader |
| `rubble` | Rubble around it, `rift_rock` shader, moved by the `rift_rubble` compute shader |
| `sparks` | Sparks thrown from the mouth, `rift_streak` shader |
| `streaks` | Streaks climbing out of it, `rift_streak` shader |
| `motes` | Motes drifting above it, `rift_mote_cluster` shader, moved by the `rift_motes` compute shader |
| `plume` | The plume rising from it, `rift_plume` shader |
The timeline has no end and plays at 60 fps.
·computeshader · born here
❒asset # rift_motes
One step of the arcane mote swarm, run as a `sim.shader` solver with a neighbour grid. Motes swirl
around the rift through a rolling vortex ring stirred by turbulence, and keep apart through
grid-based separation, so the swarm streams together with its motes kept apart. It runs one
invocation per mote in workgroups of 64.
For each mote the shader builds a desired velocity from four parts:
- a swirl around the vertical axis through the origin, `swirl * (0.45 + 1.4 * exp(-0.35 * rho))`,
fastest near the axis;
- a poloidal roll around the ring core at radius `coreR` and height `coreH`: up on the inside, out
over the top, down at the outer edge, at a rate set by `rollRate`;
- a smooth turbulence from summed sines of position and time;
- a pull back toward the ring core for any mote farther than `shell` from it, and a lift for any
mote below height 0.2.
It then visits every mote in the 27 grid cells around it and adds a push away from each neighbour
closer than `radius`, weighted by `(1 - distance / radius)^2`. The velocity eases toward the desired
one at a rate of `1 - exp(-1.6 * dt)`, takes the push times `separate * dt`, is capped at 4.5 units
per second, and moves the mote by `velocity * dt`.
## Bindings
| Binding | Access | What it holds |
|---|---|---|
| `P` | read_write | Positions, three floats per mote; the shader writes the new position. |
| `prev_P` | read | Positions at the start of the step, read for the mote itself and for its neighbours. |
| `v` | read_write | Velocities, three floats per mote; read and written back. |
| `gcfg` | read | Grid configuration, used by `zero_grid_cell` and `zero_grid_range`. |
| `grange` | read | Per-cell ranges into `gorder`, used by `zero_grid_range`. |
| `gorder` | read | Mote indices sorted by grid cell; the separation loop reads neighbours from it. |
The grid helpers come from `#include "@builtin::systems.procgen.grid"`.
## Parameters
| Parameter | Type | Default | Meaning |
|---|---|---|---|
| `count` | int | 0 | Number of motes. |
| `dt` | float | 0.0166667 | Step length in seconds. |
| `time` | float | 0.0 | Solver time in seconds; drives the turbulence. |
| `radius` | float | 0.35 | Separation radius. |
| `separate` | float | 7.0 | Strength of the separation push. |
| `coreR` | float | 2.8 | Radius of the vortex ring's core from the vertical axis. |
| `coreH` | float | 1.9 | Height of the vortex ring's core. |
| `shell` | float | 3.2 | Distance from the ring core beyond which motes are pulled back. |
| `swirl` | float | 1.7 | Speed of the swirl around the vertical axis. |
| `rollRate` | float | 0.45 | Rate of the poloidal roll. |
## Dispatch
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) dispatches it from its `swarm` node, a
`sim.shader` over 900 points seeded in a cylinder of radius 4.2 and height 3.4, with a `v` (Vec3)
attribute, `neighborRadius` 0.75 and uniforms `radius` 0.75, `separate` 5, `coreR` 2.5, `coreH` 1.7,
`shell` 1.9, `swirl` 1.5 and `rollRate` 0.5. Each of those 900 leaders is drawn as a cluster of
eight motes by `rift_mote_cluster`. In the Procgen Effects Showcase world, the `fx_rift` scene plays the graph.
·computeshader · born here
❒asset # rift_rubble
Analytic, per-chunk periodic motion for rubble orbiting the arcane rift, run as a `sim.shader`
solver. Every chunk's state is a function of the solver time alone, periodic per chunk, so the loop
is seamless: a chunk grows in while still buried, shudders, rips free, spirals up into orbit,
climbs, then crumbles to nothing glowing hot, and is buried again for its next cycle at a new spot
around the rift. It runs one invocation per chunk in workgroups of 64.
Each chunk hashes its index into its own values: a period of 0.8 to 1.2 times `period`, a phase
offset, a starting angle, the radius where it tears loose (1.5 to 4.1), its orbit radius (2.1 to
4.0), the height it climbs to (1.4 to 4.6), a size, an orbit speed and a tumble axis. Through each
cycle its phase passes four stages: emerging (0 to 0.08), ripping free (0.08 to 0.26), climbing
(0.22 to 0.9) and crumbling (0.84 to 0.985). The orbit is stretched 1.2 times along X, larger chunks
orbit and spin more slowly, and the chunk bobs gently once free.
## Bindings
| Binding | Access | What it holds |
|---|---|---|
| `P` | read_write | Positions, three floats per chunk; written. |
| `orient` | read_write | Orientation quaternion per chunk (`vec4f`); written as a rotation about the chunk's axis. |
| `scale` | read_write | Scale, three floats per chunk; written. It grows in over the first 5 percent of the cycle and falls to zero as the chunk crumbles. |
| `data0` | read_write | Per-chunk lane (`vec4f`); written as (heat, seed, cycle phase, 0). |
Heat is `0.25 + 1.2 * rip * (1 - climb) * emerge + 2.5 * crumble`: the seams flare as the chunk rips
free and burn brightest as it crumbles.
## Parameters
| Parameter | Type | Default | Meaning |
|---|---|---|---|
| `count` | int | 0 | Number of chunks. |
| `time` | float | 0.0 | Solver time in seconds. |
| `period` | float | 16.0 | Base length of one cycle in seconds. |
| `spin` | float | 0.55 | Base tumble rate. |
## Dispatch
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) dispatches it from its `orbiting` node, a
`sim.shader` over 72 points on a disc, with `orient` (Vec4), `scale` (Vec3) and `data0` (Vec4)
attributes and uniforms `period` 17 and `spin` 0.55. The graph draws each chunk as a
noise-displaced, subdivided octahedron with the `rift_rock` material, casting shadows; `rift_rock`
reads heat from `data0.x` and per-chunk variation from `data0.y`. In the Procgen Effects Showcase world, the `fx_rift` scene plays the
graph.
# rift_core
The mouth of an arcane rift on a flat ground plane: a jagged, breathing, lens-shaped tear with a
white-hot rim, a parallax churning abyss inside, and a halo of light on the ground. It uses a
premultiplied blend: the abyss covers the ground (alpha 1) and the halo only adds light (alpha 0).
The plane's UV is mapped to metres through `plane_size_x` and `plane_size_z`. The tear's outline is
a lens `half_length` long and `half_width` wide each side of its centre line, fullest in the middle
and pinched at the tips, with a jagged edge from `rift_noise` fBm that differs on each lip and
grinds slowly with time, and a slow breathing of its width. Inside, the abyss is three layers of
swirling, twisted noise seen at depths 0.25, 0.9 and 2.2 below the surface along the view ray, so it
reads as a shaft going down; the layers mix `deep_color` and `mid_color` and carry thin `hot_color`
filaments, with a darker core along the centre line. The rim is a flickering white-hot lip over a
`hot_color` sheath, and outside it a `mid_color` halo with licking tongues of light spreads over the
ground. The whole result fades out toward the plane's edges and is scaled by `gain`.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) draws its `mouth` output with it through
`coreMat` (premultiplied, culling off, depth writes off) on a 7 by 4 plane lifted 0.015 above the
ground, with `gain` wired to the graph's `intensity` input. In the Procgen Effects Showcase world, the `fx_rift` scene plays the graph.
The shader includes `rift_noise` with `#include "rift_noise"`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (range 0 to 20). |
| `half_length` | float | 2.1 | Half-length of the tear along X, in metres of the plane. |
| `half_width` | float | 0.62 | Half-width of the tear at its fullest. |
| `plane_size_x` | float | 7.0 | Size of the plane along X, mapping UV to metres. |
| `plane_size_z` | float | 4.0 | Size of the plane along Z, mapping UV to metres. |
| `deep_color` | color | [0.02, 0.0, 0.08, 1] | Darkest colour of the abyss. |
| `mid_color` | color | [0.5, 0.09, 1.5, 1] | Swirl colour of the abyss, tint of the rim and colour of the ground halo. |
| `hot_color` | color | [1.2, 1.6, 4.0, 1] | Colour of the abyss filaments and the rim's sheath. |
# rift_crack
An additive glowing ground crack for ribbons from `geometry.ribbon`: a thin white-hot seam inside a
coloured glow, with energy surges running outward from a centre (in world space) and fading with
distance (`reach`). On the ribbon `uv.x` runs along the crack and `uv.y` across it. The seam is a
tight Gaussian across the ribbon and the glow a wider one in `glow_color`. Two trains of surges
travel outward from `center` at different speeds, a slow noise flicker plays over them, and the
brightness falls off as `exp(-distance / reach)`. Fragments dimmer than 0.002 are discarded.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) draws its `cracks` output with it through
`crackMat` (additive, culling off, depth writes off). The cracks are ribbons 0.5 wide laid along
eleven trunk lines leaving the tear's rim, wandering on curl noise and forked by `curve.branch` into
three generations that narrow with each fork. The graph wires `gain` to its `intensity` input and
leaves the other properties at their defaults; the default `center` (-147.5, 0, 148) is the world
position of the `ArcaneRift` entity in the Procgen Effects Showcase world's `fx_rift` scene.
The shader includes `rift_noise` with `#include "rift_noise"`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output (range 0 to 20). |
| `center` | float3 | [-147.5, 0, 148] | World position the surges run out from and the falloff is measured from (only x and z are read). |
| `reach` | float | 3.5 | Distance scale of the falloff, `exp(-distance / reach)`. |
| `glow_color` | color | [1.0, 0.25, 3.2, 1] | Colour of the glow around the seam. |
# rift_mote_cluster
Draws a cluster of glowing motes per element. The mesh carries degenerate quads (all four corners at
one point, told apart by their UV) on a unit sphere; the vertex stage orbits each around its own
axis and expands it to a camera-facing sprite. The element's scale sets how far the cluster spreads,
so a simulated swarm of a thousand leaders draws as thousands of motes for the cost of a thousand
renderables. Lane 0 is the HDR colour and opacity; `size` is the sprite size in metres.
In `vertex_clip` each sub-mote takes a stable id from where it sits in the cluster mesh and hashes
it into three values. It circles a per-mote axis at 0.35 to 1.25 radians per second, at a radius
that breathes between 0.2 and 1.0 of its rest position, is placed through the element's origin, axes
and scale, and is expanded into a sprite `size` times 0.55 to 1.45 across. The three hashes pass to
the fragment as the vertex colour. The fragment draws a core and halo that twinkle at a per-mote
rate of 1.5 to 7.5 radians per second, leans each sub-mote's hue between a cool `(0.75, 0.85, 1.25)`
and a warm `(1.25, 0.8, 1.0)` tint of the cluster's colour, and whitens the core. It returns the
colour times its coverage times `gain`, with the coverage as alpha.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) draws its `motes` output with it through
`moteMat` (additive, culling off, depth writes off, `size` 0.055, `gain` wired to the graph's
`intensity` input). The 900 leaders come from the `rift_motes` solver; each draws a cluster mesh of
eight zero-size quads placed on a sphere shell, at a random spread of 0.6 to 1.0, coloured from a
blue-violet-pink ramp by a random hue. In the Procgen Effects Showcase world, the `fx_rift` scene plays the graph.
The shader includes `rift_noise` with `#include "rift_noise"`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (range 0 to 50). |
| `size` | float | 0.06 | Sprite size of each sub-mote in metres, before its 0.55 to 1.45 variation (range 0 to 1). |
| `vertexBoundsDilation` | float | 2.1 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour of the cluster. |
| `data0.a` | Opacity. |
# rift_noise
Precision-safe integer-hash value noise and fbm (2D, 3D, and y-periodic variants for seamless time
scrolling) shared by the arcane rift shaders. The hashes are integer PCG hashes of the lattice cell,
so they stay crisp however large the coordinates grow, and every 2D noise can wrap along y with a
period in lattice cells, so a field scrolled by time loops seamlessly. The interpolation is the
smoothstep curve `f * f * (3 - 2 * f)`.
A shader includes it with:
```wgsl
#include "rift_noise"
```
The asset folder holds the same source as `module.wgsl` and `init.wgsl`.
## Functions
| Function | Arguments | Returns |
|---|---|---|
| `rn_pcg(v)` | `v: u32` | A PCG-hashed `u32`. |
| `rn_hash2(c)` | `c: vec2i`, a lattice cell | A hash of the cell as an `f32` between 0 and 1. |
| `rn_hash3(c)` | `c: vec3i`, a lattice cell | A hash of the cell as an `f32` between 0 and 1. |
| `rn_noise2p(p, period)` | `p: vec2f`, `period: i32` | 2D value noise between 0 and 1. With `period` above 0, the lattice wraps along y every `period` cells. |
| `rn_noise2(p)` | `p: vec2f` | `rn_noise2p(p, 0)`: 2D value noise with `period` 0. |
| `rn_fbm2(p)` | `p: vec2f` | Five octaves of `rn_noise2` with amplitudes 0.5, 0.25, 0.125 and so on; each octave rotates and scales the point by the matrix (1.6, 1.2, -1.2, 1.6) and offsets it. |
| `rn_fbm2p(p, period)` | `p: vec2f`, `period: i32` | Four octaves of `rn_noise2p`, the frequency and the period doubling each octave so every octave wraps along y with the base period. |
| `rn_noise3(p)` | `p: vec3f` | 3D value noise between 0 and 1. |
| `rn_wrap(t, period)` | `t: f32`, `period: f32` | `t` wrapped into [0, period), to keep a time-scrolled coordinate small. |
## Used by
- `rift_core`: `rn_fbm2` and `rn_noise2` for the tear's jagged edge, the abyss swirl, the rim
flicker and the halo tongues.
- `rift_crack`: `rn_wrap` for the surge trains and `rn_noise2` for the flicker.
- `rift_mote_cluster`: `rn_hash3` for each sub-mote's id and `rn_wrap` for its orbit and twinkle.
- `rift_plume`: `rn_wrap` and `rn_fbm2p` with a period of 16 for the climbing wisps.
# rift_plume
A rising column of arcane light for the rift: an upright card turned about the world Y axis toward
the camera (`zero_billboard_axis`), filled with climbing, twisting wisps, brightest at the base and
fading into the dark. Lane 0 is the HDR colour and opacity, lane 1.x a per-card phase.
Across the card the column is a Gaussian body that narrows from full width at the base to 0.3 of it
at the top and sways on a sine that climbs with time. Two layers of `rn_fbm2p` noise, periodic along
y with a period of 16 and scrolled with `rn_wrap`, stream upward through it, so the loop is
seamless. The column fades in over the bottom 8 percent, falls off as `(1 - y)^2.2` toward the top,
and carries an extra glow right at the base. It returns the colour times its coverage times `gain`,
with the coverage as alpha.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) draws its `plume` output with it through
`plumeMat` (additive, culling off, depth writes off, `gain` wired to the graph's `intensity` input):
four cards spaced along X from -0.9 to 0.9 at height 3.6, each 2.2 to 3.4 wide and 7.5 tall,
coloured (1.1, 0.4, 3.0) at opacity 0.5, with a random phase each. In the Procgen Effects Showcase world, the `fx_rift` scene plays the
graph.
The shader includes `@builtin::shaderModules.billboard` and `rift_noise`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (range 0 to 20). |
| `vertexBoundsDilation` | float | 1.7 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour. |
| `data0.a` | Opacity. |
| `data1.x` | Per-card phase; the shader multiplies it by 10 and offsets the sway and the wisps with it. |
# rift_rock
Torn basalt rubble with glowing arcane seams: lit PBR stone whose seam emission follows lane 0.x
(heat), while lane 0.y varies the pattern per chunk. The fragment builds a `PbrSurface` and shades
it with `zero_pbr_shade` from `@builtin::shaderModules.pbr_shading`.
The pattern is 3D value noise over the mesh's own UV (scaled 6 by 3) plus the chunk's variation, so
it stays fixed on the chunk as it tumbles. One noise layer, folded about its middle and raised to
the 30th power, draws thin seams; a second varies the stone's brightness and roughness (0.73 to
0.88). The albedo is `base_color` darkened along the seams. The emission is `glow_color` times the
seam, the heat and a flicker at 9 radians per second, plus a faint constant seam glow and a dim glow
on downward-facing surfaces.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) draws its `rubble` output with it through
`rockMat`, on noise-displaced, subdivided octahedra that cast shadows. The `rift_rubble` compute
shader moves the chunks and writes their `data0` as (heat, seed, cycle phase, 0). In the Procgen Effects Showcase world, the `fx_rift`
scene plays the graph.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `base_color` | color | [0.09, 0.085, 0.095, 1] | Stone colour before noise and seams. |
| `glow_color` | color | [1.0, 0.3, 3.0, 1] | Colour of the seam and underside glow. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Heat: brightness of the seam glow. |
| `data0.y` | Per-chunk variation: offsets the noise pattern and the flicker phase. |
# rift_streak
An additive energy streak for procgen particles: a velocity-aligned billboard card
(`zero_billboard_streak`, turned onto the element's `orient` and stretched by its `scale.y`) drawn
as a hot filament with a soft sheath. Lane 0 carries the HDR colour and opacity, lane 1.x a shimmer
phase. Along the card the streak tapers at both ends and is fuller toward the head; across it a
tight Gaussian core sits inside a wider sheath. The core bleeds toward white, and a shimmer at 37
radians per second plays over the whole streak. It returns the colour times its coverage times
`gain`, with the coverage as alpha.
The `arcane_rift` graph (`rift/arcane_rift.procGraph`) builds `streakMat` from it (additive, culling
off, depth writes off, `gain` wired to the graph's `intensity` input) and draws two outputs with it:
`sparks`, the bursts that arc out of the tear and bounce on the ground plus the smaller sparks they
throw, and `streaks`, the energy streaks spiralling up out of the rift. The graph turns each
particle onto its velocity, stretches it by speed, colours it from a violet and blue HDR ramp over
its life, and writes a random value into lane 1. In the Procgen Effects Showcase world, the `fx_rift` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `gain` | float | 1.0 | Multiplies the output colour (range 0 to 50). |
| `vertexBoundsDilation` | float | 0.05 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour. |
| `data0.a` | Opacity. |
| `data1.x` | Per-element shimmer phase, 0 to 1 of a turn. |
# fx_noise
Procedural noise for effect shaders, as WGSL other shaders include with `#include "fx_noise"`:
integer hashes, value and gradient noise in 2D and 3D, fBm and ridged fBm over them, curl noise, and
worley (cellular) noise that returns the distance to the nearest feature point and that cell's id.
The functions are pure math over their arguments, so a material's fragment shader and a compute
shader can both include it and read the same field. Every generator takes an explicit `seed`, so one
shader draws several uncorrelated fields from the same coordinates. The hashes mix integers, so a
value is the same bit pattern wherever it runs; the float entry points hash a float's bit pattern,
with `0.0` and `-0.0` folded together.
```wgsl
#include "fx_noise"
```
| Function | Returns |
|---|---|
| `fx_hash11(x)`, `fx_hash21(p)`, `fx_hash31(p)` | a stable value in [0, 1) for a number or point |
| `fx_value_noise2(p, seed)`, `fx_value_noise3(p, seed)` | smooth value noise in [0, 1) |
| `fx_gradient_noise2(p, seed)`, `fx_gradient_noise3(p, seed)` | gradient (Perlin-style) noise in [-1, 1], exactly 0 at every integer point |
| `fx_fbm2(p, octaves, lacunarity, gain, seed)`, `fx_fbm3(...)` | fractal sum of gradient noise in [-1, 1]: clouds, smoke, rock |
| `fx_ridged_fbm3(p, octaves, lacunarity, gain, seed)` | ridged fractal noise in [0, 1]: cracks, crags, veins |
| `fx_curl_noise3(p, seed)` | a divergence-free flow vector: swirling smoke and dust |
| `fx_worley2(p, seed)`, `fx_worley3(p, seed)` | `{ dist, cell, id }` of the nearest feature point: foam cells, stone, sparkle |
| `fx_worley_fbm2(p, octaves, lacunarity, gain, seed)` | fractal cellular noise in [0, 1] |
## Arguments
- `p` is an `f32`, `vec2f` or `vec3f` coordinate, matching the function's dimension.
- `seed` is a `u32`.
- `octaves` is a `u32`, clamped to 1 through `FX_FBM_MAX_OCTAVES` (12). Each octave multiplies the
frequency by `lacunarity` and the amplitude by `abs(gain)`, and the fBm functions divide by the
summed amplitudes, which keeps the stated range for any gain. Octave `i` uses seed `seed + i *
1013`.
## Building blocks
The module also exports the pieces the generators are built from:
| Function or item | What it is |
|---|---|
| `fx_hash_u32(x)`, `fx_hash_u2(x, y)`, `fx_hash_u3(x, y, z)` | Integer avalanche hashes of one, two or three `u32` words, returning a `u32`. |
| `fx_unorm(h)`, `fx_snorm(h)` | A hashed `u32` as an `f32` in [0, 1) or [-1, 1), from its top 24 bits. |
| `fx_bits(x)` | The `u32` bits of an `f32`, with negative zero folded onto positive zero. |
| `fx_hash22(p)`, `fx_hash33(p)` | Two or three decorrelated values in [0, 1) for a 2D or 3D point. |
| `fx_lattice_u2(cell, seed)`, `fx_lattice_u3(cell, seed)` | The hashed `u32` a seeded integer lattice corner (`vec2i` or `vec3i`) carries. |
| `fx_lattice21(cell, seed)`, `fx_lattice31(cell, seed)` | That corner's value in [0, 1); `fx_value_noise2` at an integer point equals `fx_lattice21` of that point. |
| `fx_fade(t)` | The quintic fade `t^3 * (t * (6t - 15) + 10)` used to interpolate across a cell. |
| `fx_gradient2(cell, seed)`, `fx_gradient3(cell, seed)` | The unit gradient a seeded lattice corner carries; the 3D one is spread evenly over the sphere. |
| `fx_curl_potential3(p, seed)` | The three-component potential `fx_curl_noise3` takes the curl of, by central differences over `FX_CURL_EPSILON` (0.05). |
| `fx_cell_point2(cell, seed)`, `fx_cell_point3(cell, seed)` | Where a cell's feature point sits inside the cell, in [0, 1) per axis. |
| `fx_cell_id2(cell, seed)`, `fx_cell_id3(cell, seed)` | The id in [0, 1) a cell is tinted by. |
| `FxWorley2`, `FxWorley3` | The worley result: `dist` (`f32`), `cell` (`vec2i` or `vec3i`) and `id` (`f32`). |
| `FX_TAU` | 2 pi. |
`fx_worley2` searches the 3 by 3 cells around the sample and `fx_worley3` the 3 by 3 by 3 cells.
`fx_worley_fbm2` sums `1 - dist`, clamped to [0, 1], over its octaves, so feature points read as
peaks.
## Used by
The waterfall (`wf_particle`, `wf_pool`, `wf_rock`, `wf_sheet`) and wall blast (`blast_puff`,
`blast_shock`, `blast_stone`) shaders in this package draw their foam, mist, rock, dust and debris
from it, and so does the Procgen Effects Showcase world's `blast_ground`.
# schedule
The storm's clock. Every strike is a pure function of the storm phase, so the storm graph (as
expressions over its own time) and the director (as Luau over the generator's clock) agree exactly,
and the whole storm loops every `PERIOD` seconds.
Two assets require it: the `storm` graph beside it (`storm.procGraph`, which builds its flash, bolt and burst
expressions from `STRIKES`, `flashExpr`, `envelopeExpr` and `windowOne`) and the `StormDirector`
component of the Procgen Effects Showcase world (lights, materials and thunder from `phase`, `envelope`, `afterglow` and `STRIKES`).
## Exports
### Values
- `M.PERIOD`: `13.6`, the loop length in seconds.
- `M.OFFSET`: `5.006`, the phase the storm is in at graph time 0. A strike has just landed there, so
a paused frame is a hero frame.
- `M.STRIKES`: the four strikes, each `{ t, kind, target, top, variant, weight }`. `t` is the phase
the strike lands at, `target` where it lands, `top` where the bolt starts, and `weight` its
brightness weight. `kind` is `"ground"` (throws sparks and smoke where it hits) or `"tower"` (hits
the finial). `target` and `top` are in the storm graph's own space, where the tower stands at the
origin; in the world they stand where the entity playing the graph does.
| `t` | `kind` | `target` | `top` | `variant` | `weight` |
|-----|--------|----------|-------|-----------|----------|
| 1.6 | ground | `{ 15, 0, -8 }` | `{ 3, 135, -2 }` | 1 | 1.0 |
| 5.0 | tower | `{ 0.05, 48.6, 0.1 }` | `{ -7, 135, 5 }` | 2 | 1.2 |
| 8.6 | ground | `{ -19, 0, 7 }` | `{ -30, 135, 12 }` | 3 | 1.0 |
| 11.0 | tower | `{ 0.05, 48.6, 0.1 }` | `{ 9, 135, 6 }` | 4 | 1.3 |
- `M.PULSES`: the return strokes of a strike, each `{ delay after the leader, strength, decay rate
per second }`: `{ 0.00, 1.00, 30 }`, `{ 0.11, 0.80, 26 }`, `{ 0.24, 0.62, 20 }`,
`{ 0.43, 0.36, 11 }`.
### Functions
- `M.phase(t)`: the storm phase at graph time `t`, `(t + OFFSET) % PERIOD`.
- `M.envelope(ph, s)`: how bright strike `s` is at phase `ph`: 0 before the strike, about 1 at the
return stroke. It sums every pulse whose delay has passed, each decaying exponentially at its rate.
- `M.afterglow(ph, s)`: the cloud glow that lingers after strike `s`, at phase `ph`:
`0.55 * exp(-d * 1.7) * min(d * 20, 1)` for `d` seconds after the strike, 0 before it.
- `M.envelopeExpr(s)`: the same envelope as `envelope`, written as an anim expression string over
`t`.
- `M.afterglowExpr(s)`: the same afterglow as `afterglow`, as an expression string over `t`.
- `M.flashExpr()`: the scene-wide flash as an expression string: the sum of every strike's
envelope times its weight, capped at 3.
- `M.windowExpr(only, lag, rise, fall)`: a burst of activity `lag` seconds after each strike, as a
bump expression in [0, 1]: it rises with `min(d * rise, 1)` and falls with `exp(-d * fall)`.
`only` limits it to strikes of one kind (`"ground"` or `"tower"`); `nil` includes every strike.
- `M.windowOne(s, lag, rise, fall)`: the same bump for strike `s` alone.
## Usage
```luau
local S = require("~.storm.schedule")
local generator = entity.find("storm").component("Generator")
local ph = S.phase(generator:getTime())
local flash = 0
for _, strike in ipairs(S.STRIKES) do
flash += S.envelope(ph, strike) * strike.weight
end
-- the same flash, as an anim expression for a procGraph
local flashExpr = proc.anim.expr(S.flashExpr())
-- a burst 0.15 s after each ground strike
local bursts = S.windowExpr("ground", 0.15, 12, 1.0)
```
# lightning_bolt
Additive unlit surface shader for lightning ribbons: a hot white core, a blue-violet halo, and
crawling flicker along the channel. On the ribbon `uv.x` runs along the bolt and `uv.y` across it.
Across the ribbon three falloffs stack: a tight white-hot core `(1.0, 0.97, 0.92)` at 17 times
brightness, a mid glow between the core colour and blue `(0.42, 0.52, 1.0)`, and a wide blue halo,
all softened to zero at the ribbon's edges. Hot spots crawl along the channel from a value noise
over `uv.x`, the storm clock `gt` and the bolt's own `phase`. Everything is scaled by `flash`, the
strike envelope (0 dark, 1 the return stroke), and by `gain`; while `flash` is below 0.003 the
fragments are discarded.
The `storm` graph (`storm/storm.procGraph`) draws one forked bolt per strike in the storm schedule
with it, each as its own output and material (additive, culling off, depth writes off): `flash` is
that strike's envelope expression, `gt` the graph time, `phase` the strike's index, and `gain` is
1.0. Each bolt is a jittered, smoothed, resampled line from the strike's top to its target, branched
three generations deep by `curve.branch`, and drawn as two crossed ribbons 3.4 wide that thin with
each generation. In the Procgen Effects Showcase world, the `fx_storm` scene plays the graph.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `flash` | float | 1.0 | Strike envelope, 0 dark to 1 the return stroke (range 0 to 4). |
| `gt` | float | 0.0 | The storm clock in seconds; drives the crawl of the hot spots. |
| `phase` | float | 0.0 | Per-bolt offset of the crawling hot spots. |
| `gain` | float | 1.0 | Multiplies the output (range 0 to 10). |
# storm_puff
Alpha-blended smoke puff on a camera-facing card (`zero_billboard`), with wispy domain-warped edges,
a sooty body, blue-white flash lighting and an ember glow while it is young. A domain-warped
four-octave value noise, drifting with time and offset by the seed, erodes a round body. The puff
fades in over the first 10 percent of its life and out between 0.45 and 1. Its dense core reads as
soot `(0.030, 0.034, 0.045)` and its thin edges pick up a slate sky `(0.13, 0.15, 0.20)`, scaled by
`ambient`. The storm's flash adds blue-white `(0.62, 0.70, 1.0)` light times `flash * flash_gain`,
and the heat lane adds an orange ember glow `(1.0, 0.36, 0.06)`. The alpha is the body times the
fades, the lane's opacity scale and `density`.
The `storm` graph (`storm/storm.procGraph`) draws its `smoke` output with it (transparent, `flash`
from the storm schedule's flash expression, `flash_gain` 1.7, `ambient` 1.0, `density` 0.8): smoke
rising from every strike, dragged downwind and billowing as it ages. The graph writes `data0` as
(life, a random seed, a heat ramp that falls from 1 to 0 by 0.28 of the life, 0.85). In the Procgen Effects Showcase world, the `fx_storm` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `flash` | float | 0.0 | Current strike brightness (range 0 to 8). |
| `flash_gain` | float | 1.0 | Multiplies `flash` (range 0 to 20). |
| `ambient` | float | 1.0 | Multiplies the soot-and-sky shade (range 0 to 8). |
| `density` | float | 0.9 | Multiplies the alpha (range 0 to 4). |
| `vertexBoundsDilation` | float | 6.5 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 born to 1 dead. |
| `data0.y` | Per-puff seed. |
| `data0.z` | Heat, 0 to 1: the ember glow. |
| `data0.w` | Opacity scale; below 0.003 the puff is discarded. |
# storm_sheet
Additive upright sheet of slanted rain streaks in patchy gust bands, turned to face the camera about
the vertical (`zero_billboard_axis` with world Y). Dozens of them sweep across the scene on the
wind, which makes the rain read as weather. On each sheet three layers of fine lines run along a
wind-slanted fall direction; every line has its own length and phase and falls with time, so they
read as individual drops. A value-noise gust pattern, drifting with time, makes the sheet thick in
gusts and threadbare between them. The sheet fades in and out over its life and toward its edges.
The streaks are a cool blue `(0.46, 0.56, 0.78)` lifted by `ambient + flash * flash_gain`, scaled by
`density` and faded with distance by `exp(-distance * fog)`.
The `storm` graph (`storm/storm.procGraph`) draws its `sheets` output with it (additive, `flash`
from the storm schedule's flash expression, `flash_gain` 0.9, `ambient` 0.16, `fog` 0.005, and
`density` animated as `0.55 + 0.25 * sin(t * 0.9) + 0.14 * sin(t * 2.3 + 1.7)`): up to 90 sheets 46
to 92 wide, born on a wide disc upwind and carried across on the gusts for 7.5 to 9.5 s each. The
graph writes `data0` as (life, a random seed, 0, 0). In the Procgen Effects Showcase world, the `fx_storm` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `flash` | float | 0.0 | Current strike brightness (range 0 to 8). |
| `flash_gain` | float | 1.0 | Multiplies `flash` (range 0 to 20). |
| `ambient` | float | 0.3 | Base brightness of the streaks (range 0 to 8). |
| `density` | float | 1.0 | Multiplies the streaks; the storm graph swells it with the gusts (range 0 to 8). |
| `fog` | float | 0.004 | Distance fade density (range 0 to 0.1). |
| `vertexBoundsDilation` | float | 46 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.x` | Life, 0 to 1: the fade in and out. |
| `data0.y` | Per-sheet seed: offsets the lines and the gust pattern. |
# storm_stone
Procedural rain-soaked ashlar masonry in world space: courses of blocks with per-block tint,
bevelled joints, weathering streaks, lichen and running water that darkens the stone and drops its
roughness. It is an engine-lit `surface()` shader that fills a `PbrSurface`.
The shader projects each face onto the plane it lies in: XZ where the normal is within about 44
degrees of vertical (`|n.y| > 0.72`), otherwise the vertical plane facing X or Z, and scales the
result by `scale`. Courses are 0.46 high with blocks 0.8 to 1.5 wide and a random offset per course.
Each block is a slightly rounded, chiselled slab with a dark joint; the height of the slab, the
chisel and a fine grain bend the normal by `bump`. Joints and bump fade out where the pattern gets
too fine for the screen (measured with `fwidth`), where the colour also eases toward a flat grey
`(0.21, 0.20, 0.20)`. The stone is a cool grey with warm variation per block, dark vertical stains
hang from the joints, and green lichen and grime creep up from the plinth below world height 7.
Water sheets down the walls in streaks that slide with time: wetness is 0.7 of `wet` between the
streaks and rises along them, darkening the albedo by up to half and dropping roughness from 0.9 to
0.16 (1.35 times that in the joints). Reflectance is 0.6 and occlusion is lower in the joints.
The `tower` graph (`storm/tower.procGraph`) builds the stone keep of the Procgen Effects Showcase world's `fx_storm` scene with it
through `stoneMat`, with `scale` 1.0, `wet` 0.9 and `bump` 1.0.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `scale` | float | 1.0 | Scale of the masonry pattern; larger values give smaller blocks (range 0.2 to 4). |
| `bump` | float | 1.0 | Strength of the bump normal (range 0 to 4). |
| `wet` | float | 0.85 | How soaked the stone is, 0 dry to 1 streaming. |
# storm_streak
Additive billboard streak for rain, splash droplets and sparks: a card that turns about its own Y
axis toward the camera (`zero_billboard_streak`). Colour comes from the element's `data0.rgb`, which
is HDR so sparks can run hotter than 1, and an element whose colour fades to black vanishes (below
0.002 in every channel it is discarded). Across the card the streak is rounded (`1 - across^2`), so
a thin card shows a soft edge; along it a bright head gives way to a long soft tail. The storm's
lightning lifts every card by `ambient + flash * flash_gain`, which makes falling rain flicker into
view in every strike. Distance fades the streak by `exp(-distance * fog)`, the streak fades in
between 1.5 and 9 units from the camera, and the result is scaled by `gain`.
The `storm` graph (`storm/storm.procGraph`) builds three additive materials from it:
| Use | `flash` | `flash_gain` | `ambient` | `fog` | `gain` |
|---|---|---|---|---|---|
| Rain | storm flash expression | 1.0 | 0.11 | 0.010 | 1.0 |
| Splash droplets | storm flash expression | 1.6 | 0.7 | 0.0045 | 1.0 |
| Sparks and the smaller sparks they throw | 0 | 0.0 | 1.0 | 0.003 | 1.0 |
For each, the graph turns the particle onto its velocity, sets its length from its speed and writes
`data0` as a colour times a fade that takes a slow particle to black: wind-driven rain in a cool
blue at random brightness, splash droplets thrown up where rain lands, and strike sparks cooling
from `(11, 9, 6)` to black. In the Procgen Effects Showcase world, the `fx_storm` scene plays the graph.
The shader includes `@builtin::shaderModules.billboard`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `flash` | float | 0.0 | Current strike brightness (range 0 to 8). |
| `flash_gain` | float | 1.0 | Multiplies `flash` (range 0 to 20). |
| `ambient` | float | 1.0 | Base brightness of the streak (range 0 to 8). |
| `fog` | float | 0.006 | Distance fade density (range 0 to 0.1). |
| `gain` | float | 1.0 | Multiplies the output (range 0 to 20). |
| `vertexBoundsDilation` | float | 0.05 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | HDR colour; black discards the element. |
# storm
A thunderstorm around a lone tower: rain driven slantwise by the wind that splashes off the ground
and the tower, lightning bolts striking on the schedule `schedule.module` beside it keeps, sparks and smoke
where each strike lands, and sheets of rain sweeping through. It is simulated on the GPU in one
solver family.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("storm", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
| Output | What it draws |
|---|---|
| `rain` | The falling rain, `storm_streak` shader |
| `splash` | Splashes where rain lands, `storm_streak` shader |
| `sheets` | Sheets of rain sweeping through, `storm_sheet` shader |
| `bolt1` to `bolt4` | The lightning bolts, `lightning_bolt` shader |
| `sparks` | Sparks where strikes land, `storm_streak` shader |
| `smoke` | Smoke rising where strikes land, `storm_puff` shader |
The timeline has no end and plays at 60 fps. In the Procgen Effects Showcase world's `fx_storm` scene, the
`StormDirector` component on the same entity reads this clock and, on the same schedule, swings
the sun to each bolt, blazes point lights at the strike, lights the cloud deck and the puddles,
shakes the camera and plays the thunder.
# tower
A lone stone keep: a plinth, a square shaft, a corbelled gallery with a crenellated parapet, a
round turret under a conical roof and an iron finial, 48.6 m to the tip of the finial. It is built
from boxes, cylinders, a cone and a sphere joined into one entity output: the stone draws with the
`storm_stone` shader and the finial with dark metal.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("tower", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph })
```
The output `keep` is the tower. It stands at the centre of the Procgen Effects Showcase world's `fx_storm` scene.
# wf_pool_mat
Water material for the plunge pool at the foot of the waterfall. `waterfall/wf_water.procGraph` builds
the still and falling water of the waterfall and assigns this material to one of its parts by name;
the graph's header states that the water materials are `.material` assets so the Model receives
their blend, cull and depth-write state. The `WaterfallWater` Generator entity in the Procgen Effects Showcase world's `fx_falls`
scene runs that graph. This material goes on `pool_skin`: a 21 x 18.5 m grid at height -0.35,
centred 4.4 m on the -z side of the waterfall's z (Z0 - 4.4).
Shader: `wf_pool` (`waterfall/shaders/wf_pool.shader`). Its README: plunge-pool water with travelling ripples,
churn turbulence, two-phase radial foam flow, depth-driven shore and rock foam, fresnel sky
reflection and sun glitter, and a refracted bed. It reads the scene depth already drawn under the
surface to choose between a lighter and a darker water colour and to put foam where the water meets
the bed and rocks; alpha runs from 0.55 to 1 with that depth term, plus foam.
Render state: `blend: alphaBlend`, `cull: none`, `depth_write: false`.
## Properties
- `origin_x: 0` and `origin_z: 42`: world x and z the shader measures positions from. 42 is the `Z0`
constant `waterfall/wf_water.procGraph` places the waterfall at.
- `impact_x: 0` and `impact_z: -3.5`: the plunge point, relative to the origin (default impact_z
-3.4). The water churns around it and the foam flows outward from it.
- `churn_radius: 3.7`: size of the churned zone around the plunge point, as the falloff `exp(-(d /
churn_radius)^2)` (default 3.6).
- `foam: 1`: blend weight of the foam over the water colour (default 1.0).
# wf_river_mat
Water material for the river that runs to the waterfall's lip. `waterfall/wf_water.procGraph` builds the
still and falling water of the waterfall and assigns this material to one of its parts by name; the
graph's header states that the water materials are `.material` assets so the Model receives their
blend, cull and depth-write state. The `WaterfallWater` Generator entity in the Procgen Effects Showcase world's `fx_falls` scene
runs that graph. This material goes on `river_skin`: a 7.2 x 13 m grid at the lip height (21.9),
centred 6.9 m on the +z side of the waterfall's z (Z0 + 6.9).
Shader: `wf_pool` (`waterfall/shaders/wf_pool.shader`). Its README: plunge-pool water with travelling ripples,
churn turbulence, two-phase radial foam flow, depth-driven shore and rock foam, fresnel sky
reflection and sun glitter, and a refracted bed. It reads the scene depth already drawn under the
surface to choose between a lighter and a darker water colour and to put foam where the water meets
the bed and rocks; alpha runs from 0.55 to 1 with that depth term, plus foam. The river uses the
same shader as the plunge pool (`wf_pool_mat`) with its own values.
Render state: `blend: alphaBlend`, `cull: none`, `depth_write: false`.
## Properties
- `origin_x: 0` and `origin_z: 42`: world x and z the shader measures positions from. 42 is the `Z0`
constant `waterfall/wf_water.procGraph` places the waterfall at.
- `impact_x: 0` and `impact_z: 60`: the point, relative to the origin, that the churn is centred on
and the foam flows away from. The river grid spans z 0.4 to 13.4 relative to the origin, so this
point lies upstream of it, and the foam flow moves toward the lip.
- `churn_radius: 3`: size of the churned zone around that point, as the falloff `exp(-(d /
churn_radius)^2)`.
- `foam: 0.55`: blend weight of the foam over the water colour (default 1.0).
# wf_sheet_back
Waterfall curtain material, the back one of the three staggered curtains of the falls.
`waterfall/wf_water.procGraph` builds the still and falling water of the waterfall and assigns this
material to one of its parts by name; the graph's header states that the water materials are
`.material` assets so the Model receives their blend, cull and depth-write state. The
`WaterfallWater` Generator entity in the Procgen Effects Showcase world's `fx_falls` scene runs that graph. The graph builds each
curtain from a 6.8 x 21.5 m grid of 44 x 220 cells stood upright with its top at the lip, and
assigns this material to `curtain_back`; the graph's comment states that the vertex shader places
the real curtain and the grid only carries bounds for culling.
Shader: `wf_sheet` (`waterfall/shaders/wf_sheet.shader`). Its README: a waterfall curtain with a ballistic
vertex path off the lip, packet-timed ribbon noise that tears with distance fallen, aeration to
white, backlit scatter and a refracted background. From its header: the vertex stage places every
vertex on the path water takes off a lip, leaving with `out_speed` and falling under gravity (9.81),
so the curtain bows out and widens; the fragment pattern is pinned to packets of water, so its
streaks stretch as the water speeds up and the sheet tears into ribbons as the breakup threshold
rises with the distance fallen. The cliff behind shows through, bent by the ripples.
Render state: `blend: alphaBlend`, `cull: none`, `depth_write: false`.
## Properties
- `z0: 42`: z of the lip line the water leaves from (the graph's `Z0`).
- `base_y: 21.9`: height of the lip, the top edge of the sheet.
- `height: 21.5`: drop from the lip to the plunge.
- `width: 9.8`: width of the sheet at the lip (default 6.6).
- `out_speed: 1.45`: horizontal speed the water leaves the lip with; the sheet moves `out_speed`
times the fall time away from `z0` toward -z (default 1.7).
- `spread: 0.2`: extra width at the bottom, as width x (1 + spread x s) with s running from 0 at the
lip to 1 at the plunge (default 0.22).
- `seed: 11`: seed for the sway and the noise patterns (default 3).
- `density: 1.1`: multiplier on the sheet's body coverage (default 1.15).
- `z_bias: -0.32`: z offset subtracted from the whole sheet (default 0); -0.32 shifts this curtain
0.32 m toward +z, behind the main one.
# wf_sheet_front
Waterfall curtain material, the front one of the three staggered curtains of the falls.
`waterfall/wf_water.procGraph` builds the still and falling water of the waterfall and assigns this
material to one of its parts by name; the graph's header states that the water materials are
`.material` assets so the Model receives their blend, cull and depth-write state. The
`WaterfallWater` Generator entity in the Procgen Effects Showcase world's `fx_falls` scene runs that graph. The graph builds each
curtain from a 6.8 x 21.5 m grid of 44 x 220 cells stood upright with its top at the lip, and
assigns this material to `curtain_front`; the graph's comment states that the vertex shader places
the real curtain and the grid only carries bounds for culling.
Shader: `wf_sheet` (`waterfall/shaders/wf_sheet.shader`). Its README: a waterfall curtain with a ballistic
vertex path off the lip, packet-timed ribbon noise that tears with distance fallen, aeration to
white, backlit scatter and a refracted background. From its header: the vertex stage places every
vertex on the path water takes off a lip, leaving with `out_speed` and falling under gravity (9.81),
so the curtain bows out and widens; the fragment pattern is pinned to packets of water, so its
streaks stretch as the water speeds up and the sheet tears into ribbons as the breakup threshold
rises with the distance fallen. The cliff behind shows through, bent by the ripples.
Render state: `blend: alphaBlend`, `cull: none`, `depth_write: false`.
## Properties
- `z0: 42`: z of the lip line the water leaves from (the graph's `Z0`).
- `base_y: 21.9`: height of the lip, the top edge of the sheet.
- `height: 21.5`: drop from the lip to the plunge.
- `width: 7.6`: width of the sheet at the lip (default 6.6).
- `out_speed: 1.95`: horizontal speed the water leaves the lip with; the sheet moves `out_speed`
times the fall time away from `z0` toward -z (default 1.7).
- `spread: 0.26`: extra width at the bottom, as width x (1 + spread x s) with s running from 0 at
the lip to 1 at the plunge (default 0.22).
- `seed: 27`: seed for the sway and the noise patterns (default 3).
- `density: 0.8`: multiplier on the sheet's body coverage (default 1.15).
- `z_bias: 0.34`: z offset subtracted from the whole sheet (default 0); 0.34 shifts this curtain
0.34 m toward -z, in front of the main one.
# wf_sheet_main
Waterfall curtain material, the middle of the three staggered curtains of the falls.
`waterfall/wf_water.procGraph` builds the still and falling water of the waterfall and assigns this
material to one of its parts by name; the graph's header states that the water materials are
`.material` assets so the Model receives their blend, cull and depth-write state. The
`WaterfallWater` Generator entity in the Procgen Effects Showcase world's `fx_falls` scene runs that graph. The graph builds each
curtain from a 6.8 x 21.5 m grid of 44 x 220 cells stood upright with its top at the lip, and
assigns this material to `curtain_main`; the graph's comment states that the vertex shader places
the real curtain and the grid only carries bounds for culling.
Shader: `wf_sheet` (`waterfall/shaders/wf_sheet.shader`). Its README: a waterfall curtain with a ballistic
vertex path off the lip, packet-timed ribbon noise that tears with distance fallen, aeration to
white, backlit scatter and a refracted background. From its header: the vertex stage places every
vertex on the path water takes off a lip, leaving with `out_speed` and falling under gravity (9.81),
so the curtain bows out and widens; the fragment pattern is pinned to packets of water, so its
streaks stretch as the water speeds up and the sheet tears into ribbons as the breakup threshold
rises with the distance fallen. The cliff behind shows through, bent by the ripples.
Render state: `blend: alphaBlend`, `cull: none`, `depth_write: false`.
## Properties
- `z0: 42`: z of the lip line the water leaves from (the graph's `Z0`).
- `base_y: 21.9`: height of the lip, the top edge of the sheet.
- `height: 21.5`: drop from the lip to the plunge.
- `width: 9.2`: width of the sheet at the lip (default 6.6).
- `out_speed: 1.7`: horizontal speed the water leaves the lip with; the sheet moves `out_speed`
times the fall time away from `z0` toward -z (default 1.7).
- `spread: 0.22`: extra width at the bottom, as width x (1 + spread x s) with s running from 0 at
the lip to 1 at the plunge (default 0.22).
- `seed: 3`: seed for the sway and the noise patterns (default 3).
- `density: 1.15`: multiplier on the sheet's body coverage (default 1.15).
- `z_bias: 0`: z offset subtracted from the whole sheet (default 0); the middle curtain sits at the
unshifted position.
# wf_particle
Waterfall particle card: mist, foam and droplet looks selected by `mode`, with sun forward-scatter
and a view-dependent rainbow, premultiplied output and a soft depth fade. One shader draws the whole
spray family:
- Mode 0, mist: big soft camera-facing cards (`zero_billboard`). A four-octave `fx_noise` fBm,
offset per particle and drifting with time, billows a round puff. It is lit by the ambient light
and by the sun through a Henyey-Greenstein phase (g 0.62), so the mist glows when the sun is
behind it. The rainbow lives in these cards: a band 42 degrees from the anti-solar point, about
1.7 degrees half-width, red outside and violet inside, scaled by `rainbow`.
- Mode 1, foam: bright bubbly clumps on camera-facing cards. Worley cells drift through a ragged
disc, lit by ambient and sun light.
- Mode 2, droplet: a teardrop on a card turned onto its motion (`zero_billboard_streak`), with a fat
head leading along +Y and a tail thinning to a hair, plus a sun glint on the head when the sun is
behind it.
- Mode 3, flat foam: the foam look on a card lying on the water, its right along world X and its up
along world Z (`zero_card_clip`).
Every card dissolves into whatever is drawn behind it over `depth_fade` (`zero_scene_depth_fade`),
so cards blend softly where they meet rock or water. The output is premultiplied: the colour, scaled
by `energy`, carries its own coverage, and the rainbow and glint add on top.
Three graphs beside it in `waterfall/` draw with it, all premultiplied with culling off and depth
writes off, and in the Procgen Effects Showcase world the `fx_falls` scene places them: `wf_mist` (mode 0, `opacity` from its `density` input, default
0.7, `energy` 1.7, `depth_fade` 2.2, `rainbow` from its `rainbow` input, default 1.0), `wf_foam`
(mode 1, `depth_fade` 0.9, `rainbow` 0) and `wf_spray` (mode 2, `depth_fade` 0.6, `rainbow` 0). Each
writes `data0` from a colour ramp over life and `data1` as (particle id, life, 0).
The shader includes `@builtin::shaderModules.billboard`, `fx_noise` and `zero::scene_depth`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `mode` | float | 0 | Look selector, range 0 to 3: mist, foam, droplet, flat foam. |
| `opacity` | float | 1 | Multiplies the coverage (range 0 to 4). |
| `energy` | float | 1 | Multiplies the lit colour (range 0 to 8). |
| `depth_fade` | float | 1.2 | Distance over which a card fades into the surface behind it (range 0 to 10). |
| `rainbow` | float | 0.6 | Strength of the rainbow in mist cards (range 0 to 4). |
| `vertexBoundsDilation` | float | 11 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
## Lanes
| Lane | Carries |
|---|---|
| `data0.rgb` | Colour, multiplied by the lighting. |
| `data0.a` | Opacity over life. |
| `data1.x` | Particle id; its low 16 bits seed the per-card noise offsets. |
| `data1.y` | Life, 0 to 1. |
| `data1.z` | Spin phase, as the shader header names it; the graphs write 0. |
# wf_pool
Plunge-pool water: travelling ripples, churn turbulence that grows toward the impact, a two-phase
radial foam flow, depth-driven shore and rock foam, a fresnel sky reflection and sun glitter. All
positions are taken relative to the waterfall origin (`origin_x`, `origin_z`) on the world XZ plane.
The surface normal sums four travelling sine waves, rings running outward from the impact point, and
an `fx_noise` fBm chop whose strength rises with the churn, a Gaussian falloff of distance from the
impact over `churn_radius`. The water colour comes from the depth already drawn underneath
(`zero_scene_depth_fade`): light teal where the bed is close, dark blue-green where it is deep. Over
that it mixes a sky gradient from a horizon `(0.62, 0.76, 0.92)` to a zenith `(0.18, 0.34, 0.66)` by
a Schlick fresnel term, and adds two lobes of sun glitter, stronger in the churn. Foam is drawn from
two flow phases advected outward from the impact and cross-faded so the cycle loops smoothly, plus
the churn itself, a spread term around the impact, ragged contact foam where the water meets rock or
shore (a depth fade over 0.42), fine breakup noise and worley lace cells. The foam colour is a lit
white, mixed in by `foam`. The alpha runs from 0.55 at the shore to 1 in deep water, raised by the
foam.
Two materials use it, both alpha-blended with culling off and depth writes off, and the `wf_water`
graph (`waterfall/wf_water.procGraph`) draws both in the Procgen Effects Showcase world's `fx_falls` scene: `wf_pool_mat` on the 21 by
18.5 plunge pool (origin z 42, impact at (0, -3.5), `churn_radius` 3.7, `foam` 1), and
`wf_river_mat` on the 7.2 by 13 river above the lip (origin z 42, impact at (0, 60), `churn_radius`
3, `foam` 0.55).
The shader includes `fx_noise` and `zero::scene_depth`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `origin_x` | float | 0 | World X of the waterfall origin; positions are measured from it. |
| `origin_z` | float | 0 | World Z of the waterfall origin. |
| `impact_x` | float | 0 | X of the plunge point, relative to the origin. |
| `impact_z` | float | -3.4 | Z of the plunge point, relative to the origin. |
| `churn_radius` | float | 3.6 | Radius of the churned water around the plunge point (range 0.5 to 20). |
| `foam` | float | 1.0 | How strongly the foam is mixed over the water (range 0 to 2). |
# wf_rock
World-space procedural wet rock: strata, moss, mineral streaks, bump and spray-driven wetness for
the meshed signed-distance cliff of the waterfall. Every pattern is computed from the world position
relative to the waterfall's origin (`origin_x`, `origin_y`, `origin_z`). It is a `surface()` shader
that fills a `PbrSurface`.
A height field of `fx_noise` fBm plus ridged fBm gives the bump normal, scaled by `bump`. The dry
rock is warm and pale, banded into long warped horizontal beds with grain. Dark seeps run down the
face on either side of the falls, fading out between 6 and 17 units from its centre line. Wetness
comes from the falls' spray around the plunge line, the waterline around the pool, and the seeps;
wet rock is darker (by up to 42 percent) and glossier (roughness from 0.92 down to 0.28). Moss grows
on gentle upward faces that stay wet, scaled by `moss`, and rust-brown mineral streaks run down the
face. The albedo is multiplied by `gain` and `tint`, and occlusion follows the height field.
The `wf_env` graph (`waterfall/wf_env.procGraph`) applies it to the cliff, plunge basin and boulders it
contours from signed-distance solids, with the origin at z 42, `bump` 0.55 and `moss` 1.0. The
Procgen Effects Showcase world's `wf_ground_mat` material uses it with the same origin, `bump` 0.4, `gain` 1.15, `moss` 1.3 and a
warm `tint` (1.25, 1.08, 0.85), and the Procgen Effects Showcase world's `fx_falls` scene places that material.
The shader includes `fx_noise` and `@builtin::shaderModules.pbr_shading`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `bump` | float | 0.55 | Strength of the bump normal (range 0 to 3). |
| `moss` | float | 1.0 | Amount of moss on wet upward faces (range 0 to 2). |
| `tint` | color | [1, 1, 1, 1] | Multiplies the albedo. |
| `gain` | float | 0.75 | Multiplies the albedo (range 0 to 2). |
| `origin_x` | float | 0 | World X of the waterfall origin. |
| `origin_y` | float | 0 | World Y of the waterfall origin. |
| `origin_z` | float | 0 | World Z of the waterfall origin. |
# wf_sheet
Waterfall curtain: a ballistic vertex path off the lip, packet-timed ribbon noise that tears with
distance fallen, aeration to white, backlit scatter and a refracted background.
The vertex stage reads each vertex's place on the sheet back from the grid's own position: `u`
across a 6.8-wide grid and `s` from 0 at the lip (`base_y`) to 1 at the bottom of `height`. It then
places the vertex on the path water takes off a lip: it leaves with `out_speed` and falls under
gravity (9.81), so after falling a distance `s * height` it has moved `out_speed * t` outward along
-Z from `z0`. The sheet widens by `spread` toward the bottom, sways a little more the farther it has
fallen, and is pushed along -Z by `z_bias`.
The fragment pins its pattern to packets of water: a packet released at time `t0` has fallen `0.5 *
g * (t - t0)^2`, so the noise is sampled at the time each point's water left the lip. As the water
speeds up its streaks stretch, and the sheet tears into ribbons because the breakup threshold rises
with the distance fallen. Broad tongues, fine strands and grain come from `fx_noise` seeded by
`seed`. Aeration turns the water from a deep green-blue to white where the flow is thin and fast,
crests catch the sun through a Henyey-Greenstein phase (g 0.55), and the cliff behind shows through,
bent by the ripples (`zero_scene_color_refract`). The tail fades into the plunge over the last 7
percent and the top edge fades in at the lip.
Three materials use it, all alpha-blended with culling off and depth writes off, and the `wf_water`
graph (`waterfall/wf_water.procGraph`) hangs one curtain from each on a 6.8 by 21.5 grid in the Procgen Effects Showcase world's
`fx_falls` scene:
| Material | `width` | `out_speed` | `spread` | `seed` | `density` | `z_bias` |
|---|---|---|---|---|---|---|
| `wf_sheet_back` | 9.8 | 1.45 | 0.2 | 11 | 1.1 | -0.32 |
| `wf_sheet_main` | 9.2 | 1.7 | 0.22 | 3 | 1.15 | 0 |
| `wf_sheet_front` | 7.6 | 1.95 | 0.26 | 27 | 0.8 | 0.34 |
All three use `z0` 42, `base_y` 21.9 and `height` 21.5.
The shader includes `fx_noise`, `zero::scene_depth` and `zero::scene_color`.
## Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
| `width` | float | 6.6 | Width of the curtain at the lip (range 0.5 to 40). |
| `height` | float | 21.5 | Height the water falls (range 1 to 80). |
| `z0` | float | 0 | World Z of the lip (range -1000 to 1000). |
| `base_y` | float | 21.9 | World height of the lip (range -10 to 100). |
| `out_speed` | float | 1.7 | Speed at which the water leaves the lip along -Z (range 0 to 8). |
| `spread` | float | 0.22 | How much wider the curtain is at the bottom, as a fraction of `width` (range 0 to 1). |
| `seed` | float | 3 | Seed for the curtain's noise and sway (range 0 to 99). |
| `density` | float | 1.15 | Multiplies the sheet's body before it becomes alpha (range 0 to 3). |
| `z_bias` | float | 0.0 | Shift of the whole curtain along -Z (range -3 to 3); the three curtain materials use -0.32, 0 and 0.34. |
| `vertexBoundsDilation` | float | 3.5 | How far the vertex hook moves a vertex past the mesh, in world units: the culling bounds grow by it so a moved vertex stays inside them. |
# wf_env
The rock of the waterfall scene: the cliff, the plunge basin and the boulders, as signed-distance
solids contoured to one mesh. The cliff face is the z = 0 plane facing -z, the river runs along +z
on top of the plateau and spills over the lip at (0, 21.9, 0), the ground is at y = 0 and the pool
surface at y = -0.35. The cliff and boulders carry deep fluted noise; the ground slab with the
basin cut into it carries only a little, so the pool bed stays smooth and the shore reads as a
beach. The mesh draws with the `wf_rock` shader.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wf_env", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, params = { resolution = 150 } })
```
| Input | Default | What it does |
|---|---|---|
| `resolution` | 150 | Cells along the contoured volume's longest side (40 to 260): more is finer rock and a longer cook. |
| `roughness` | 1 | How deep the noise cuts into the cliff and boulders (0 to 3). |
| `seed` | 7 | Re-rolls the noise. |
The output `env` is the one mesh. It belongs with `wf_water`, `wf_spray`, `wf_foam` and `wf_mist`,
which share its frame.
# wf_foam
The white boil where the waterfall lands: churning clumps of foam that heave up out of the plunge,
roll outward across the pool and burst. They are bubbly cards (the `wf_particle` shader in its
foam mode) born on a ring around the impact point at (0, -0.3, -3.5) and driven by a vortex around
it, in the frame `wf_env` sets up.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wf_foam", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
The output `foam` is the cards. The timeline has no end and plays at 30 fps.
# wf_mist
The waterfall's mist: big soft cards born at the plunge and along the falling face, carried
outward across the pool on a gentle breeze and curl turbulence. The `wf_particle` shader draws a
rainbow inside these cards from the angle between the view ray and the point opposite the sun,
so the bow appears where it would in the real spray.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wf_mist", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
| Input | Default | What it does |
|---|---|---|
| `rainbow` | 1 | Strength of the rainbow drawn in the mist (0 to 4). |
| `density` | 0.7 | How thick the mist stands (0 to 3). |
The output `mist` is the cards. The timeline has no end and plays at 30 fps.
# wf_spray
The waterfall's droplets: beads torn off the lip that streak down beside the falling curtain, the
plume thrown up where it lands, and the smaller drops those throw when they strike water and rock,
all one GPU solver family drawn with the `wf_particle` shader. It shares `wf_env`'s frame: the
lip runs along x at y = 21.9.
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wf_spray", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
The output `spray` is every droplet. The timeline has no end and plays at 30 fps.
# wf_water
The waterfall's water: three staggered falling curtains, the plunge pool and the river above the
lip. The curtains draw with the `wf_sheet_*` materials, whose vertex shader shapes the falling
sheet; the pool and river draw with `wf_pool_mat` and `wf_river_mat`. Its parts stand at z = 42 in
the generator's own space, and the materials state that spot too (`z0`, `origin_z`).
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("wf_water", { position = { 0, 0, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load", ending = "loop" })
```
The output `water` is one entity output holding every part. The timeline has no end and plays at
30 fps, which keeps the water's materials moving.
# Package (asset type)
A package is an asset — a `<Name>.package/` folder that bundles
components, modules, scenes, presets, tools, and any other typed
sub-assets under one identity, installed into a world as one unit.
Packages are how authoring sets get distributed across worlds.
A **library** is the addressable root scope content resolves under
(`@builtin`, `@<name>`) — a whole world mounted into this one under a
namespace, via `zeromind.installLib` / `world.installLibrary`. A package is
an asset that lives inside such a root, and one library holds as many
packages as its author put there. A package is installed by guid the
way every asset is (`zeromind.install`, `zeromind.installAsset`).
## When to use one
- You're authoring a coherent feature suite (character controller
stack, voxel engine, UI kit, animation library) and want to ship
it as one install target.
- You want a stable identity worlds can install and resolve sub-assets
against (`@<lib>::pkg.sub_module`).
- You want versioning + a per-package metadata layer that travels with
the contents.
If you're authoring inside a single world and don't need a separate
install lifecycle, you don't need a package — drop your `.component`
/ `.module` / etc. directly in `/zero/source/`. Packages are for
cross-world distribution.
## Where it lives
- Source: `/zero/source/.../<Name>.package/` (or, for legacy
packages, a folder containing `package.yaml` at its root).
- Identity: `<Name>` (the `.package` suffix collapses in identity —
mirrors `.module`). A file at `<Name>.package/<sub>/init.luau`
resolves under the same identity it would at `<Name>/<sub>/init.luau`.
- Folder shape (container-style — open child set):
- `package.yaml` — metadata (name, version, description).
**Required.**
- `README.md` — instance documentation. **Required.**
- `.metadata` — agent-editable tags + free-form fields. **Required.**
- Sub-asset folders: any combination of `.component/`,
`.module/`, `.scene/`, `.preset/`, `.tool/`, `.toolbox/`,
`.material/`, `.shader/`, etc.
The validator permits unlisted children (`allow_unlisted: true`) by
design — packages are open containers.
## How to create one
```luau
asset.create("package", "<Name>")
-- Creates: /zero/source/<Name>.package/
-- package.yaml (metadata scaffold)
-- README.md (instance README template)
-- `folder` places it in a subfolder of /source instead of the root:
asset.create("package", "<Name>", { folder = "systems" })
```
Then scaffold sub-assets inside it:
```luau
local pkg = asset.resolve("<Name>.package")
asset.create("component", "Spinner", { into = pkg })
asset.create("module", "util", { into = pkg })
```
## How it operates
1. **Registration.** Writing `package.yaml` indexes the package with
the package registry.
2. **Install.** A package is installed by guid, the way every asset
is: `zeromind.install` from the shared library, or
`tools.use("zeromind", "installAsset", <guid>)` inside the engine. The
install writes the package's files (and the deps its `.refs`
record) into this world's source, so its sub-assets resolve here.
`tools.use("zeromind", "preview", <guid>)` reports what an install would
write before it writes anything.
3. **Sub-asset resolution.** Because `.package` collapses in
identity, sub-asset paths work whether the package folder is
suffixed (`<Name>.package/<sub>`) or not (`<Name>/<sub>`).
4. **Updates.** `tools.use("zeromind", "updates")` lists installed content
with upstream changes available; re-running the install pulls them.
5. **Metadata.** `package.yaml::name` overrides the folder stem if
present (rare); `version` + `description` are surfaced in the
package index.
6. **Hot reload.** Edits to sub-assets hot-reload as normal; edits to
`package.yaml` reload package metadata.
## Discovery
- `asset.list("package")` — every registered package.
- `asset.inspect("<name>")` — sub-asset catalog, source, this type
README.
- `ls /zero/source/<Name>.package` lists the files the folder holds.
- `library.list()` — the library assets registered in this world,
including the ones packages under an installed library contribute.
## Authoring conventions
- Name packages by their feature (`voxels`, `cameraRigs`,
`dialogSystem`), not by their owner — the host library is the
`@<lib>::` scope prefix.
- Cluster cohesive sub-assets together. A "character controller"
package should ship the controller component, its preset configs,
its supporting modules, and a demo scene — not just the component.
- Document the package's surface in `README.md` — what install
installs, what each sub-asset does, how they relate. Agents reading
`cat /zero/source/<Name>.package/README.md` get the full picture in one shot.
- Version conservatively — breaking a package means breaking every
world that installed it. Use semantic version bumps.
## Common pitfalls
- **Missing `package.yaml`.** Without it the folder isn't recognized
as a package even if it has a `.package/` suffix.
- **Dependency installs.** A package that depends on another package
must declare it (in `package.yaml::dependencies` once supported)
and the parent world must install dependencies in order.
- **Identity collapse.** Sub-asset paths skip the `.package` suffix.
Don't include `.package` in `require` strings.
## Referencing inside a package: the `~` rule
`~` means **this package**, resolved structurally from the referring file's own
location. It is the only package-specific syntax. It is rename-safe (no package
name is ever written) and library-agnostic (it never names the host library, so
it survives `@builtin` being renamed or the package being mounted elsewhere).
`~.tail` maps to `<pkg>.package/tail` from anywhere inside the package.
| Referencing from | Use | Example |
|---|---|---|
| An asset/module **inside this package** | `~.tail` | `require("~.modules.myThing")`, `shader: "~.shaders.myThingBlend"` |
| A **different package** under the same root | root-relative identity | `require("@root::systems.otherPkg.modules.x")` |
| A **different library's** public API (a deliberate external dependency) | `@otherlib::…` | `require("@physics::modules.rigidbody")` |
**Never write `@builtin::` (or any host-library name) to reference your own
package** — that couples the package to the library it currently lives in and
breaks if the library is renamed or the files move. Use `~.`. It works from
Luau `require()` and from asset-reference strings in data files (a material's
`mat.yaml` `shader:` field, a component's default asset ref); the engine
resolves it against the referring file's own package. `asset.resolve` takes a
`~.` name the same way, against the package of the file that calls it:
`asset.resolve("~.materials.myThingStandard", "material")`.
`~.` is the package-relative self-reference; `@root::` is the root-relative one
(the library root, or the world source tree for non-library content). The
`core/requiring-modules` guide covers the root model in full.
A package that ships substantial functionality should include a `.guide` asset
(a `<name>.guide/guide.md` folder anywhere under the package); the `guides` tool
discovers it live, so collaborators find the system the way they find built-in
guides. A package's test suite reaches the shared `Test` framework through its
enclosing asset: `local Test = asset.containing(__FILE__).modules.shared`
(`asset.containing` returns the deepest enclosing typed asset, the `.testSuite`,
not the outer `.package`).
## Related types
- Any of `.component`, `.module`, `.scene`, `.preset`, `.tool`,
`.toolbox`, `.material`, `.shader`, `.style`, `.service`,
`.importer` — packages bundle these.
## In the Inspector
Package: the name, version and description package.yaml declares, how many things the package holds and what they are, and **Open** for package.yaml. A package.yaml that does not decode is listed under Problems with the decoder's error.