Builtin Library
README.md
areaLights system
Lights that emit from a surface rather than from a point.
Every screen, neon sign, glowing window, ceiling panel and lantern face is an area emitter. Standing a point light in the middle of one gets the brightness roughly right and the character completely wrong: a point puts a pinpoint reflection on the floor and a radial falloff on the wall, where the real thing puts the shape of the emitter and a falloff that follows its extent.
Using it
An emitter that should follow an entity's face is a component:
local id = entity.spawn("tv").id
entity(id).component.add("SurfaceLight", {
width = 2.0,
height = 1.2,
face = "front", -- front | back | up | down | left | right
intensity = 6.0,
range = 12,
})
The rectangle turns with the entity, so a tilted screen lights the direction it
actually points. Every field is live, and an intensity of 0 removes the light.
For code placing emitters directly:
local surfaceLight = require("@builtin::systems.areaLights.surfaceLight")
surfaceLight.set("strip", {
position = { 0, 5, -12 },
right = { 1, 0, 0 }, up = { 0, 1, 0 },
width = 0.6, height = 7,
color = { 1, 0.72, 0.35 }, intensity = 5,
})
surfaceLight.remove("strip")
surfaceLight.configure({ diffuse = 1, specular = 0.5 })
surfaceLight.clear()
What the knobs do
| field | effect |
|---|---|
width / height | Size of the emitting rectangle, world units. The shape of the light is the shape of this. |
face | Which way the entity emits. |
range | How far the light carries, with a smooth cutoff at the end. |
intensity | Brightness. 0 removes the light. |
colorR / colorG / colorB | Emitter colour. |
offset | Distance the rectangle sits proud of the entity's origin, along the face's normal. |
diffuse / specular | Scales on the two terms — the wash across a surface, and the reflected shape of the emitter. |
Shape is the point. A wide panel puts a broad pool on the floor; a narrow strip puts a narrow elongated one. That difference is not reachable with a point light at any brightness.
At most 32 emitters are held at once, because the shader loops every light for every pixel.
How it renders
areaLighting.renderFeature runs one compute pass after lighting. Diffuse is
the exact irradiance of the quad — Lambert's formula over its four edges — which
matters where a point approximation fails hardest: a wide emitter close to a
wall. Specular uses the representative point on the rectangle nearest the
reflection ray, with the lobe widened to cover the emitter's solid angle and its
peak scaled down to match, so a large emitter reads broader rather than
brighter.
Files
areaLights.package/
SurfaceLight.component/ -- an emitter on an entity's face
surfaceLight.module/ -- the emitter registry and scales
areaLighting.renderFeature/ -- the compute pass