Files
Jérôme Bousquié 5ae978da23 doc
2026-09-21 10:07:32 +02:00

3.3 KiB

Lights

Lights are scene-global: a single list is packed into the frame uniforms every frame, and all entities receive their lighting (per-material lights are out of the current scope).

Model

  • Bounded capacity: MAX_LIGHTS = 8 lights in total (directional + point + spot combined). Adding beyond that returns an error.
  • Default: one white directional light along +Z (from the surface point toward the light) + white ambient. This default exactly reproduces the historical single-light rendering — your scene "just works" with no configuration.
  • Ambient (set_ambient) is a global hemispherical term, independent of the lights.

Adding lights

use glam::Vec3;

// Directional: `dir` points FROM the surface point TOWARD the light.
app.scene
    .add_directional_light(Vec3::new(1.0, 1.2, 1.0).normalize(), [1.0, 0.98, 0.92], 1.5)
    .unwrap();

// Point: world position, tint, intensity, attenuation radius (linear down to 0).
app.scene
    .add_point_light(Vec3::new(0.5, 1.6, 1.8), [1.0, 0.7, 0.3], 1.2, 6.0)
    .unwrap();

// Spot: position, cone axis (FROM the light TOWARD the scene), tint, intensity, radius,
// half-angle in radians (penumbra smoothed at the edge).
app.scene.add_spot_light(
    Vec3::new(-2.5, 2.2, 1.0),          // position
    Vec3::new(2.5, -2.2, -1.0).normalize(), // axis, toward the scene
    [0.3, 1.0, 0.5],                    // green tint
    1.4, 8.0, 0.45,                     // intensity, radius, half-angle (~26°)
).unwrap();

These three calls are the ones in the demo example; cube.rs shows a point + a spot on top of the default directional, and spot_test.rs isolates a single spot (ambient nearly zero).

Global settings:

Method Effect
set_ambient([r, g, b]) hemispherical ambient color (default white)
clear_lights() empties the list — only ambient will light the scene (useful for a flat look without switching to unlit)
set_lights(Lights) replaces the whole list (batch reset)
lights() reads the current list

⚠️ Packed indices (important for shadows)

Lights are stacked in the GPU array by type, in order:

index 0 .. n_dir-1       : directional
index n_dir .. +n_point-1 : point
index … .. +n_spot-1      : spot

Two consequences:

  1. Index 0 is the default +Z directional (the one Lights::new() pre-loads), not your first added light. This is a classic pitfall — see Shadows.
  2. If you want your light to be the only one (and thus at index 0), clear the list first: app.scene.clear_lights(); then add_*_light(…) (this is the technique in shadow_test.rs).

Intensities and tints

  • color is an RGB in [0..1]; intensity is an unbounded multiplier.
  • Local lights (point/spot) attenuate linearly — intensity drops to zero at radius. Beyond the radius, the light contributes nothing.
  • The standard shader accumulates ambient + all lights (no mutual occlusion between lights; the spot cone culling happens at the fragment).