Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

readme.md

imageBasedLighting system

The sky as a light source — metal reflects what is actually above it, and matte surfaces pick up the sky's colour in the directions they face.

A surface lit only by direct lights has nothing to show where no light points. Polished metal is the extreme case: almost all of what it shows is reflected surroundings, so without an environment it renders near black with a pinpoint highlight, which is why untextured metal reads as plastic.

Using it

Environment lighting is a component — author it once and it persists:

local id = entity.spawn("environment").id
entity(id).component.add("EnvironmentLighting", {
    diffuse = 1.0,    -- sky-lit diffuse on matte surfaces
    specular = 1.0,   -- environment reflection on glossy surfaces
})

The sky's colours and the sun come from the scene, so recolouring the sky recolours the reflections with nothing on the component moving.

For code that changes it at runtime — moving indoors, a cutscene, a quality slider — reach for the module:

local ibl = require("@builtin::systems.imageBasedLighting.ibl")

ibl.set({ diffuse = 0.4, specular = 1.0 })  -- any subset; the rest is kept
ibl.follow()                                 -- resume tracking the scene's sky
ibl.refresh()                                -- carry the scene's sky and sun in
ibl.get()
ibl.active()
ibl.clear()

A set from code holds against an EnvironmentLighting component in the same scene: the component pushes on the frames one of its own fields moves, and such a move puts all five of its values back in force at once. The sky and sun keep following the scene through either of them, until a set pins one of them.

What the knobs do

fieldeffect
diffuseHow much sky-lit diffuse reaches matte surfaces.
specularHow much environment reflection reaches glossy surfaces.
skyIntensityScales the environment's radiance before either term.
horizonSharpnessHow tightly the gradient hugs the horizon. Higher keeps zenith colour further down the sky.
sunSizeAngular size of the sun in the environment. Larger spreads its highlight over a wider part of a reflection.

diffuse and specular are separate because an interior usually wants the sky's diffuse contribution pulled down while its reflections stay, and an exterior often wants the reverse.

Passing a colour or a sun direction explicitly pins it and takes the scene off automatic tracking for that value. follow() puts it back.

How it renders

Four compute passes after lighting: the sky evaluated over the sphere into an equirectangular map, that map convolved into a diffuse irradiance map and into GGX-prefiltered specular levels, then both applied from the G-buffer.

Prefiltering is why this is more than one pass. A rough surface gathers light from a wide cone, and gathering it per pixel per frame would mean hundreds of samples per pixel; convolving once into a small map and reading one texel per pixel is what makes it affordable.

The maps are small enough that rebuilding them each frame costs less than a full-screen pass, which is what lets the sky change continuously with nothing to invalidate.

Files

imageBasedLighting.package/
  EnvironmentLighting.component/     -- the scene's environment lighting
  ibl.module/                        -- ibl.set / .follow / .refresh / .get / .clear / .active
  environmentLighting.renderFeature/ -- the environment, convolution and apply passes