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

6.5 KiB
Raw Blame History

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.