docs(draft): épurate Étape 9, plan Étape 10 (Textures, Phase 4.1)

- DRAFT.md vidé Après l'Étape 9 (bilan conservé dans git) et réinitialisé
  pour l'Étape 10 : plan Textures avec décisions D1-D4 à valider.
- ROADMAP : coche 'uvs dans Geometry' (déjà implémenté dans le code),
  prérequis de l'Étape 10.
This commit is contained in:
Jérôme Bousquié
2026-09-18 13:21:26 +02:00
parent dfa7403260
commit 7a5d627221
2 changed files with 83 additions and 98 deletions
+82 -97
View File
@@ -1,119 +1,104 @@
# DRAFT — Étape 9 : Depth Buffer # DRAFT — Étape 10 : Textures (Phase 4.1)
> 📅 **Rédigé le 2026-09-18.** Fonctionnel validé, plan à valider avant implémentation. > 📅 **Rédigé le 2026-09-18.** Plan à valider avant implémentation.
> Source de vérité = code + README.md. Ce document est vidé à la complétion de l'étape. > Source de vérité = code + README.md. Ce document est vidé à la complétion de l'étape.
## Contexte (constat d'observation) ## Contexte (état de départ)
L'exemple `cube` en rotation montre que **la profondeur n'est pas calculée** : les faces La Phase 4.1 (Textures) du ROADMAP vise à texturer le rendu. État actuel du code :
les plus lointaines ne sont pas masquées par les faces plus proches. Causes identifiées dans - **La tuyauterie UV existe déjà** : `Geometry.uvs: Option<Vec<[f32; 2]>>` (builder `with_uvs`)
le code (observation du 2026-09-18) : et `Vertex.uv` (location 2, offset 24, stride 56) sont déclarés dans `build_pipeline` et lus
- `lib/src/pipeline/pipeline_cache.rs` → `depth_stencil: None` à la création du pipeline (L233). par le shader `VertexInput`. Seul l'**échantillonnage manque**.
- `lib/src/core/renderer.rs` → les deux render passes (`render`, `render_scene`) ne posent - Le shader `standard_shader.wgsl` reçoit l'UV mais ne le transmet pas au fragment et n'échantillonne rien.
**aucun** `depth_stencil_attachment` ; seul un `color_attachments` est fourni (L194, L239). - **Aucun type `Texture`**, aucun sampler, aucun bind group de texture. `Material` ne porte pas de texture.
- Aucune texture de profondeur n'est créée, donc aucun z-test n'est exécuté. - Architecture bind group : « un seul layout pour tous » (Étape 3) — les groupes `frame @0`
+ `object @1` sont posés sur **toutes** les pipelines et bindés à chaque draw (`draw_entity`).
**Conclusion** : non implémenté (pas un bug). C'est un prérequis de correction du rendu avant **Conclusion** : l'UV est prête ; les pièces manquantes sont un type `Texture`, un bind group de
les textures (Phase 4.1), tel que décidé avec l'utilisateur le 2026-09-18. texture (groupe 2) partagé, un échantillonnage dans le shader et le portage sur `Material`.
## Objectif ## Objectif
Activer le test de profondeur (z-buffer) sur tous les chemins de rendu (`render` ET `render_scene`) Permettre de texturer un mesh : charger une image → `wgpu::Texture` (+ view + sampler), lier une
pour que les faces cachées soient correctement masquées. texture diffuse à un `Material`, échantillonner dans le shader, **sans casser** le pattern « un seul
layout pour tous ».
## Décisions (validées le 2026-09-18) ## Décisions (à valider)
- **[x] D1 (format)** — *(validée 2026-09-18)* : texture de profondeur `TextureFormat::Depth32Float`, - **[ ] D1 (intégration bind group — « un seul layout pour tous »)** : on ajoute un **3ᵉ bind group**
valeur de clear `1.0` (portée maximale), `depth_compare: Less`, `depth_write_enabled: true`. `@group(2)` (sampler + texture diffuse) posé sur **toutes** les pipelines, avec une **texture
- **[x] D2 (portée)** — *(validée 2026-09-18)* : le depth buffer est posé sur **les deux** render blanche 1×1 de secours (placeholder)** utilisée quand un `Material` n'a pas de texture. Cela
passes (bas niveau `render` et haut niveau `render_scene`), pas seulement `render_scene`, pour préserve l'unicité du layout (aucune pipeline multiple), donc un seul flux de rendu. *(alternative
garder la cohérence du flux 2D. écartée : bind group optionnel → casse l'unicité du layout, refactor de toutes les pipelines)*.
- **[x] D3 (résolution)** — *(validée 2026-09-18)* : la texture de profondeur est allouée à la taille - **[ ] D2 (échantillonnage inconditionnel)** : le fragment shader échantillonne **toujours** la
de la surface initiale. **Le resize est planifié comme chantier dédié** dans `docs/ROADMAP.md` texture diffuse ; le placeholder blanc (texel = 1) reproduit exactement le comportement actuel
(Phase 4.4, avec recréation de la depth texture en même temps que la reconfiguration de la surface), d'un material sans texture. Donc pas de flag conditionnel → un seul flow de shader. En mode lit,
hors périmètre de l'Étape 9. On créera un helper `create_depth_texture` isolé pour rendre ce le **texel remplace la couleur du vertex** (`texel.rgb * (ambient + diffuse)`) ; en unlit, le texel
recreate trivial plus tard. tel quel. Symétrique du pattern unlit existant.
- **[x] D4 (échantillonnage)** — *(validée 2026-09-18, YAGNI)* : aucune texture de profondeur n'est - **[ ] D3 (chargement d'image & format)** : ajouter la dépendance `image` (décodage PNG/JPEG) à
échantillonnée pour l'instant (pas de shadow mapping ni de post-processing) — on n'a donc pas `lib/Cargo.toml` ; upload en `TextureFormat::Rgba8UnormSrgb`, usage `TEXTURE_BINDING | COPY_DST`,
besoin de l'exposer dans un bind group. Simple render target + state pipeline suffisent ; le bind dimension `D2`, `mip_level_count: 1` (**YAGNI** : pas de génération de mipmaps cette étape),
group sera ajouté quand le shadow mapping arrivera. sampler `filter: Linear`, `address_mode: Repeat`.
- **[ ] D4 (API et portage)** : nouveau type `Texture` (device + view + sampler). `Material` gagne
`texture: Option<Arc<Texture>>` et détient son **bind group de texture (groupe 2)**, construit
depuis le layout partagé ; sans texture il lie le placeholder. API `Scene` : `add_texture(id, tex)`
et liaison d'une texture à un material. `draw_entity` bind `set_bind_group(2, ...)`.
## Plan d'implémentation ## Plan d'implémentation
### 9.1 — Créer la texture de profondeur dans `Renderer` *(terminée et vérifiée le 2026-09-18)* ### 10.1 — Nouveau type `Texture` + dépendance `image`
- [x] Ajouter un champ `depth_view: wgpu::TextureView` au `Renderer` (avec helper pour allouer - [ ] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
`depth_texture` + `depth_view` à partir de `device` et de la taille surface). - [ ] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`.
- [x] Taille : `width`/`height` passés à `Renderer::new` (aujourd'hui il ne reçoit que `format`). - [ ] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un
Portée : avantage à `create_depth_texture(device, width, height, format)`. buffer RGBA et upload via `Queue::write_texture` (D3).
- [x] `TextureDescriptor` : `size: Extent3d { width, height, depth_or_array_layers: 1 }`, - [ ] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
`mip_level_count: 1`, `sample_count: 1`, `dimension: D2`, - [ ] Enregistrer `pub mod texture` dans `resources/mod.rs`.
`format: wgpu::TextureFormat::Depth32Float`,
`usage: RENDER_ATTACHMENT`, `label: Some("depth texture")`.
### 9.2 — Attacher le depth au render pass *(terminée et vérifiée le 2026-09-18)* ### 10.2 — Bind group layout texture (groupe 2) partagé
- [x] Dans `render_scene` (renderer.rs) : ajouter `depth_stencil_attachment: Some(...)` au - [ ] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture`
`RenderPassDescriptor`, avec `depth_ops: Operations { load: Clear(1.0), store: Store }`. = `BindingType::Sampler(Filtering)` (binding 0) + `Texture { sample_type: Float, view_dimension: D2 }`
- [x] Même ajout dans `render` (renderer.rs) pour la cohérence du chemin bas niveau. (binding 1), visibilité fragment.
- [ ] `build_pipeline` : ajouter le 3ᵉ layout au `PipelineLayoutDescriptor` (l'unicité du layout est
conservée — D1).
- [ ] Mettre à jour les destructures `let [frame_layout, object_layout]` (renderer.rs) pour 3 éléments.
### 9.3 — Activer le depth-stencil state dans le pipeline *(terminée et vérifiée le 2026-09-18)* ### 10.3 — Shader standard : UV → fragment + échantillonnage
- [x] Dans `pipeline_cache.rs` (L233) : remplacer `depth_stencil: None` par - [ ] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
`Some(DepthStencilState { format: Depth32Float, depth_write_enabled: Some(true), - [ ] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et
depth_compare: Some(CompareFunction::Less), stencil: StencilState::default(), `@group(2) @binding(1) var diffuse_texture: texture_2d<f32>;`.
bias: DepthBiasState::default() })` — note : en wgpu 30, `depth_write_enabled` et - [ ] `fs_main` : `let texel = textureSample(diffuse_texture, texture_sampler, in.uv);` — en lit →
`depth_compare` sont des `Option` (le DRAFT initial les donnait nus). `texel.rgb * (ambient + diffuse)`, en unlit → `texel` (D2). Mettre à jour la doc du module shader.
- [x] Le format du depth doit matcher celui de la texture (D1 : `Depth32Float`) — garanti par la
constante partagée `DEPTH_FORMAT` (pipeline_cache.rs), réexportée via `pipeline::mod`.
### 9.4 — Vérification *(terminée et vérifiée le 2026-09-18)* ### 10.4 — `Material` porte la texture diffuse
- [x] `cargo fmt --all` (aucun diff attendu), `cargo check --workspace` (zéro warning), - [ ] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
`cargo test --workspace` (vert : 3 tests), `cargo doc` (pas de `missing_docs`). bind group (groupe 2) depuis le layout partagé, avec placeholder si `None` (D1/D4).
- [x] Lancer l'exemple `cube` (`cargo run -p wsg-lib --example cube`) : tourne sans erreur backend - [ ] Méthode `set_texture(...)` qui recrée le bind group si la texture change.
(le z-buffer est accepté par wgpu, aucun format mismatch) — faces arrière masquées. - [ ] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout
- [x] Lancer `simple` (rendu plat via `render_scene`) : pas de régression, tourne sans erreur GPU. Material sans texture lie le blanc.
### 10.5 — API Scene & binding dans `draw_entity`
- [ ] Rendre accessible un layout du groupe 2 aux Materials (via `SceneGpu` / cache) pour construire
leurs bind groups.
- [ ] Route déclarative `Scene` : `add_texture(id, tex)` et liaison d'une texture par id à un material
(ex. `add_material_texture` ou paramètre de `add_material_shader`).
- [ ] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`.
- [ ] Vérifier qu'un material *sans* texture continue de fonctionner (placeholder → aucune régression).
### 10.6 — Exemples + validation
- [ ] `cube` : texturer le cube (image PNG embarquée via `include_bytes!` pour rester autonome,
ou un motif procédural RGBA généré en mémoire).
- [ ] `simple`/`manual` : pas de régression (placeholder).
- [ ] `cargo fmt --all`, `cargo check --workspace` (0 warning), `cargo test --workspace` (vert, tests de
layout uniform + validation WGSL à jour), `cargo doc` (pas de `missing_docs`).
- [ ] Exécuter `cube` (faces texturées) et `simple` sans erreur backend.
## Point d'étape ## Point d'étape
- [x] Valider D1–D4 avant implémentation. *(fait — 2026-09-18 : D1 Depth32Float, D2 les deux passes, - [ ] Valider D1–D4 avant implémentation.
D3 resize planifié dans RoADMAP Phase 4.4, D4 YAGNI)* - [ ] Caser 10.1–10.6, validation verte, exemples OK.
- [x] Caser 9.1–9.4, validation verte, exemples OK. *(fait — 2026-09-18)* - [ ] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT.
- [x] Deux commits séparés : `refactor(...)` (9.1–9.3) puis `docs(...)` (9.4) + README/DRAFT. - [ ] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé).
*(fait — refactor fca5d72 puis docs)*
- [x] Rédiger le bilan et ouvrir la suite (textures/matériaux Phase 4.1).
--- ---
## Bilan de l'Étape 9 — Depth Buffer (terminée le 2026-09-18) _Fin du DRAFT Étape 10 — à valider avant implémentation._
La profondeur est maintenant calculée sur **tous** les chemins de rendu :
- **1 texture de profondeur partagée** (`Depth32Float`, D1), allouée par le `Renderer` à la taille
de la surface initiale, gardée vivante par le champ `_depth_texture` (rétention) et vue via
`depth_view`.
- **Les deux render passes** (`render` bas niveau et `render_scene` haut niveau) posent un
`depth_stencil_attachment` identique (clear `1.0` / store), ce qui masque les faces arrière du
cube en rotation (D2).
- **Toutes les pipelines** déclarent un `DepthStencilState` cohérent (write `true`, compare `Less`)
via la constante partagée `DEPTH_FORMAT` — impossible d'avoir un format pipeline ≠ texture.
### Décisions appliquées
- **D1** : `Depth32Float`, clear `1.0`, compare `Less`, write `true`. ✓
- **D2** : profondeur posée sur `render` ET `render_scene`. ✓
- **D3** : texture à la taille initiale ; le resize (recréation de la depth texture à chaque
changement de taille, couplée à la reconfiguration surface) est planifié en **ROADMAP Phase 4.4**.
Le helper `create_depth_texture` isolé rend ce recreate trivial. ✓
- **D4** (YAGNI) : pas de bind group présentant la profondeur ; simple render target + state
pipeline. Le bind group attendra le shadow mapping. ✓
### API modifiée (breaking)
- `Renderer::new(context, format)` → `Renderer::new(context, format, width, height)` —
les appelants (`app.rs`, `manual.rs`, `README.md`) ont été mis à jour en conséquence.
### Commits de l'étape
- `fca5d72` refactor(renderer): activate depth buffer on all render paths (9.1–9.3)
- *(docs — 9.4 : DRAFT coché + bilan, README mis à jour)*
### Suite logique
Ouvrir la Phase 4.1 (Textures / matériaux) — le z-buffer était le prérequis de lisibilité : les
faces arrière étant désormais masquées, un cube texturé sera correctement rendu.
_Fin de l'Étape 9._
+1 -1
View File
@@ -147,7 +147,7 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
### 4.1 Textures ### 4.1 Textures
- [ ] Struct `Texture` avec chargement d'image - [ ] Struct `Texture` avec chargement d'image
- [ ] Ajouter `uvs: Option<Vec<[f32; 2]>>` dans `Geometry` - [x] Ajouter `uvs: Option<Vec<[f32; 2]>>` dans `Geometry` *(déjà en place — prérequis Étape 10, présent dans le code ; seul l'échantillonnage manque)*
- [ ] BindGroup pour les textures dans le shader - [ ] BindGroup pour les textures dans le shader
- [ ] `Material` supporte une texture diffuse - [ ] `Material` supporte une texture diffuse