feat: lumières spot (cône + angle) (Étape 13, Phase 4.2)

- Light étendu à 4×Vec4 (64 o) : dir_angle (axe du cône + cos demi-angle)
- FrameUniforms 576→704 o : compteur num_spot, _pad[1]
- Lights + spot: Vec<Light> ; into_frame_array renvoie (arr, n_dir, n_point, n_spot)
- Scene::add_spot_light(pos, dir, color, intensity, radius, half_angle) ; clear_lights inclut spot
- standard_shader.wgsl : 3e boucle d'accumulation (pénombre lissée ±0.1 rad + atténuation linéaire)
- exemple cube : lumière spot verte pointée vers le cube
- Docs : shaders/resources README (704 B), README roadmap (item 11), ROADMAP (item coché)
- Build/test/fmt OK (5 tests + validation WGSL + doctests) ; non-régression par défaut
This commit is contained in:
Jérôme Bousquié
2026-09-18 19:05:13 +02:00
parent d518948deb
commit e8ff364d0d
11 changed files with 211 additions and 70 deletions
+8 -6
View File
@@ -187,11 +187,12 @@ impl Renderer {
/// Rewrites the shared per-frame uniform buffer from the scene's active camera, its global
/// light list, its ambient color, and the current viewport aspect, then returns the frame bind
/// group wired to that buffer. Called at the start of every `render_scene` so the GPU sees the
/// latest camera matrices, camera position, and lighting (Étape 4.3, Étape 12).
/// latest camera matrices, camera position, and lighting (Étape 4.3, Étapes 12–13).
///
/// The light array is packed via `Lights::into_frame_array` (directionals first, then point
/// lights). Inputs: camera (the scene's active camera), lights (the scene's global light list),
/// ambient (the scene's ambient hemisphere color, rgb), aspect (viewport width / height).
/// The light array is packed via `Lights::into_frame_array` (directionals first, then point,
/// then spot lights). Inputs: camera (the scene's active camera), lights (the scene's global
/// light list), ambient (the scene's ambient hemisphere color, rgb), aspect (viewport width /
/// height).
fn write_frame_uniforms(
&self,
camera: &Camera,
@@ -199,7 +200,7 @@ impl Renderer {
ambient: [f32; 3],
aspect: f32,
) {
let (light_array, num_directional, num_point) = lights.into_frame_array();
let (light_array, num_directional, num_point, num_spot) = lights.into_frame_array();
let frame = FrameUniforms {
view: camera.view_matrix(),
proj: camera.projection_matrix(aspect),
@@ -208,7 +209,8 @@ impl Renderer {
lights: light_array,
num_directional,
num_point,
_pad: [0, 0],
num_spot,
_pad: [0],
options: [if self.unlit { 1 } else { 0 }, 0, 0, 0],
};
self.queue