3de84aa4dc
D1 placeholder 1×1 + groupe 2 sur toutes les pipelines · D2 échantillonnage inconditionnel · D3 image + Rgba8UnormSrgb · D4 Material propriétaire du bind group. Suite : implémentation 10.1-10.5 (refactor) puis docs (10.6).
105 lines
6.6 KiB
Markdown
105 lines
6.6 KiB
Markdown
# 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 (actées le 2026-09-18)
|
||
|
||
- **[x] 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)*.
|
||
- **[x] 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.
|
||
- **[x] 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`.
|
||
- **[x] 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
|
||
|
||
- [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é).
|
||
|
||
---
|
||
|
||
_Fin du DRAFT Étape 10 — à valider avant implémentation._
|