Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

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

fieldeffect
width / heightSize of the emitting rectangle, world units. The shape of the light is the shape of this.
faceWhich way the entity emits.
rangeHow far the light carries, with a smooth cutoff at the end.
intensityBrightness. 0 removes the light.
colorR / colorG / colorBEmitter colour.
offsetDistance the rectangle sits proud of the entity's origin, along the face's normal.
diffuse / specularScales 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