Files
wsg/docs/DRAFT.md
T
Jérôme Bousquié 7a5d627221 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.
2026-09-18 13:21:26 +02:00

105 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DRAFT — Étape 10 : Textures (Phase 4.1)
> 📅 **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 (état de départ)
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** : 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
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 (à valider)
- **[ ] 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
### 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`.
### 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.
### 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.
### 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
- [ ] 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é).
---
_Fin du DRAFT Étape 10 — à valider avant implémentation._