# 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 ```rust 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`](../../lib/examples/demo.rs) example; [`cube.rs`](../../lib/examples/cube.rs) shows a point + a spot on top of the default directional, and [`spot_test.rs`](../../lib/examples/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](shadows.md). 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`](../../lib/examples/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). ## Links - [User README](README.md) · [Shadows](shadows.md) · [Materials & textures](materials.md) - [Root README](../../README.md) · [ARCHI_RENDU](../tech/ARCHI_RENDU.md)