feat(renderer): active camera wired to frame uniforms (Étape 4.3)

- Camera enrichie: fov/near/far stockés, Default (pos (0,0,3), 45°, near 0.1,
  far 100), with_perspective(), projection_matrix(aspect) depuis les params
  stockés (au lieu de les passer en argument).
- Scene porte une caméra active: set_camera()/camera() (défaut Camera::default).
- Renderer::render_scene(view, scene, aspect) écrit chaque frame view/proj/
  cam_pos réels dans le buffer frame (write_frame_uniforms) avant de dessiner;
  le Renderer garde le handle du frame_buffer. Le chemin bas-niveau render()
  conserve les valeurs par défaut (identité).
- App::render_scene calcule l'aspect depuis window.inner_size() (le Renderer
  reste indépendant de la fenêtre).

Docs synchronisées: DRAFT (4.3 coche), README (statut 3D-infra + quick ref),
PLAN (caméras), ROADMAP (1.1/1.3/1.5/2.3).

Validation: check workspace+examples 0 warning, test (Pod + wgsl) OK, doc 0
warning, fmt propre. Le rendu 3D visible attend Étape 5 (brancher standard).
This commit is contained in:
Jérôme Bousquié
2026-09-16 17:25:50 +02:00
parent c1e07b42b4
commit f10e249898
8 changed files with 159 additions and 52 deletions
+8 -4
View File
@@ -93,14 +93,18 @@ toucher au rendu (pure façade de données, validable par compilation).
- [X] 4.2 **Buffers object par entité** : le `Renderer` maintient un cache
`RefCell<HashMap<String,(wgpu::Buffer, wgpu::BindGroup)>>` clefé par label d'entité ; chaque frame il
écrit `ObjectUniform.world = entity.transform.to_matrix()` (via `object_bind_group_for`). *(fait — 2026-09-16)*
- [ ] 4.3 **Caméra active** : ajouter `scene.set_active_camera(Camera)` / `scene.active_camera() -> Option<&Camera>` ;
écrire view/proj (avec aspect de la fenêtre) dans le buffer frame chaque frame. *(non fait — laisse le
`FrameUniforms::default()` : simple/manual tournent toujours via `basic` qui ignore ces uniforms)*
- [X] 4.3 **Caméra active** : `Scene` porte une caméra active (`Camera::default()` : position (0,0,3),
fov 45°, near 0.1, far 100) via `set_camera()` / `camera()` ; `Camera` enrichie (fov/near/far +
`with_perspective` / `projection_matrix(aspect)`). Chaque frame, `Renderer::render_scene` écrit
view/proj/cam_pos réels dans le buffer frame via `write_frame_uniforms` ; l'aspect est calculé par
`App::render_scene` depuis `window.inner_size()` (le Renderer reste indépendant de la fenêtre).
*(fait — 2026-09-16)*
- [X] 4.4 **`draw_entity` étendu** : pose `set_bind_group(0, frame_bg)` + `set_bind_group(1, object_bg)` avant le
draw (groupes requis par le layout unique) ; le chemin bas-niveau `Renderer::render` pose aussi les 2 bind
groups (frame partagé + object identité partagé). *(fait — 2026-09-16)*
- [ ] **Validation** : `cargo check` 0 warning ; exécution `simple` (sans panique, boucle active). *(une partie :
`simple` reste exécutable car `basic` ignore les uniforms ; le rendu 3D réel attend 4.3)*
`simple` reste exécutable car `basic` ignore les uniforms ; le rendu 3D réel attend l'Étape 5 où `standard` est
branché sur un exemple)*
## Étape 5 — Exemple 3D (cube éclairé)
+7 -1
View File
@@ -16,6 +16,10 @@ Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle
> **« Scene auto-render »** a été réalisée : le rendu de la `Scene` est **automatisé** en une seule
> passe groupée via `App::render_scene(frame.view())` (appelée par défaut dans `AppHandler::render`),
> et `simple.rs` (API `AppBuilder`, sans `winit`/`wgpu`) déclare un quad rendu automatiquement.
> Le même jour (Étape 3 + 4, 2026-09-16) l'**infrastructure 3D** est en place : bind groups uniformes
> partagés (frame + object), caméra active dans la `Scene` (`Scene::set_camera`/`camera()`) écrite dans
> le buffer frame chaque frame, matrices monde par entité. L'éclairage visible (`standard_shader.wgsl`
> branché sur un exemple) reste une étape suivante.
## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue)
@@ -74,7 +78,9 @@ Une fois les phases 1 à 3 validées, nous pourrons introduire :
- [ ] **Système de Lumières** : Ajout de buffers d'uniformes dans le PipelineCache.
- [ ] **Textures** : Intégration d'un module de chargement d'images et de BindGroups.
- [ ] **Caméras** : Gestion des matrices de projection/vue dans la Scene.
- [X] **Caméras** : Gestion des matrices de projection/vue dans la Scene *(fait 2026-09-16, Étape 4.3 —
`Scene::set_camera`/`camera()` porte une caméra active ; `render_scene` écrit view/proj/cam_pos réels
dans le buffer frame chaque frame, aspect calculé depuis la fenêtre)*.
## Check-list de Vérification pour le LLM d'Assistance
+28 -24
View File
@@ -20,16 +20,19 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
> est exposée (`Frame::view()`), `render()` dessine la scène en une passe groupée
> (`App::render_scene`) et la présentation est automatique dans `App::run` (exemple `simple`).
> - `Scene` avec identifiants **String** (décision prise — voir tableau Notes de Décision) : 🚧 enregistrement seul.
> - `Camera` / `Transform` et `glam` : types et mathématiques présents (`math/`, `resources/camera.rs`), non branchés au pipeline.
> - `Camera` / `Transform` et `glam` : types et mathématiques présents (`math/`, `resources/camera.rs`),
> initialement non branchés au pipeline — **désormais branchés** (caméra active + matrices monde écrites
> chaque frame, Étape 4.3, 2026-09-16 ; voir §1.1/1.5 ci-dessous).
> **Étape suivante (prochaine itération) — « 3D + éclairage Phong » (ROADMAP 1.3 + 1.5).**
> Le rendu automatique est aujourd'hui **plat** : le `basic_shader.wgsl` interprète les positions comme
> déjà en NDC, sans matrice monde/vue/projection ni lumière. L'étape suivante rend la scène réellement
> 3D et éclairée : créer `standard_shader.wgsl` (Phong : matrice `projection * view * world` + lumière
> directionnelle), ajouter les uniform buffers (frame : view/proj/light ; par mesh : world matrix dérivée
> du `Transform`) et les brancher dans `Renderer::render_scene` et `Material`, puis exposer `Camera`/
> `Transform` à la `Scene` (caméra active) et ajouter un mesh de test (cube) à l'exemple. Objectif MVP :
> **un mesh 3D éclairé à l'écran**.
> déjà en NDC, sans matrice monde/vue/projection ni lumière. **Une grande partie de l'infrastructure est
> déjà en place (Étapes 3+4, 2026-09-16)** : le `standard_shader.wgsl` Phong (matrice
> `projection * view * world` + lumière directionnelle) existe et valide ; les uniform buffers sont
> branchés (frame : view/proj/cam_pos + lumière ; par mesh : `world` dérivé du `Transform`) ; le `Renderer`
> écrit chaque frame la caméra active (via `Scene::set_camera`/`camera()`) et la matrice monde de chaque
> entité. **Reste à faire** pour un mesh 3D éclairé à l'écran : brancher `standard` sur un exemple et
> ajouter un mesh de test (cube). Objectif MVP : **un mesh 3D éclairé à l'écran**.
---
@@ -40,11 +43,11 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
### 1.1 Dépendances & Mathématiques
- [x] `glam = "0.33"` ajouté (`lib/Cargo.toml`) — déjà présent, utilisé par `math/transform.rs` et `resources/camera.rs`
- [x] `slotmap` **retiré** — décision prise : **String IDs pour le MVP** ; slotmap reporté à l'étape "handles typés" (voir Notes de Décision)
- [ ] Créer module `math/` (ou `transform.rs`) :
- [ ] Struct `Transform { translation: Vec3, rotation: Quat, scale: Vec3 }`
- [ ] Méthode `to_matrix() -> Mat4` pour calculer la matrice locale
- [ ] Struct `Camera { position: Vec3, target: Vec3, up: Vec3 }` : resources/camera.rs
- [ ] Fonctions `view_matrix()` et `projection_matrix(fov, aspect, near, far)`
- [x] Module `math/` / `transform.rs`:
- [x] Struct `Transform { translation: Vec3, rotation: Quat, scale: Vec3 }`
- [x] Méthode `to_matrix() -> Mat4` pour calculer la matrice locale
- [x] Struct `Camera { position: Vec3, target: Vec3, up: Vec3 }` : resources/camera.rs — enrichi en Étape 4.3 (fov/near/far + `with_perspective`)
- [x] Fonctions `view_matrix()` et `projection_matrix(fov, aspect, near, far)` (Étape 4.3 : `projection_matrix(aspect)` utilise fov/near/far stockés)
### 1.2 Geometry & Mesh
- [ ] Créer struct `Geometry` (math/geometry.rs) :
@@ -59,22 +62,23 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
- [ ] Ajouter un mesh de test (cube unitaire) en exemple
### 1.3 Shader Phong Minimal
- [ ] Créer `standard_shader.wgsl` :
- [ ] Vertex shader : projection * view * world * position
- [ ] Fragment shader : éclairage hémisphérique + diffuse avec une lumière directionnelle
- [ ] Uniforms : `view_matrix`, `proj_matrix`, `world_matrix`, `light_dir`, `light_color`
- [ ] Mettre à jour `Material` pour supporter les uniforms du shader Phong
- [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) :
- [x] Vertex shader : projection * view * world * position
- [x] Fragment shader : éclairage directionnel (+ hémisphérique)
- [x] Uniforms : `view`, `proj`, `cam_pos`, `light_dir`, `light_color`, `options`
- [x] Mettre à jour `Material` / pipeline pour supporter les uniforms du shader Phong (bind group layouts frame+object, Étape 3) — `standard` n'est pas encore branché sur un exemple
### 1.4 Scene avec identifiants (MVP : String IDs)
- [x] `Scene` implémentée avec **String IDs** (`HashMap<String, Arc<Mesh>>`, `...Material`, entités) — état actuel validé ; décision : rester en String IDs pour le MVP
- [x] Méthodes : `add_mesh()`, `get_mesh()`, `add_material()`, `add_entity()`, `iter_entities()`, `remove_entity()`
- [x] Caméra active dans la `Scene` : `set_camera()` / `camera()` (Étape 4.3)
- [ ] **Reporté (étape "Handles typés")** : migrer vers `slotmap` générationnel (`MeshId`/`MaterialId`) quand l'éviction/les performances le justifieront
### 1.5 Rendu du Prototype
- [ ] Uniform buffer pour la frame : `view_matrix`, `proj_matrix`, `light_dir`
- [ ] Uniform buffer par mesh : `world_matrix` (calculée sur CPU pour le MVP)
- [ ] `Renderer::render()` itère sur les meshes de la Scene et dessine chacun
- [ ] Exemple fonctionnel : un cube éclairé tourne à l'écran
- [x] Uniform buffer pour la frame : `view`, `proj`, `cam_pos`, `light_dir` (Étapes 3+4) — écrit chaque frame depuis la caméra active
- [x] Uniform buffer par mesh : `world` (calculée sur CPU depuis `transform.to_matrix()`, Étape 4.2)
- [x] `Renderer::render_scene()` itère sur les entités de la Scene et dessine chacune (liaison bind groups frame+object)
- [ ] Exemple fonctionnel : un cube éclairé tourne à l'écran — **à faire** (Étape 5 : brancher `standard` sur un exemple + mesh cube)
---
@@ -94,9 +98,9 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
- [ ] `Scene::iter_entities()` → pour le render loop
### 2.3 Camera dans la Scene
- [ ] Intégrer `Camera` comme ressource de la Scene
- [ ] Permettre plusieurs caméras (actuelle/inactive)
- [ ] Exposer API : `scene.set_active_camera(camera_id)`
- [x] Intégrer `Camera` comme ressource de la Scene (Étape 4.3 : `Scene::set_camera` / `camera()`, caméra active unique)
- [ ] Permettre plusieurs caméras (actuelle/inactive) et une sélection par identifiant (`scene.set_active_camera(camera_id)`)
- [ ] Exposer une caméra orbitale contrôlable (exemple final, Phase 5)
---