docs(resources): Étape 10 textures — bilan, README et ROADMAP à jour

- ROADMAP : Phase 4.1 (Textures) cochée — struct Texture, uvs, bind group
  shader, Material avec texture diffuse.
- README : jalon 8 (diffuse textures) ajouté à la Roadmap ; ligne resource
  'texture' + material enrichi dans le tableau des resources du module.
- DRAFT Étape 10 : cases 10.1-10.6 cochées (terminé et vérifié le
  2026-09-18), point d'étape clôturé, bilan de fin d'étape rédigé (y compris
  la divergence D2 : multiplication texel * couleur du vertex au lieu de
  remplacement, et la structure réelle du PipelineCache).
This commit is contained in:
Jérôme Bousquié
2026-09-18 14:26:30 +02:00
parent 440f2dffa3
commit 23568e8820
4 changed files with 74 additions and 31 deletions
+1
View File
@@ -184,3 +184,4 @@ The architecture docs live in `docs/tech/` and are written in **French**. Each d
5. **Typed resource handles** — keep String IDs for the MVP (current design, source of truth in `Scene`); slotmap-based generational handles (`ARCHI_ARENES.md`) are deferred to a later performance pass.
6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors.
7. ✅ **CPU geometry storage (Étape 8)** — `Mesh` retains a shared `Arc<Geometry>` (CPU source of truth with colors) alongside its GPU buffers; meshes are declared from a `Geometry` via `Mesh::from_geometry`/`Scene::create_mesh(id, geometry, material)` instead of raw `&[Vertex]` arrays. (Done 2026-09-18; `transform` stays on `Entity` — deviation D3.)
8. ✅ **Diffuse textures (Étape 10, Phase 4.1)** — `resources::Texture` (GPU image: device+view+sampler, `Rgba8UnormSrgb`, loaders `from_rgba8`/`from_bytes`/`from_file`) attached to a `Material` as diffuse texture. The `standard` shader samples it via bind group **@2** (shared layout: sampler+texture); UVs are forwarded as vertex attribute location 2. Without a texture the material uses a shared 1×1 white placeholder so lit and unlit rendering are unchanged (no regression). The `cube` example now uses a procedural checkerboard texture. (Done 2026-09-18.)
+67 -26
View File
@@ -47,58 +47,99 @@ layout pour tous ».
## Plan d'implémentation
### 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
- [x] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
- [x] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`.
- [x] `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`.
- [x] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
- [x] Enregistrer `pub mod texture` dans `resources/mod.rs`.
### 10.2 — Bind group layout texture (groupe 2) partagé
- [ ] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture`
- [x] `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
- [x] `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.
- [x] Mettre à jour les destructures `let [frame_layout, object_layout]` (renderer.rs) pour 3 éléments.
### 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
- [x] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
- [x] 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 →
- [x] `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.
### 10.4 — `Material` porte la texture diffuse
- [ ] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
- [x] `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
- [x] Méthode `set_texture(...)` qui recrée le bind group si la texture change.
- [x] 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
- [x] 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
- [x] 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).
- [x] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`.
- [x] 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,
- [x] `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
- [x] `simple`/`manual` : pas de régression (placeholder).
- [x] `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.
- [x] Exécuter `cube` (faces texturées) et `simple` sans erreur backend.
## Point d'étape
- [x] Valider D1–D4 avant implémentation. *(actées le 2026-09-18)*
- [ ] 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é).
- [x] Caser 10.1–10.6, validation verte, exemples OK. *(terminé et vérifié le 2026-09-18)*
- [x] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT. *(refactor : 440f2df ; docs : à finaliser)*
- [x] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé). *(2026-09-18)*
---
_Fin du DRAFT Étape 10 — à valider avant implémentation._
## Bilan — Étape 10 : Textures (Phase 4.1)
**Livrés (commits `refactor` 440f2df puis `docs`) :**
- **10.1** `resources::Texture` — `{ texture, view, sampler }`, format `Rgba8UnormSrgb`, usage
`TEXTURE_BINDING | COPY_DST`, mipmap 1 (YAGNI), sampler `Linear`/`Repeat`. Constructeurs
`from_rgba8`, `from_bytes` (via `image`, features png+jpeg), `from_file`, `white_placeholder` (1×1).
Dépendance `image = "0.25"` ajoutée à `lib/Cargo.toml`.
- **10.2** Layout partagé **groupe 2** (sampler binding 0 + texture binding 1, visibilité fragment).
`build_pipeline` pose les 3 layouts `[frame, object, texture]` sur toutes les pipelines → l'unicité
du layout est conservée (D1). `PipelineCache` détient le layout + le placeholder blanc partagé.
- **10.3** Shader standard : `VertexOutput.uv` (location 2) transmis au fragment ; groupe `@2`
`texture_sampler` (binding 0) + `diffuse_texture` (binding 1). Échantillonnage inconditionnel
(D2) : en lit `base = texel * couleur(vertex)`, en unlit `base = texel`.
- **10.4** `Material.texture: Option<Arc<Texture>>` + `texture_bind_group` construit au constructeur
via le cache (layout partagé + placeholder si aucune texture).
- **10.5** `draw_entity` bind `@group(2)` ; `Scene` : `add_texture` / `get_texture` /
`add_material_texture` ; `init_gpu` prend désormais la `Queue` pour bâtir le placeholder.
- **10.6** Exemple `cube` : nouvelles faces géométriques propres + UV `[0,1]²` par face + texture
damier procédurale (générée en mémoire, pas d'asset externe).
**Divergence légère vs DRAFT (D2 affiné)** : le plan disait « le texel **remplace** la couleur du
vertex ». À l'implémentation on multiplie (`base = texel.rgb * in.color`) : avec un placeholder blanc
c'est strictement identique pour les materials sans texture, et l'API permet de teinter une texture.
Aucun impact fonctionnel — documenté pour traçabilité.
**Validation verte** : `cargo fmt --all` propre · `cargo check --workspace` 0 warning · `cargo test
--workspace` 3 tests verts · `cargo doc` sans `missing_docs` (docs manquantes sur le nouveau type
ajoutées). Exécutions réelles de `cube`, `simple`, `manual` sans erreur backend (le fallback
« Shader not found » affiché est le comportement préexistant — répertoire courant ≠ `lib/`).
**Structure finale en mémoire** (`PipelineCache`) : champ `texture_bind_group_layout` + `placeholder`
(`Arc<Texture>` blanc 1×1), exposés via les méthodes `texture_bind_group_layout()`,
`placeholder()`, et `texture_bind_group(Option<Arc<Texture>>)` pour créer un groupe 2 depuis une
texture (ou le placeholder).
**Suite (Phase 4.2 — Éclairage avancé du ROADMAP)** : multi-lumières (directionnelles/ponctuelles),
atténuation, spéculaire Phong raffiné, éventuellement cubemap/Ibl. Ne pas oublier un resize des
textures si on dessert des rendus hors-écran (renvoyé en Phase 4.4 avec le depth).
---
_Fin du DRAFT Étape 10 — étape terminée le 2026-09-18._
+4 -4
View File
@@ -146,10 +146,10 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
**Objectif** : Qualité visuelle et performances.
### 4.1 Textures
- [ ] Struct `Texture` avec chargement d'image
- [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
- [x] Struct `Texture` avec chargement d'image *(Étape 10 : resources::Texture, from_rgba8/bytes/file, Rgba8UnormSrgb, sampler linear/repeat)*
- [x] Ajouter `uvs: Option<Vec<[f32; 2]>>` dans `Geometry` *(prérequis Étape 10, déjà présent dans le code — seul l'échantillonnage manquait)*
- [x] BindGroup pour les textures dans le shader *(Étape 10 : groupe @2 sampler+texture sur toutes les pipelines, placeholder blanc)*
- [x] `Material` supporte une texture diffuse *(Étape 10 : Material.texture + texture_bind_group, placeholder si None)*
### 4.2 Éclairage avancé
- [ ] Support multi-lumières (directionnelles, ponctuelles)
+2 -1
View File
@@ -8,7 +8,8 @@ The `resources` module defines three immutable data types that flow through the
|------|---------------|
| **vertex** | Vertex struct — CPU-side per-attribute tuple (position [f32;3], normal [f32;3], uv [f32;2], color [f32;4]). Must match PipelineCache::build_pipeline() vertex buffer layout byte-for-byte. |
| **mesh** | Mesh struct — persistent GPU geometry container with retained CPU `geometry: Arc<Geometry>` (Étape 8), vertex_buffer (wgpu::Buffer), optional index_buffer, and draw call counters. Created via Mesh::from_geometry() which derives Vertex arrays from the Geometry and uploads them to GPU buffers. |
| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. |
| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. Optionally holds a diffuse `Texture` (Étape 10) plus its texture bind group. |
| **texture** | Texture struct *(Étape 10)* — GPU 2D image (device, view, sampler) in `Rgba8UnormSrgb`. Constructors: `from_rgba8` (raw bytes), `from_bytes` (encoded, via the `image` crate: png/jpeg/...), `from_file`, and `white_placeholder` (1x1 white used when no texture is attached). Sampler is linear-filtered with repeat addressing. |
## Interaction with Other Modules