Target: 19.10
Context — what already shipped (19.8)
The first tier of 3D mesh lighting landed with the glTF loader arc (#1502 / #1504 / #1506):
Light3d world renderable (src/lighting/light3d.ts) — "directional" and "ambient" types, runtime-mutable (day/night cycles, fading), stage-registered like Light2d.
- Half-Lambert diffuse + ambient floor in
mesh-lit.frag, up to MAX_LIGHTS = 8 directional lights, composed with alpha-cutoff and emissive.
NORMAL parsing (+ flat-normal synthesis when absent) and world-space aNormal through LitMeshBatcher; zero-alloc uniform packing (src/video/webgl/lighting/pack3d.ts).
KHR_lights_punctual parsing for all three punctual types (directional / point / spot) with world-space direction (node −Z), position and range resolved into GLTFData.lights.
GLTFScene.addTo({ lights }) auto-instantiates directional suns + a soft ambient fill.
Point and spot lights are currently parsed but deliberately not shaded:
pack3d.ts — "only directional lights are shaded in this release" (skipped)
GLTFScene.js — "point / spot lights are parsed but not yet shaded" (skipped)
light3d.ts — "point" typed but documented as reserved
Scope — the delta
Shade positional lights on the lit-mesh path:
- Point lights — inverse-square falloff with the optional
range cutoff, per the glTF punctual-lights spec.
- Spot lights — point falloff × smooth cone attenuation (
innerConeAngle → outerConeAngle).
Light3d — un-reserve "point", add "spot"; add range and cone-angle fields (position already exists).
- glTF parser — capture
def.spot.innerConeAngle / outerConeAngle (currently dropped).
GLTFScene.addTo — instantiate point/spot Light3ds instead of skipping them.
pack3d.ts + mesh-lit.frag — extend packing/uniforms with positions, ranges and cone params (either type-tagged arrays or separate per-type arrays, whichever keeps the WebGL1 uniform budget comfortable at MAX_LIGHTS = 8).
Optional stretch (same layer in the original plan)
- Blinn-Phong specular — needs the camera world position as a uniform; worth doing while
vWorldPos is being added anyway. Fine to split out if it bloats the PR.
Implementation notes
- Positional lights need the fragment's world-space position. The lit mesh path currently pushes view-transformed vertices and only the normal stays in world space (
mesh-lit.vert carries vNormal alone). Either push a world-space position as an extra attribute, or evaluate positional lighting per-vertex — the extra varying is the likely answer for quality at low poly counts.
- Blender exports point/spot intensity in candela (directional is lux) — same 1:1-is-wrong story as the directional case;
GLTFScene normalizes to intensity: 1 and lets the app tune, keep that convention.
- The night-city and diorama examples are natural test beds (street lamps / cone spots showcase this well).
Non-goals (later layers, per the original plan)
- Normal-mapped meshes (
TANGENT parsing + TBN)
- Full PBR / IBL, shadows
Target: 19.10
Context — what already shipped (19.8)
The first tier of 3D mesh lighting landed with the glTF loader arc (#1502 / #1504 / #1506):
Light3dworld renderable (src/lighting/light3d.ts) —"directional"and"ambient"types, runtime-mutable (day/night cycles, fading), stage-registered likeLight2d.mesh-lit.frag, up toMAX_LIGHTS = 8directional lights, composed with alpha-cutoff and emissive.NORMALparsing (+ flat-normal synthesis when absent) and world-spaceaNormalthroughLitMeshBatcher; zero-alloc uniform packing (src/video/webgl/lighting/pack3d.ts).KHR_lights_punctualparsing for all three punctual types (directional/point/spot) with world-space direction (node −Z), position andrangeresolved intoGLTFData.lights.GLTFScene.addTo({ lights })auto-instantiates directional suns + a soft ambient fill.Point and spot lights are currently parsed but deliberately not shaded:
pack3d.ts— "only directional lights are shaded in this release" (skipped)GLTFScene.js— "point / spot lights are parsed but not yet shaded" (skipped)light3d.ts—"point"typed but documented as reservedScope — the delta
Shade positional lights on the lit-mesh path:
rangecutoff, per the glTF punctual-lights spec.innerConeAngle→outerConeAngle).Light3d— un-reserve"point", add"spot"; addrangeand cone-angle fields (positionalready exists).def.spot.innerConeAngle/outerConeAngle(currently dropped).GLTFScene.addTo— instantiate point/spotLight3ds instead of skipping them.pack3d.ts+mesh-lit.frag— extend packing/uniforms with positions, ranges and cone params (either type-tagged arrays or separate per-type arrays, whichever keeps the WebGL1 uniform budget comfortable atMAX_LIGHTS = 8).Optional stretch (same layer in the original plan)
vWorldPosis being added anyway. Fine to split out if it bloats the PR.Implementation notes
mesh-lit.vertcarriesvNormalalone). Either push a world-space position as an extra attribute, or evaluate positional lighting per-vertex — the extra varying is the likely answer for quality at low poly counts.GLTFScenenormalizes tointensity: 1and lets the app tune, keep that convention.Non-goals (later layers, per the original plan)
TANGENTparsing + TBN)