Skip to content

3D lighting: point & spot light shading for lit meshes (Light3d layer B) #1536

Description

@obiot

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:

  1. Point lights — inverse-square falloff with the optional range cutoff, per the glTF punctual-lights spec.
  2. Spot lights — point falloff × smooth cone attenuation (innerConeAngleouterConeAngle).
  3. Light3d — un-reserve "point", add "spot"; add range and cone-angle fields (position already exists).
  4. glTF parser — capture def.spot.innerConeAngle / outerConeAngle (currently dropped).
  5. GLTFScene.addTo — instantiate point/spot Light3ds instead of skipping them.
  6. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions