env
The film slice that makes GREEN SKY's world look like it is standing outdoors: the sky as a light source, reflections of what is on screen, and one wetness number that every surface in the frame acknowledges.
greensky/env — the scene's render environment
The film slice that makes GREEN SKY's world look like it is standing outdoors: the sky as a light source, reflections of what is on screen, and one wetness number that every surface in the frame acknowledges.
What it owns
| system | package | what it buys this film |
|---|---|---|
EnvironmentLighting | @builtin::systems.imageBasedLighting | Metal, wet concrete and standing water reflect the sky instead of black. No bake, no probe — it reads the scene's ProceduralSky and the scene's sun every frame, so the sky slice recolouring the gradient recolours every reflection, and a discharge reaches the water because the discharge is the sun for three frames. |
ScreenSpaceReflections | @builtin::systems.reflections | The hangar, the marker posts, the rain shafts and the gliders standing in the flood. The environment carries the sky; this carries the geometry. |
Weather | @builtin::systems.weather | The whole-world half of the arc. One number darkens and glosses the crust, the concrete, the hangar, the posts, the tie-down rings and the aircraft at once, straight off the g-buffer. |
All three are authored as components on one temporary entity,
gs_environment, spawned in build(). The per-act values are written to
the component fields from update(c) on the film's own storm and
clear ramps — the same two ramps the sky slice blends its palette on.
Nothing here runs a clock of its own.
The one cross-slice call
greensky/pan owns how wet the ground reads — Pan.wetness(t, flood),
which leads the flood because the rain darkens the crust well before there
is standing water on it. This slice reads that curve rather than inventing
a second one, so the surface response and the water level can never
disagree.
Why the balance is on diffuse and not on the ambient light
The scene's ambient Light does environment lighting's job badly — a flat
constant added to every surface regardless of which way it faces — and the
obvious move is to scale it down as the sky-lit term comes in. That move
does not work, and the reason is worth knowing before anyone tries it
again.
A component field write is DEFERRED. Reading Light.intensity inside a
frame answers the last COMMITTED value, never what the sky slice queued
earlier in the same frame: ten sky-then-env pairs driven in one call all
read back the same 0.18. So the read a trim needs cannot tell "the sky
slice just wrote its palette value" from "this is the trimmed value I
wrote last frame", and the two readings lead to different answers — one
leaves the light alone, the other multiplies it a second time. Driven per
frame that walks the whole scene to black; measured, 0.18 became 0.081
became 0.036 in three calls.
So the sky-lit term is balanced against the ambient light through
ENV.diffuse, which this slice owns outright: nothing else writes it and
nothing has to be read back. Act I is the smallest of the three because a
white salt pan under a 3.4-intensity sun is where a sky-lit term most
easily takes the frame past white; act II is the largest because the sun
is crushed to 0.55 there and the sky-lit term is most of what holds the
middle of the film out of black.
Reading it back
local ibl = require("@builtin::systems.imageBasedLighting.ibl")
local ssr = require("@builtin::systems.reflections.ssr")
local weather = require("@builtin::systems.weather.weather")
ibl.get() -- the sky and sun the environment is carrying right now
ssr.get() -- intensity, distance, cutoff, quality
weather.get() -- wetness, gloss, and the three colours
active() on any of the three reports only that create() was once
called — it stays true after a render error kills the feature (#9391). It
is not proof that anything is drawing. Judge these with a picture, from
capture { source = "screen" } in play: all three are render features, and
render features are skipped for offscreen capture targets (#6952).
Scoped to this part · feeds back into the world's score.