Rectangular Area Lights
RectAreaLight adds a solid-color or textured rectangular emitter to PBR and Standard
materials. Like other Lite lights, it is scene-free state: create it, edit its
properties, then add it to the scene.
import { addToScene, createEngine, createFreeCamera, createPbrMaterial, createRectAreaLight, createSceneContext, createSphere, enableRectAreaLights, registerScene, startEngine,} from "@babylonjs/lite";
const engine = await createEngine(canvas);const scene = createSceneContext(engine);scene.camera = createFreeCamera({ x: 0, y: 2, z: -6 }, { x: 0, y: 1, z: 0 });
const sphere = createSphere(engine);sphere.position.y = 1;sphere.material = createPbrMaterial({ baseColorFactor: [0.8, 0.2, 0.1, 1], metallicFactor: 0.5, roughnessFactor: 0.25,});addToScene(scene, sphere);
const light = createRectAreaLight([0, 2, 3], 4, 2, 3);light.diffuse = [1, 1, 1];light.specular = [1, 1, 1];addToScene(scene, light);
await enableRectAreaLights(scene);await registerScene(scene);await startEngine(engine);Orientation, dimensions, and materials
createRectAreaLight(position, width, height, intensity = 1)takes an[x, y, z]position.widthandheightare nonnegative full dimensions in the light's local X/Y plane, not half-extents or world-space bounds. A zero width or height produces a dark, degenerate emitter.- An unrotated emitter illuminates its local −Z side. Its +Z back face does
not emit. Rotate
light.rotationQuaternionto aim it; editlight.position,light.scaling, orlight.parentjust like other scene nodes. Parent rotation and nonuniform scaling affect the rectangle. The light itself draws no panel: add an emissive mesh separately if the source should be visible. diffuse,specular,intensity,width, andheightare mutable state. PBR usesmetallicFactor,roughnessFactor, andbaseColorFactor; Standard usesdiffuseColor,specularColor, androughnessfor the LTC lookup. StandardspecularPowercontinues to control ordinary point/spot highlights; it does not replace area-light roughness.- Rectangular lights can coexist with point, directional, spot, and hemispheric lights; normal light-selection rules still apply.
Textured emission
Load a source image, process it, then assign the processed Texture2D to
light.emissionTexture. This changes the illumination of receiving materials;
putting a texture on an emissive panel alone does not create a light.
import { addToScene, createRectAreaLight, enableRectAreaLights, loadTexture2D, processAreaLightTexture, registerScene, startEngine } from "@babylonjs/lite";
// engine, scene, camera, and receiver meshes are set up as in the example above.const source = await loadTexture2D(engine, "https://raw.githubusercontent.com/SergioRZMasson/TemporaryAssetsRepo/refs/heads/main/Textures/Cyberpunk_AI_Square.png", { srgb: false,});const processed = await processAreaLightTexture(engine, source);const light = createRectAreaLight([0, 2, 3], 4, 2, 3);light.emissionTexture = processed;addToScene(scene, light);
await enableRectAreaLights(scene);await registerScene(scene);await startEngine(engine);processAreaLightTexture(engine, source): Promise<Texture2D> is the standalone
equivalent of Babylon.js AreaLightTextureTools.processAsync(source). It produces
a separate 1024×1024 RGBA8 texture with eleven progressively blurred mip levels
and trilinear clamp sampling. It does not change the source sampler or transfer
ownership of the source. Do not assign the original image directly: initialization
rejects unprocessed, invalid, released, or wrong-device emission textures.
Use { srgb: false } for the source to match Babylon.js processing in display-RGB
byte space; enabling hardware sRGB decoding changes the light's emission. Raster
loadTexture2D defaults already match Babylon.js's Y orientation, linear filtering,
and mipmap generation. Keep those defaults for the reference scenes rather than
adding another V flip. Processed output is an immutable emission snapshot: its UV
transform fields are not an emitter transform, and changing source pixels does
not update it automatically.
Ownership and changes after registration
- The source and processed result each have their own caller-owned texture
reference. Use the public
releaseTexture(texture)when that reference is no longer needed; never destroy underlying GPU resources yourself. - Processing keeps no persistent source reference. You can release the source after processing if nothing else uses it. If a visible panel uses the source, retain it through that material's registration instead.
light.emissionTextureis borrowed state, not an ownership transfer. Multiple lights and cloned lights may share one processed texture. SuccessfulenableRectAreaLights(scene)retains the selected processed textures for that scene's snapshot, so the caller can then release its result reference. Scene disposal releases scene-held references; disposal does not release references still owned by the application or another scene.- Live light position, rotation, scaling, dimensions, colors, and intensity
continue to update normally. Replacing or clearing
emissionTexture, or changing the light list/order, requires another preparation and a renderable rebuild. Pause a running engine before these changes so it cannot render an intermediate combination of light slots and texture bindings:
import { enableRectAreaLights, rebuildSceneRenderables, startEngine, stopEngine } from "@babylonjs/lite";
stopEngine(engine);light.emissionTexture = null; // Return this emitter to solid-color illumination.await enableRectAreaLights(scene);await rebuildSceneRenderables(scene);await startEngine(engine);To change the emitted image, process the updated source again, assign the new result, then await the same preparation/rebuild sequence. Do not mutate the processed texture's pixels or try to reuse a released handle. Balance each caller-owned reference once, including when replacing a map.
Initialization and network requirements
Call and await enableRectAreaLights(scene) before registerScene(scene).
This opt-in prepares the material support and uploads the exact Babylon.js
linearly transformed cosine (LTC) lookup tables; do not mark a scene ready until
initialization and its first rendered frame have completed.
The first initialization needs access to:
https://assets.babylonjs.com/core/areaLights/areaLightsLTC.binAllow that HTTPS origin and your emission-image origin in your application's network/CSP policy, with image CORS enabled for decoding. The lookup data is an external asset, not an emission texture. Loading, preprocessing, and initialization failures reject their promises; surface the error rather than rendering a partially ready scene. Await every emission-processing operation, LTC preparation, registration, and the first frame before declaring readiness.
The current milestone supports solid-color and processed textured emission. It does not support area-light shadows, emission-texture serialization, or importing serialized Babylon.js scenes. Use supported non-area shadow lights separately when needed. Layered PBR materials are not supported with rectangular area lights yet. Lit Node Material graphs are not supported with rectangular area lights yet; initialization rejects those combinations rather than silently misrendering.
Reference scenes
| Scene | Source / coverage |
|---|---|
| 316 PBR | Upstream areaLightsPBRMaterial, Playground #T7FXR8#12. |
| 317 Standard | Upstream areaLightsStandardMaterial, Playground #T7FXR8#67. |
| 318 Transforms and Materials | Scaled/rotated parent, light-local rotation, mixed lights, back-face rejection, and white/tinted dielectric/metal pairs. |
| 319 Textured Standard | Upstream Textured - Area Lights - Standard Material, Playground #T7FXR8#103. |
| 320 Textured PBR | Upstream Textured - Area Lights - PBR, Playground #T7FXR8#102. |
The metallic/albedo pairs cover the regression fixed by Babylon.js PR #16723.
Scenes 316/317 preserve the upstream camera pose, receiver geometry, material
values, colored emitter transforms, and panel parenting. Only diagnostic
AxesViewer arrows are omitted in both engines. Lite's free camera matches
the upstream FlyCamera's initial view; interactive navigation is not part of
the comparison. Standard ignores the original snippet's dynamic metallic
assignments, so those no-op assignments are not ported.
For scene 318, the Babylon.js reference uses a child transform for the emitter's
local yaw, because its light has no local quaternion property; Lite stores that
same rotation directly on the light's SceneNode.
The reference panel also inherits this child transform directly, rather than
the Babylon.js light's untranslated child frame; its world placement matches
Lite's panel parented to the light.
Scenes 319/320 preserve the upstream panel scale of two, light dimensions and
intensities, parent transforms, receiver geometry, camera poses, and original
image URLs. Their source snippets are the current Babylon.js visualization
entries whose reference images are textureAreaLightStandard.png and
texturedAreaLightsPBR.png. Scene 319 uses Cyberpunk_AI_Square.png; scene 320
uses separate White.png, Cyberpunk3_AI_Square.png, and Grey.png maps from
the upstream asset folder.
Only diagnostic axes are omitted symmetrically. The Standard snippet's dynamic
metallic and albedoColor assignments have no effect in Babylon.js, so both
engines preserve the default white diffuse color; its actual roughness values
remain 0.2 and 0.9.
The corresponding Babylon.js references live in lab/lite/src/bjs/scene316.ts
through scene320.ts. All five scenes are static and signal
canvas.dataset.ltcReady after initialization and canvas.dataset.ready after
the first frame. Textured scenes additionally signal canvas.dataset.emissionReady
after every emission map has been processed. Their parity tests read committed
immutable goldens; they never navigate to a Babylon.js reference or regenerate
images at test time.
In the Playground, choose Lights — Textured area light to run a compact root-API example with colored emitted illumination and an optional source-image panel.