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:
+82
-97
@@ -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.
|
||||
|
||||
## 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
|
||||
les plus lointaines ne sont pas masquées par les faces plus proches. Causes identifiées dans
|
||||
le code (observation du 2026-09-18) :
|
||||
- `lib/src/pipeline/pipeline_cache.rs` → `depth_stencil: None` à la création du pipeline (L233).
|
||||
- `lib/src/core/renderer.rs` → les deux render passes (`render`, `render_scene`) ne posent
|
||||
**aucun** `depth_stencil_attachment` ; seul un `color_attachments` est fourni (L194, L239).
|
||||
- Aucune texture de profondeur n'est créée, donc aucun z-test n'est exécuté.
|
||||
La Phase 4.1 (Textures) du ROADMAP vise à texturer le rendu. État actuel du code :
|
||||
- **La tuyauterie UV existe déjà** : `Geometry.uvs: Option<Vec<[f32; 2]>>` (builder `with_uvs`)
|
||||
et `Vertex.uv` (location 2, offset 24, stride 56) sont déclarés dans `build_pipeline` et lus
|
||||
par le shader `VertexInput`. Seul l'**échantillonnage manque**.
|
||||
- Le shader `standard_shader.wgsl` reçoit l'UV mais ne le transmet pas au fragment et n'échantillonne rien.
|
||||
- **Aucun type `Texture`**, aucun sampler, aucun bind group de texture. `Material` ne porte pas de texture.
|
||||
- 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
|
||||
les textures (Phase 4.1), tel que décidé avec l'utilisateur le 2026-09-18.
|
||||
**Conclusion** : l'UV est prête ; les pièces manquantes sont un type `Texture`, un bind group de
|
||||
texture (groupe 2) partagé, un échantillonnage dans le shader et le portage sur `Material`.
|
||||
|
||||
## Objectif
|
||||
|
||||
Activer le test de profondeur (z-buffer) sur tous les chemins de rendu (`render` ET `render_scene`)
|
||||
pour que les faces cachées soient correctement masquées.
|
||||
Permettre de texturer un mesh : charger une image → `wgpu::Texture` (+ view + sampler), lier une
|
||||
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`,
|
||||
valeur de clear `1.0` (portée maximale), `depth_compare: Less`, `depth_write_enabled: true`.
|
||||
- **[x] D2 (portée)** — *(validée 2026-09-18)* : le depth buffer est posé sur **les deux** render
|
||||
passes (bas niveau `render` et haut niveau `render_scene`), pas seulement `render_scene`, pour
|
||||
garder la cohérence du flux 2D.
|
||||
- **[x] D3 (résolution)** — *(validée 2026-09-18)* : la texture de profondeur est allouée à la taille
|
||||
de la surface initiale. **Le resize est planifié comme chantier dédié** dans `docs/ROADMAP.md`
|
||||
(Phase 4.4, avec recréation de la depth texture en même temps que la reconfiguration de la surface),
|
||||
hors périmètre de l'Étape 9. On créera un helper `create_depth_texture` isolé pour rendre ce
|
||||
recreate trivial plus tard.
|
||||
- **[x] D4 (échantillonnage)** — *(validée 2026-09-18, YAGNI)* : aucune texture de profondeur n'est
|
||||
échantillonnée pour l'instant (pas de shadow mapping ni de post-processing) — on n'a donc pas
|
||||
besoin de l'exposer dans un bind group. Simple render target + state pipeline suffisent ; le bind
|
||||
group sera ajouté quand le shadow mapping arrivera.
|
||||
- **[ ] D1 (intégration bind group — « un seul layout pour tous »)** : on ajoute un **3ᵉ bind group**
|
||||
`@group(2)` (sampler + texture diffuse) posé sur **toutes** les pipelines, avec une **texture
|
||||
blanche 1×1 de secours (placeholder)** utilisée quand un `Material` n'a pas de texture. Cela
|
||||
préserve l'unicité du layout (aucune pipeline multiple), donc un seul flux de rendu. *(alternative
|
||||
écartée : bind group optionnel → casse l'unicité du layout, refactor de toutes les pipelines)*.
|
||||
- **[ ] D2 (échantillonnage inconditionnel)** : le fragment shader échantillonne **toujours** la
|
||||
texture diffuse ; le placeholder blanc (texel = 1) reproduit exactement le comportement actuel
|
||||
d'un material sans texture. Donc pas de flag conditionnel → un seul flow de shader. En mode lit,
|
||||
le **texel remplace la couleur du vertex** (`texel.rgb * (ambient + diffuse)`) ; en unlit, le texel
|
||||
tel quel. Symétrique du pattern unlit existant.
|
||||
- **[ ] D3 (chargement d'image & format)** : ajouter la dépendance `image` (décodage PNG/JPEG) à
|
||||
`lib/Cargo.toml` ; upload en `TextureFormat::Rgba8UnormSrgb`, usage `TEXTURE_BINDING | COPY_DST`,
|
||||
dimension `D2`, `mip_level_count: 1` (**YAGNI** : pas de génération de mipmaps cette étape),
|
||||
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
|
||||
|
||||
### 9.1 — Créer la texture de profondeur dans `Renderer` *(terminée et vérifiée le 2026-09-18)*
|
||||
- [x] Ajouter un champ `depth_view: wgpu::TextureView` au `Renderer` (avec helper pour allouer
|
||||
`depth_texture` + `depth_view` à partir de `device` et de la taille surface).
|
||||
- [x] Taille : `width`/`height` passés à `Renderer::new` (aujourd'hui il ne reçoit que `format`).
|
||||
Portée : avantage à `create_depth_texture(device, width, height, format)`.
|
||||
- [x] `TextureDescriptor` : `size: Extent3d { width, height, depth_or_array_layers: 1 }`,
|
||||
`mip_level_count: 1`, `sample_count: 1`, `dimension: D2`,
|
||||
`format: wgpu::TextureFormat::Depth32Float`,
|
||||
`usage: RENDER_ATTACHMENT`, `label: Some("depth texture")`.
|
||||
### 10.1 — Nouveau type `Texture` + dépendance `image`
|
||||
- [ ] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
|
||||
- [ ] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`.
|
||||
- [ ] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un
|
||||
buffer RGBA et upload via `Queue::write_texture` (D3).
|
||||
- [ ] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
|
||||
- [ ] Enregistrer `pub mod texture` dans `resources/mod.rs`.
|
||||
|
||||
### 9.2 — Attacher le depth au render pass *(terminée et vérifiée le 2026-09-18)*
|
||||
- [x] Dans `render_scene` (renderer.rs) : ajouter `depth_stencil_attachment: Some(...)` au
|
||||
`RenderPassDescriptor`, avec `depth_ops: Operations { load: Clear(1.0), store: Store }`.
|
||||
- [x] Même ajout dans `render` (renderer.rs) pour la cohérence du chemin bas niveau.
|
||||
### 10.2 — Bind group layout texture (groupe 2) partagé
|
||||
- [ ] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture`
|
||||
= `BindingType::Sampler(Filtering)` (binding 0) + `Texture { sample_type: Float, view_dimension: D2 }`
|
||||
(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)*
|
||||
- [x] Dans `pipeline_cache.rs` (L233) : remplacer `depth_stencil: None` par
|
||||
`Some(DepthStencilState { format: Depth32Float, depth_write_enabled: Some(true),
|
||||
depth_compare: Some(CompareFunction::Less), stencil: StencilState::default(),
|
||||
bias: DepthBiasState::default() })` — note : en wgpu 30, `depth_write_enabled` et
|
||||
`depth_compare` sont des `Option` (le DRAFT initial les donnait nus).
|
||||
- [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`.
|
||||
### 10.3 — Shader standard : UV → fragment + échantillonnage
|
||||
- [ ] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
|
||||
- [ ] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et
|
||||
`@group(2) @binding(1) var diffuse_texture: texture_2d<f32>;`.
|
||||
- [ ] `fs_main` : `let texel = textureSample(diffuse_texture, texture_sampler, in.uv);` — en lit →
|
||||
`texel.rgb * (ambient + diffuse)`, en unlit → `texel` (D2). Mettre à jour la doc du module shader.
|
||||
|
||||
### 9.4 — Vérification *(terminée et vérifiée le 2026-09-18)*
|
||||
- [x] `cargo fmt --all` (aucun diff attendu), `cargo check --workspace` (zéro warning),
|
||||
`cargo test --workspace` (vert : 3 tests), `cargo doc` (pas de `missing_docs`).
|
||||
- [x] Lancer l'exemple `cube` (`cargo run -p wsg-lib --example cube`) : tourne sans erreur backend
|
||||
(le z-buffer est accepté par wgpu, aucun format mismatch) — faces arrière masquées.
|
||||
- [x] Lancer `simple` (rendu plat via `render_scene`) : pas de régression, tourne sans erreur GPU.
|
||||
### 10.4 — `Material` porte la texture diffuse
|
||||
- [ ] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
|
||||
bind group (groupe 2) depuis le layout partagé, avec placeholder si `None` (D1/D4).
|
||||
- [ ] Méthode `set_texture(...)` qui recrée le bind group si la texture change.
|
||||
- [ ] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout
|
||||
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
|
||||
|
||||
- [x] Valider D1–D4 avant implémentation. *(fait — 2026-09-18 : D1 Depth32Float, D2 les deux passes,
|
||||
D3 resize planifié dans RoADMAP Phase 4.4, D4 YAGNI)*
|
||||
- [x] Caser 9.1–9.4, validation verte, exemples OK. *(fait — 2026-09-18)*
|
||||
- [x] Deux commits séparés : `refactor(...)` (9.1–9.3) puis `docs(...)` (9.4) + README/DRAFT.
|
||||
*(fait — refactor fca5d72 puis docs)*
|
||||
- [x] Rédiger le bilan et ouvrir la suite (textures/matériaux Phase 4.1).
|
||||
- [ ] Valider D1–D4 avant implémentation.
|
||||
- [ ] Caser 10.1–10.6, validation verte, exemples OK.
|
||||
- [ ] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT.
|
||||
- [ ] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé).
|
||||
|
||||
---
|
||||
|
||||
## Bilan de l'Étape 9 — Depth Buffer (terminée le 2026-09-18)
|
||||
|
||||
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._
|
||||
_Fin du DRAFT Étape 10 — à valider avant implémentation._
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
|
||||
|
||||
### 4.1 Textures
|
||||
- [ ] 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
|
||||
- [ ] `Material` supporte une texture diffuse
|
||||
|
||||
|
||||
Reference in New Issue
Block a user