3.3 KiB
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 = 8lights 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:
- 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. - If you want your light to be the only one (and thus at index 0), clear the list
first:
app.scene.clear_lights();thenadd_*_light(…)(this is the technique inshadow_test.rs).
Intensities and tints
coloris an RGB in[0..1];intensityis an unbounded multiplier.- Local lights (point/spot) attenuate linearly — intensity drops to zero at
radius. Beyond the radius, the light contributes nothing. - The
standardshader accumulates ambient + all lights (no mutual occlusion between lights; the spot cone culling happens at the fragment).