From 83daeb4c7dc9f6a395c54e703efe7251125d2a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Bousqui=C3=A9?= Date: Fri, 25 Sep 2026 14:54:27 +0200 Subject: [PATCH] readmes --- README.md | 154 ++++++++------- README_DETAILS.md | 89 +++++---- lib/examples/README.md | 413 ++++++++++++++++++++--------------------- 3 files changed, 341 insertions(+), 315 deletions(-) diff --git a/README.md b/README.md index d6e45e5..82171da 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,28 @@ # WSG — WGPU Simple Graphics Library -**WSG** (WGPU Simple Graphics) est une bibliothèque Rust qui wrap [wgpu](https://github.com/gfx-rs/wgpu) et [winit](https://crates.io/crates/winit) pour dessiner en 3D **sans toucher wgpu directement**. +**WSG** (WGPU Simple Graphics) is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://crates.io/crates/winit) to draw 3D **without touching wgpu directly**. -## Ce que vous obtenez +## What you get -- **Une fenêtre 3D en ~30 lignes** — pas de wgpu, pas de winit dans votre code -- **Éclairage Phong** (directional, point, spot) + **ombres portées** (shadow mapping) -- **HDR + Tone Mapping** (ACES Filmic / Reinhard) — opt-in, zéro coût si désactivé -- **Pipeline GPU-driven** — world matrices + frustum culling sur le GPU, indirect draws -- **LOD** (Level of Detail) — dégradation automatique de la géométrie selon la distance -- **Primitives procédurales** — cube, sphère, cylindre, cône, tore, plan -- **Import de fichiers** — parser OBJ intégré (glTF en cours) -- **Caméra orbitale** + input unifié (clavier/souris) -- **LOD, culling, HDR, ombres** : tout est **opt-in** — ce que vous n'activez pas ne coûte rien +- **A 3D window in ~30 lines** — no wgpu, no winit in your code +- **Phong lighting** (directional, point, spot) + **shadows** (shadow mapping) +- **HDR + Tone Mapping** (ACES Filmic / Reinhard) — opt-in, zero cost when disabled +- **GPU-driven pipeline** — world matrices + frustum culling on the GPU, indirect draws +- **LOD** (Level of Detail) — automatic geometry degradation based on distance +- **Procedural primitives** — cube, sphere, cylinder, cone, torus, plane +- **File import** — built-in OBJ parser (glTF in progress) +- **Orbital camera** + unified input (keyboard/mouse) +- **LOD, culling, HDR, shadows**: everything is **opt-in** — what you don't enable costs nothing -## Forces +## Strengths -| Force | Détail | -|-------|--------| -| **Zéro wgpu dans votre code** | L'API déclarative (`AppBuilder` + `AppHandler`) encapsule tout | -| **Opt-in = zéro coût** | Un effet non activé n'alloue rien, n'exécute rien | -| **Features Cargo** | Ne compilez que les primitives/import dont vous avez besoin | -| **Un seul shader** | Le `standard` shader (Phong) couvre 90 % des cas ; mode unlit pour la 2D | -| **GPU-driven** | Le CPU envoie des transforms, le GPU fait le reste (matrices, culling, draws) | +| Strength | Detail | +|----------|--------| +| **Zero wgpu in your code** | The declarative API (`AppBuilder` + `AppHandler`) encapsulates everything | +| **Opt-in = zero cost** | A disabled effect allocates nothing, executes nothing | +| **Cargo features** | Only compile the primitives/importers you need | +| **One shader** | The `standard` shader (Phong) covers 90% of cases; unlit mode for 2D | +| **GPU-driven** | CPU sends transforms, GPU does the rest (matrices, culling, draws) | ## Quickstart @@ -31,9 +31,9 @@ use wsg_lib::prelude::*; use wsg_lib::app::AppBuilder; use wsg_lib::utils::WsgError; -struct MaScene; +struct MyScene; -impl AppHandler for MaScene { +impl AppHandler for MyScene { fn setup(&mut self, app: &mut wsg_lib::App) { app.scene .register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH) @@ -42,7 +42,7 @@ impl AppHandler for MaScene { .create_material("mat", "standard", None) .unwrap(); - // Un cube lit par Phong, posé au-dessus d'un plan + // A Phong-lit cube, sitting on a ground plane app.scene .create_mesh("cube", cube(1.0), Some("mat")) .unwrap(); @@ -61,10 +61,10 @@ impl AppHandler for MaScene { fn main() -> Result<(), WsgError> { let mut app = AppBuilder::new() - .title("Ma scène WSG") - .with_hdr(ToneMapper::Aces) // optionnel : HDR + tone mapping + .title("My WSG scene") + .with_hdr(ToneMapper::Aces) // optional: HDR + tone mapping .build()?; - app.run(MaScene); + app.run(MyScene); Ok(()) } ``` @@ -76,81 +76,91 @@ pollster = { version = "1", features = ["macro"] } ``` ```sh -cargo run --example demo # le showcase complet (6 primitives, 3 lumières, ombres, HDR) +cargo run --example demo # full showcase (6 primitives, 3 lights, shadows, HDR) ``` -## Fonctionnalités +## Features -| Catégorie | Ce qui est disponible | -|-----------|----------------------| -| **Géométrie** | 6 primitives procédurales + import OBJ + `Geometry` custom | -| **Rendu** | Phong (lit), unlit (2D flat), HDR + tone mapping (ACES/Reinhard) | -| **Lumières** | Directional, point, spot (8 max) + ambient | -| **Ombres** | Shadow mapping (directional/spot), slope-scaled bias, PCF | -| **LOD** | Décimation quadric auto, hystérésis, 1 buffer multi-niveaux | +| Category | What's available | +|----------|-----------------| +| **Geometry** | 6 procedural primitives + OBJ import + custom `Geometry` | +| **Rendering** | Phong (lit), unlit (2D flat), PBR metallic/roughness, HDR + tone mapping | +| **Lights** | Directional, point, spot (8 max) + ambient | +| **Shadows** | Shadow mapping (directional/spot), slope-scaled bias, PCF | +| **LOD** | Auto quadric decimation, hysteresis, 1 buffer multi-level | | **GPU-driven** | Compute pass (matrices + culling) → indirect draws | -| **Caméra** | Orbitale (drag/zoom/reset) + presets (front/side/top) | -| **Input** | Clavier (pressed/held/released), souris (delta, scroll, boutons) | -| **Textures** | RGBA8 (de bytes, de fichier, placeholder blanc) | +| **Post-process** | Bloom, Depth of Field, Fog (3 modes), MSAA 4× | +| **Camera** | Orbital (drag/zoom/reset) + presets (front/side/top) | +| **Input** | Keyboard (pressed/held/released), mouse (delta, scroll, buttons) | +| **Textures** | RGBA8 (from bytes, from file, white placeholder) | ## Documentation -| Où | Quoi | -|----|------| -| [docs/user/](docs/user/README.md) | **Guide utilisateur** (EN) — comment utiliser l'API, pas à pas | -| [docs/tech/](docs/tech/ARCHI_APP.md) | **Architecture interne** (FR) — décisions, specs, cibles | -| [docs/ROADMAP.md](docs/ROADMAP.md) | Feuille de route (phases 1-5 ✅, phase 6 en cours) | -| [docs/PLAN.md](docs/PLAN.md) | Livre de recette (historique des étapes) | -| `cargo doc -p wsg-lib --no-deps` | **Référence API** (rustdoc, 100 % couvert) | +| Where | What | +|-------|------| +| [docs/user/](docs/user/README.md) | **User guide** — how to use the API, step by step | +| [docs/tech/](docs/tech/ARCHI_APP.md) | **Internal architecture** — decisions, specs, targets | +| [docs/ROADMAP.md](docs/ROADMAP.md) | Roadmap (phases 1-5 ✅, phase 6 in progress) | +| [docs/PLAN.md](docs/PLAN.md) | Recipe book (step history) | +| `cargo doc -p wsg-lib --no-deps` | **API reference** (rustdoc, 100% covered) | -## Exemples +## Examples -| Exemple | Ce qu'il montre | -|---------|----------------| -| `demo` | Le showcase : 6 primitives, 3 lumières, ombres, HDR, LOD, caméra orbitale | -| `cube` | MVP 3D : un cube lit par Phong, texture checkerboard | -| `simple` | Minimal : un quad coloré en mode unlit (2D) | -| `shadow_test` | Ombres portées isolées | -| `spot_test` | Spotlight isolé | -| `import` | Import de fichier OBJ (feature `import-obj`) | -| `manual` | Workflow low-level (Context/Renderer/PipelineCache, sans App) | +| Example | What it shows | +|---------|---------------| +| `demo` | Full showcase: 6 primitives, 3 lights, shadows, HDR, LOD, orbital camera | +| `bloom` | HDR bloom post-process | +| `hdr` | HDR + tone mapping (ACES/Reinhard) | +| `emissive` | Emissive materials + runtime exposure control | +| `shadow` | Shadow mapping in isolation | +| `culling` | GPU-driven frustum culling (15×15 grid) | +| `msaa` | 4× multisample anti-aliasing | +| `fog` | 3 fog modes (linear, exponential, exponential²) | +| `dof` | Depth of field with focus presets | +| `pbr` | PBR metallic/roughness + normal mapping | +| `import` | OBJ file import (feature `import-obj`) | +| `manual` | Low-level workflow (Context/Renderer/PipelineCache, no App) | -## Features Cargo +## Cargo Features ```toml -# Default : toutes les primitives +# Default: all primitives wsg-lib = { path = "../lib" } -# Minimal : juste le cube +# Minimal: just the cube wsg-lib = { path = "../lib", default-features = false, features = ["prim-cube"] } -# Avec import OBJ +# With OBJ import wsg-lib = { path = "../lib", features = ["import-obj"] } ``` -| Feature | Active | -|---------|--------| +| Feature | Enables | +|---------|---------| | `prim-cube`, `prim-plane`, `prim-sphere`, `prim-cylinder`, `prim-cone`, `prim-torus` | Primitives | -| `all-prims` (default) | Les 6 primitives | -| `import-obj` | Parser Wavefront OBJ | +| `all-prims` (default) | All 6 primitives | +| `import-obj` | Wavefront OBJ parser | | `import-gltf` | glTF (stub) | ## Build ```sh -cargo build --workspace # tout -cargo test --workspace # 116 tests -cargo check --all-targets # vérification rapide -cargo run -p wsg-lib --example demo # lancer le showcase +cargo build --workspace # everything +cargo test --workspace # 127 tests +cargo check --all-targets # quick check +cargo run -p wsg-lib --example demo # run the showcase ``` -## Projet +## Project -- **Langage** : Rust 2024 -- **Dépendances** : wgpu 30, winit 0.30, glam (math) -- **Pas publié sur crates.io** (dépendance par path) -- **Status** : MVP complet (phases 1-5 ✅), post-MVP en cours (phase 6) +- **Language**: Rust 2024 +- **Dependencies**: wgpu 30, winit 0.30, glam (math) +- **Not published on crates.io** (path dependency) +- **Status**: MVP complete (phases 1-5 ✅), post-MVP in progress (phase 6) --- -*Documentation détaillée (architecture, status, API reference, workflow manuel) : [README_DETAILS.md](README_DETAILS.md)* +*Detailed documentation (architecture, status, API reference, manual workflow): [README_DETAILS.md](README_DETAILS.md)* + +--- + +> This project was heavily developed using OpenCode, Pi Code, and JCode AI agents running on local Qwen3-27b_Q4 and DeepSeek V4 Flash Q4 instances. The project organization and architecture are the author's own design. diff --git a/README_DETAILS.md b/README_DETAILS.md index a0962a0..d5135fb 100644 --- a/README_DETAILS.md +++ b/README_DETAILS.md @@ -1,6 +1,6 @@ -# WSG — Documentation détaillée +# WSG — Detailed Documentation -> Contenu technique du README principal : status, architecture, API reference, workflows, roadmap. +> Technical content from the main README: status, architecture, API reference, workflows, roadmap. ## Status @@ -9,14 +9,19 @@ | Manual workflow (`Context` + `Renderer` + `PipelineCache`) | ✅ Working (advanced — fine-grained control) | | `App` / `AppBuilder` / `AppHandler` event-loop facade | ✅ Working — window, events, frame presentation, automatic scene rendering | | `Scene` resource/entity registry | ✅ Working — auto-rendered in one batched pass (`App::render_scene`) | -| GPU-driven two-pass pipeline (Compute → indirect draw) | ✅ Working (Phase 3) — `render_scene` + shadow pass 100 % indirect; opt-in frustum culling | +| GPU-driven two-pass pipeline (Compute → indirect draw) | ✅ Working (Phase 3) — `render_scene` + shadow pass 100% indirect; opt-in frustum culling | | 3D infrastructure (uniform bind groups, MVP + camera) | ✅ Working — per-frame camera + per-entity world matrices in shared uniforms | | Shadows (shadow mapping) | ✅ Working — directional/spot, slope-scaled bias, PCF 3×3 | -| HDR + Tone Mapping | ✅ Working (Étape 20) — offscreen Rgba16Float, ACES/Reinhard, opt-in | -| LOD (Level of Detail) | ✅ Working (Étape 19) — quadric decimation, hysteresis, multi-level buffer | -| Mesh module (primitives + import) | ✅ Working (Étape 21) — feature-gated primitives, OBJ parser | +| HDR + Tone Mapping | ✅ Working — offscreen Rgba16Float, ACES/Reinhard, opt-in | +| LOD (Level of Detail) | ✅ Working — quadric decimation, hysteresis, multi-level buffer | +| Mesh module (primitives + import) | ✅ Working — feature-gated primitives, OBJ parser | +| Bloom | ✅ Working — threshold + separable blur + composite, HDR required | +| Fog | ✅ Working — 3 modes (linear, exp, exp²), runtime switchable | +| MSAA | ✅ Working — 4× multisample, resolve pass | +| DoF | ✅ Working — CoC + disc blur, focus presets | +| PBR (metallic/roughness + normal maps) | ✅ Working — Cook-Torrance, GGX, IBL, derivative tangent | -Note: `standard_shader.wgsl` (Phong, with an explicit **unlit** mode) is the **single** shader the library ships. Flat 2D drawing is its unlit variant (`Renderer::set_unlit(true)`). +Note: `standard_shader.wgsl` (Phong + PBR, with an explicit **unlit** mode) is the **single** shader the library ships. Flat 2D drawing is its unlit variant (`Renderer::set_unlit(true)`). ## Architecture @@ -37,28 +42,31 @@ Spec: [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) · User guide: [d ``` lib/src/ ├── lib.rs # crate root, re-exports -├── prelude.rs # glob re-exports (types quotidiens) +├── prelude.rs # glob re-exports ├── app.rs # App + AppBuilder ├── handler.rs # AppHandler trait +├── camera.rs # Camera, CameraController +├── input.rs # InputState +├── lights.rs # Lights, Light, LightType, directional_light, … ├── core/ │ ├── context.rs # GPU lifecycle (Instance/Surface/Adapter/Device/Queue) │ ├── renderer.rs # RenderPass execution, shadow pass, HDR/TM pass │ ├── frame.rs # Per-frame RAII (surface texture + view) -│ ├── input.rs # Unified keyboard/mouse state │ ├── geometry.rs # Geometry (positions/normals/UVs/indices) + BBox │ ├── transform.rs # Transform (translation/rotation/scale) │ ├── frustum.rs # Frustum (6 planes, sphere/box culling) │ ├── lod.rs # LOD decimation (quadric edge collapse) │ ├── shadow.rs # ShadowConfig (map size, bias, PCF) -│ └── hdr.rs # ToneMapper enum (Aces/Reinhard) +│ ├── hdr.rs # ToneMapper enum (Aces/Reinhard) +│ ├── bloom.rs # BloomConfig + BloomPipeline +│ ├── msaa.rs # MsaaConfig +│ ├── fog.rs # FogConfig + FogMode +│ └── dof.rs # DoFConfig + DoFPipeline ├── mesh/ │ ├── mod.rs # Re-exports flat │ ├── primitives/ # 6 feature-gated generators │ └── import/ # OBJ parser + glTF stub ├── pipeline/ # PipelineCache (shader → RenderPipeline) -├── camera/ # Camera, CameraController -├── lights/ # Lights, Light, LightType, directional_light, … -├── input/ # InputState ├── resources/ # Mesh, Material, Texture, Uniform, Vertex ├── scene/ # Scene (registry), Entity └── utils/ # Conf constants, WsgError @@ -72,9 +80,9 @@ lib/src/ | AppHandler | Trait | `setup()` / `update()` / `render()` callbacks | | Scene | Struct | Registry: shaders, materials, meshes, entities, lights, camera | | Context | Struct | GPU hardware (Instance, Surface, Adapter, Device, Queue) | -| Renderer | Struct | RenderPass execution (scene, shadow, HDR/TM) | +| Renderer | Struct | RenderPass execution (scene, shadow, HDR/TM, bloom, DoF) | | PipelineCache | Struct | Shader → compiled RenderPipeline cache | -| Material | Struct | Shader ID + texture + pipeline | +| Material | Struct | Shader ID + texture + pipeline + PBR params | | Geometry | Struct | CPU vertex data (positions/normals/UVs/colors/indices) | | Mesh / Vertex | Struct | GPU geometry / interleaved upload tuple | | Frame | Struct | Per-frame RAII (surface texture + view) | @@ -85,6 +93,10 @@ lib/src/ | Lights / Light | Struct | Light list (directional/point/spot, MAX=8) + ambient | | ShadowConfig | Struct | Shadow map size, bias, PCF taps, scene radius | | ToneMapper | Enum | ACES Filmic / Reinhard | +| BloomConfig | Struct | Threshold, intensity, H/V passes | +| MsaaConfig | Struct | Sample count (1 = disabled) | +| FogConfig | Struct | Mode, near/far, density, color | +| DoFConfig | Struct | Focus distance, aperture, max blur | | BBox | Struct | Axis-aligned bounding box (min/max) | | Frustum | Struct | 6 planes, sphere/box culling | @@ -95,14 +107,14 @@ use wsg_lib::prelude::*; use wsg_lib::app::AppBuilder; use wsg_lib::utils::WsgError; -struct MaScene; +struct MyScene; -impl AppHandler for MaScene { +impl AppHandler for MyScene { fn setup(&mut self, app: &mut wsg_lib::App) { app.scene .register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH) .unwrap(); - app.scene.create_material("mat", "standard", None).unwrap(); + app.scene.add_material_shader("mat", "standard").unwrap(); app.scene.create_mesh("cube", cube(1.0), Some("mat")).unwrap(); app.scene.add_entity("my_cube", "cube").unwrap(); } @@ -112,9 +124,10 @@ impl AppHandler for MaScene { // render() default: app.render_scene(frame.view()) — auto-draws everything } -fn main() -> Result<(), WsgError> { - let app = AppBuilder::new().title("WSG").build()?; - app.run(MaScene); +#[pollster::main] +async fn main() -> Result<(), WsgError> { + let app = AppBuilder::new().title("WSG").build().await?; + app.run(MyScene); Ok(()) } ``` @@ -167,15 +180,15 @@ fn main() { ## Features -| Feature | Default | Fournit | -|---------|---------|---------| +| Feature | Default | Provides | +|---------|---------|----------| | `prim-cube` | ✅ | `cube(size)` | | `prim-plane` | ✅ | `plane(w, d, seg_x, seg_z)` | | `prim-sphere` | ✅ | `uv_sphere(…)`, `icosphere(…)` | | `prim-cylinder` | ✅ | `cylinder(…)` | | `prim-cone` | ✅ | `cone(…)` | | `prim-torus` | ✅ | `torus(…)` | -| `all-prims` | ✅ (default) | Les 6 primitives | +| `all-prims` | ✅ (default) | All 6 primitives | | `import-obj` | ⬜ | `load_obj(path)`, `parse_obj(str)` | | `import-gltf` | ⬜ | `load_gltf(path)` (stub) | @@ -185,6 +198,10 @@ fn main() { |---------|--------------|----------------| | Shadows | `scene.set_shadow_caster(Some(idx))` | No shadow map, no depth pass, no PCF | | HDR + TM | `AppBuilder::with_hdr(ToneMapper::Aces)` | No offscreen texture, no TM pass | +| Bloom | `AppBuilder::with_bloom(BloomConfig::…)` | No bloom textures, no passes | +| MSAA | `AppBuilder::with_msaa(MsaaConfig { sample_count: 4 })` | Single sample, no resolve | +| Fog | `AppBuilder::with_fog(FogConfig::…)` | No fog uniforms | +| DoF | `AppBuilder::with_dof(DoFConfig::…)` | No CoC/blur textures | | GPU-driven culling | `AppBuilder::with_gpu_driven(true)` | No compute pipeline, no indirect buffers | | LOD | `scene.create_mesh_with_lod(…, levels)` | Single-level mesh | | Primitives | Cargo feature `prim-*` | Not compiled | @@ -194,19 +211,19 @@ fn main() { | Phase | Status | |-------|--------| -| 1 — Fondations (window, render loop, Context) | ✅ | -| 2 — Infrastructure 3D (Geometry, Mesh, Material, Pipeline) | ✅ | +| 1 — Foundations (window, render loop, Context) | ✅ | +| 2 — 3D infrastructure (Geometry, Mesh, Material, Pipeline) | ✅ | | 3 — GPU-driven (compute pass, indirect draws, culling) | ✅ | -| 4 — Rendu avancé (shadows, HDR/TM, lights) | ✅ | -| 5 — Polissage (LOD, camera controller, input, demo) | ✅ | -| 6 — Post-MVP (bloom, PBR, cascaded shadows, SSAO, refactoring) | 🔄 | +| 4 — Advanced rendering (shadows, HDR/TM, lights) | ✅ | +| 5 — Polish (LOD, camera controller, input, demo) | ✅ | +| 6 — Post-MVP (bloom, PBR, fog, DoF, MSAA, cascaded shadows, SSAO) | 🔄 | ## Documentation -| Où | Quoi | -|----|------| -| [docs/user/](docs/user/README.md) | Guide utilisateur (EN) | -| [docs/tech/](docs/tech/ARCHI_APP.md) | Architecture interne (FR) | -| [docs/ROADMAP.md](docs/ROADMAP.md) | Feuille de route | -| [docs/PLAN.md](docs/PLAN.md) | Livre de recette (historique) | -| `cargo doc -p wsg-lib --no-deps` | Référence API (rustdoc) | +| Where | What | +|-------|------| +| [docs/user/](docs/user/README.md) | User guide | +| [docs/tech/](docs/tech/ARCHI_APP.md) | Internal architecture | +| [docs/ROADMAP.md](docs/ROADMAP.md) | Roadmap | +| [docs/PLAN.md](docs/PLAN.md) | Recipe book (step history) | +| `cargo doc -p wsg-lib --no-deps` | API reference (rustdoc) | diff --git a/lib/examples/README.md b/lib/examples/README.md index 7bc28a6..7d645c3 100644 --- a/lib/examples/README.md +++ b/lib/examples/README.md @@ -1,341 +1,340 @@ -# Exemples WSG +# WSG Examples -Chaque exemple est autonome et illustre **un effet ou une fonctionnalité** spécifique -de la bibliothèque. Tous utilisent l'API déclarative (`AppBuilder` + `AppHandler`). +Each example is self-contained and demonstrates **one effect or feature** of the library. All use the declarative API (`AppBuilder` + `AppHandler`). -## Lancer un exemple +## Running an example ```sh -cargo run -p wsg-lib --example +cargo run -p wsg-lib --example ``` -| Exemple | Effet démontré | -|---------|---------------| -| `demo` | Showcase complet (tous les effets combinés) | -| `bloom` | Post-process bloom (glow autour des zones brillantes) | -| `hdr` | HDR + Tone Mapping (ACES) + contrôle d'exposition | -| `emissive` | Matériaux émissifs (intensités croissantes 0 → 4.0) | -| `shadow` | Shadow mapping (ombre portée directionnelle) | -| `culling` | Culling GPU-driven (grille 15×15, objets hors frustum ignorés) | -| `msaa` | MSAA 4× (anti-aliasing multi-échantillons, arêtes lisses) | -| `fog` | Brouillard de distance (3 modes : linéaire, exp, exp²) | -| `manual` | Workflow bas niveau (Context + Renderer + PipelineCache) | -| `import` | Import de fichier OBJ (non graphique, stdout) | +| Example | Effect demonstrated | +|---------|-------------------| +| `demo` | Full showcase (all effects combined) | +| `bloom` | Post-process bloom (glow around bright areas) | +| `hdr` | HDR + Tone Mapping (ACES) + exposure control | +| `emissive` | Emissive materials (increasing intensities 0 → 4.0) | +| `shadow` | Shadow mapping (directional shadow) | +| `culling` | GPU-driven culling (15×15 grid, off-frustum objects skipped) | +| `msaa` | MSAA 4× (multisample anti-aliasing, smooth edges) | +| `fog` | Distance fog (3 modes: linear, exp, exp²) | +| `dof` | Depth of Field (cinematic bokeh, focus presets) | +| `pbr` | PBR metallic/roughness + normal mapping | +| `manual` | Low-level workflow (Context + Renderer + PipelineCache) | +| `import` | OBJ file import (non-graphical, stdout) | --- -## `demo` — Showcase complet +## `demo` — Full Showcase -Combine **tous** les effets : primitives LOD, textures procédurales, lumières -(directional + point + spot), ombres, HDR/ACES, exposition, émissif, bloom, culling. +Combines **all** effects: LOD primitives, procedural textures, lights +(directional + point + spot), shadows, HDR/ACES, exposure, emissive, bloom, culling. ```sh cargo run -p wsg-lib --example demo ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | -| `1` / `2` / `3` | Presets : face / côté / dessus | -| `+` / `-` | Exposition ×1.3 / ÷1.3 | -| `0` | Reset exposition | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | +| `1` / `2` / `3` | Presets: front / side / top | +| `+` / `-` | Exposure ×1.3 / ÷1.3 | +| `0` | Reset exposure | --- ## `bloom` — Post-process Bloom -Deux sphères émissives (orange intensité 2.0, bleue intensité 3.0) produisent un -halo visible. Le cube et le sol servent de référence (non-émissifs). +Two emissive spheres (orange intensity 2.0, blue intensity 3.0) produce a visible +halo. The cube and floor serve as reference (non-emissive). -Le bloom est un pipeline 4 passes GPU : threshold → blur H → blur V → composite. +Bloom is a 4-pass GPU pipeline: threshold → blur H → blur V → composite. ```sh cargo run -p wsg-lib --example bloom ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | | `+` / `-` | **Bloom threshold** +0.1 / −0.1 | | `[` / `]` | **Bloom intensity** +0.1 / −0.1 | | `I` / `O` | **Bloom radius** +0.5 / −0.5 | -| `E` / `Q` | Exposition ×1.3 / ÷1.3 | -| `0` | Reset exposition | +| `E` / `Q` | Exposure ×1.3 / ÷1.3 | +| `0` | Reset exposure | -### Ce qu'on voit +### What to observe -- **threshold bas** (0.0) : tout l'image "bloom" (effet très diffus). -- **threshold élevé** (2.0+) : seules les sphères émissives brillantes produisent du glow. -- **intensity 0.0** : pas de glow visible (même si le threshold extrait des pixels). -- **radius grand** (10+) : le glow s'étend sur une grande zone. +- **Low threshold** (0.0): the entire image "blooms" (very diffuse effect). +- **High threshold** (2.0+): only the bright emissive spheres produce glow. +- **Intensity 0.0**: no visible glow (even though the threshold extracts pixels). +- **Large radius** (10+): the glow spreads over a large area. --- ## `hdr` — HDR + Tone Mapping -Démontre le rendu HDR avec la courbe ACES Filmic. Trois objets : +Demonstrates HDR rendering with the ACES Filmic curve. Three objects: -- **Cube** : éclairage normal (aucun émissif) — référence LDR. -- **Sphère brillante** (émissif 3.0) : sans HDR, elle serait clampée à blanc. - Avec ACES, les highlights "roulent" doucement vers le blanc (rolloff). -- **Sphère sombre** (émissif 0.3) : reste sombre même à haute exposition. +- **Cube**: normal lighting (no emissive) — LDR reference. +- **Bright sphere** (emissive 3.0): without HDR, it would be clamped to white. + With ACES, highlights "roll off" smoothly toward white. +- **Dark sphere** (emissive 0.3): stays dark even at high exposure. ```sh cargo run -p wsg-lib --example hdr ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | -| `E` | **Exposition ×1.3** (plus clair) | -| `Q` | **Exposition ÷1.3** (plus sombre) | -| `0` | Reset exposition à 1.0 | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | +| `E` | **Exposure ×1.3** (brighter) | +| `Q` | **Exposure ÷1.3** (darker) | +| `0` | Reset exposure to 1.0 | -### Ce qu'on voit +### What to observe -- À exposition 1.0 : la sphère brillante est blanche mais avec des détails (rolloff ACES). -- À exposition haute (E×E×E) : la scène s'éclaircit, la sphère brillante reste blanche - (saturée), mais le cube gagne en détail. -- À exposition basse (Q×Q) : tout s'assombrit, la sphère brillante devient orangée - (les valeurs HDR > 1.0 sont compressées). +- At exposure 1.0: the bright sphere is white but with detail (ACES rolloff). +- At high exposure (E×E×E): the scene brightens, the bright sphere stays white + (saturated), but the cube gains detail. +- At low exposure (Q×Q): everything darkens, the bright sphere becomes orange + (HDR values > 1.0 are compressed). -> **Note** : le tone mapper est compilé dans le pipeline au build. Pour comparer -> ACES vs Reinhard, modifier `ToneMapper::Aces` → `ToneMapper::Reinhard` dans le source. +> **Note**: the tone mapper is compiled into the pipeline at build time. To compare +> ACES vs Reinhard, change `ToneMapper::Aces` → `ToneMapper::Reinhard` in the source. --- -## `emissive` — Matériaux Émissifs +## `emissive` — Emissive Materials -Cinq sphères alignées avec des intensités émissives croissantes : +Five spheres in a row with increasing emissive intensities: -| Sphere | Couleur | Intensité | Effet | -|--------|---------|-----------|-------| -| 1 | Gris | 0.0 | Aucune glow (référence) | -| 2 | Orange | 0.5 | Légère lueur | -| 3 | Jaune | 1.0 | Lueur visible | -| 4 | Vert | 2.0 | Glow HDR (au-delà de 1.0) | -| 5 | Bleu | 4.0 | Glow intense (saturation) | +| Sphere | Color | Intensity | Effect | +|--------|-------|-----------|--------| +| 1 | Gray | 0.0 | No glow (reference) | +| 2 | Orange | 0.5 | Slight glow | +| 3 | Yellow | 1.0 | Visible glow | +| 4 | Green | 2.0 | HDR glow (beyond 1.0) | +| 5 | Blue | 4.0 | Intense glow (saturation) | -Avec HDR, les intensités > 1.0 produisent un vrai "glow" (les valeurs dépassent -[0,1] en espace linéaire). Sans HDR, elles seraient clampées à blanc. +With HDR, intensities > 1.0 produce a true "glow" (values exceed +[0,1] in linear space). Without HDR, they would be clamped to white. ```sh cargo run -p wsg-lib --example emissive ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | -| `E` / `Q` | Exposition ×1.3 / ÷1.3 | -| `0` | Reset exposition | -| `C` | **Cycler le multiplicateur d'émissif** (1× → 2× → 0.5× → ...) | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | +| `E` / `Q` | Exposure ×1.3 / ÷1.3 | +| `0` | Reset exposure | +| `C` | **Cycle emissive multiplier** (1× → 2× → 0.5× → ...) | -### Ce qu'on voit +### What to observe -- La sphère 1 (intensité 0) est simplement éclairée par la lumière directionnelle. -- Les sphères 2-5 brillent de leur propre lumière, indépendamment de l'éclairage. -- `C` double ou réduit toutes les intensités en même temps (pour voir l'effet HDR). +- Sphere 1 (intensity 0) is simply lit by the directional light. +- Spheres 2-5 glow with their own light, independent of scene lighting. +- `C` doubles or halves all intensities simultaneously (to see the HDR effect). --- ## `shadow` — Shadow Mapping -Quatre objets (cube, sphère, cône, cylindre) sur un sol, éclairés par une lumière -directionnelle qui projette des ombres. La qualité des ombres est contrôlée par -`ShadowConfig` (taille de la shadow map, biais anti-acne). +Four objects (cube, sphere, cone, cylinder) on a floor, lit by a directional light +that casts shadows. Shadow quality is controlled by `ShadowConfig` (map size, anti-acne bias). ```sh cargo run -p wsg-lib --example shadow ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | -| `1` | Vue de face | -| `2` | Vue de côté | -| `3` | **Vue de dessus** (voir la forme des ombres clairement) | -| `L` | Changer la direction de la lumière (3 presets) | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | +| `1` | Front view | +| `2` | Side view | +| `3` | **Top view** (see shadow shapes clearly) | +| `L` | Change light direction (3 presets) | -### Ce qu'on voit +### What to observe -- Le cube tourne lentement → son ombre bouge sur le sol. -- La sphère a une transition ombre/lumière douce (terminateur lisse). -- Le cône produit une ombre triangulaire distincte. -- En vue de dessus (`3`), on voit la forme exacte des ombres projetées. -- La taille de la shadow map (1024 par défaut) détermine la résolution : - modifier `SHADOW_MAP_SIZE` en haut du fichier pour tester 256 (pixelisé) ou 2048 (net). +- The cube rotates slowly → its shadow moves on the floor. +- The sphere has a smooth shadow/light transition (soft terminator). +- The cone produces a distinct triangular shadow. +- In top view (`3`), you see the exact shape of projected shadows. +- Shadow map size (1024 default) determines resolution: + modify `SHADOW_MAP_SIZE` at the top of the file to test 256 (pixelated) or 2048 (sharp). --- ## `culling` — GPU Frustum Culling -Une grille de **15×15 = 225 cubes** est placée sur un grand sol. Le culling -GPU-driven (compute shader) détermine quels cubes sont visibles dans le frustum -de la caméra et zéro leurs draw args indirects — **zéro coût CPU**. +A grid of **15×15 = 225 cubes** is placed on a large floor. The GPU-driven culling +(compute shader) determines which cubes are visible in the camera frustum and zeros +their indirect draw args — **zero CPU cost**. ```sh cargo run -p wsg-lib --example culling ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra (regarder autour) | -| Molette | Zoom in/out | -| `R` | Reset (vue de dessus) | -| `1` | Vue de face (les cubes derrière sont culled) | -| `2` | Vue de côté | -| `3` | **Vue de dessus** (voir toute la grille) | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera (look around) | +| Wheel | Zoom in/out | +| `R` | Reset (top view) | +| `1` | Front view (cubes behind are culled) | +| `2` | Side view | +| `3` | **Top view** (see the full grid) | -### Ce qu'on voit +### What to observe -- En vue de dessus (`3`) : toute la grille 20×20 est visible. -- Orbiter à 90° : les cubes derrière la caméra **ne sont pas dessinés** (culled). -- Zoomer très près : seuls les cubes proches du plan de near sont rendus. -- Les cubes tournent lentement (phases décalées) → le culling est dynamique - (un cube peut entrer/sortir du frustum au cours d'une frame). +- In top view (`3`): the entire grid is visible. +- Orbit to 90°: cubes behind the camera **are not drawn** (culled). +- Zoom very close: only cubes near the near plane are rendered. +- Cubes rotate slowly (staggered phases) → culling is dynamic + (a cube can enter/leave the frustum during a frame). -> **Note** : le culling est activé via `AppBuilder::with_culling(true)`. Le modifier -> à `false` dans le source désactive le culling (tous les 400 cubes sont toujours -> dessinés, même hors écran). +> **Note**: culling is enabled via `AppBuilder::with_culling(true)`. Changing +> it to `false` in the source disables culling (all cubes are always drawn, even off-screen). --- ## `msaa` — MSAA 4× (Anti-aliasing) -Démontre l'anti-aliasing multi-échantillons : les arêtes des objets (cube, sphère) -sont lisses au lieu d'être "en escalier". La scène contient un cube (arêtes nettes), -une sphère (silhouette courbe) et un petit cube près de la caméra (aliasing maximal). +Demonstrates multisample anti-aliasing: object edges (cube, sphere) +are smooth instead of "stair-stepped". The scene contains a cube (sharp edges), +a sphere (curved silhouette), and a small cube near the camera (maximum aliasing). ```sh cargo run -p wsg-lib --example msaa ``` -### Touches +### Keys -| Touche | Action | -|--------|--------| -| Glisser (LMB) | Orbiter la caméra | -| Molette | Zoom | -| `R` | Reset caméra | -| `M` | Afficher le nombre d'échantillons | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | +| `M` | Show sample count | -### Pour comparer avec/sans MSAA +### To compare with/without MSAA -Supprimer la ligne `.with_msaa(4)` dans le source et recompiler : la scène est -identique, seules les arêtes diffèrent (escaler vs lisse). +Remove the `.with_msaa(4)` line in the source and recompile: the scene is +identical, only the edges differ (stair-stepped vs smooth). -> **Note** : MSAA est un réglage de build-time (allocation de textures multi-échantillons). -> Il fonctionne indépendamment de HDR : avec HDR, la texture MSAA est `Rgba16Float` -> et résout dans la texture HDR avant bloom/TM. +> **Note**: MSAA is a build-time setting (multisample texture allocation). +> It works independently of HDR: with HDR, the MSAA texture is `Rgba16Float` +> and resolves into the HDR texture before bloom/TM. --- -## `fog` — Brouillard de distance +## `fog` — Distance Fog -Démontre les 3 modes de brouillard : **linéaire**, **exponentiel**, **exponentiel²**. -La scène contient une rangée de cubes qui s'éloignent et des sphères dispersées sur -un grand plan au sol. Le brouillard fond les objets vers une couleur de fond, -créant l'illusion d'un monde infini. +Demonstrates the 3 fog modes: **linear**, **exponential**, **exponential²**. +The scene contains a row of cubes receding into the distance and scattered spheres +on a large floor plane. Fog blends objects toward a background color, +creating the illusion of an infinite world. ```sh cargo run -p wsg-lib --example fog --features "all-prims" ``` -**Touches** : `1` = linéaire, `2` = exp, `3` = exp², `4` = désactivé, `R` = reset. +**Keys**: `1` = linear, `2` = exp, `3` = exp², `4` = off, `R` = reset. -> Le brouillard est appliqué dans le shader fragment principal (après l'éclairage, -> avant le tone mapping). Il utilise la distance euclidienne du fragment à la caméra. +> Fog is applied in the main fragment shader (after lighting, before tone mapping). +> It uses the Euclidean distance from the fragment to the camera. --- -## `dof` — Depth of Field (bokeh cinématique) +## `dof` — Depth of Field (cinematic bokeh) -Démontre le flou de profondeur de champ : un objet au centre reste net tandis que -le premier et arrière-plan se flouent selon leur distance au plan de mise au point. -Crée un effet d'attention naturelle (type cinématique). +Demonstrates depth of field blur: an object at the focus plane stays sharp while +foreground and background blur according to their distance from the focus plane. +Creates a natural attention effect (cinematic style). -La scène contient un cube de focus au centre, des sphères en premier plan (proches) -et des cubes en arrière-plan (loin), sur un plan au sol. +The scene contains 20 cubes in a row along Z (z=3 to z=-25.5) and 5 spheres to the +sides, on a floor plane. Focus presets at 3m / 8m / 15m. ```sh cargo run -p wsg-lib --example dof --features "all-prims" ``` -**Touches** : `1` = cinématique, `2` = subtil, `3` = focus 2m, `4` = focus 10m, `5` = off, `R` = reset. +**Keys**: `1` = cinematic, `2` = subtle, `3` = focus 3m, `4` = focus 15m, `5` = off, `R` = reset. -> DoF opère en HDR linéaire (après bloom, avant tone mapping). Deux passes : -> CoC (depth → rayon de flou par pixel) puis blur disque 12-taps à rayon variable. +> DoF operates in linear HDR (after bloom, before tone mapping). Two passes: +> CoC (depth → per-pixel blur radius) then 12-tap disc blur with variable radius. --- -## `manual` — Workflow bas niveau +## `pbr` — PBR Metallic/Roughness + Normal Mapping -Démontre l'API **sans** la façade `App` : utilisation directe de `Context`, -`Renderer`, `PipelineCache`, `Mesh`, `Material`. Rend un quad coloré (unlit). - -Utile pour comprendre ce que la façade `App` encapsule. - -```sh -cargo run -p wsg-lib --example manual -``` - -Pas de touches — rendu statique (quad unlit, 4 couleurs). - ---- - -## `import` — Import de fichier OBJ - -Exemple **non graphique** : parse un fichier `.obj` et affiche les statistiques -(nombre de sommets, normales, UVs, indices, bounding box) sur stdout. - -```sh -# Avec un fichier : -cargo run -p wsg-lib --example import --features import-obj -- /path/to/model.obj - -# Sans argument (triangle de démonstration) : -cargo run -p wsg-lib --example import --features import-obj -``` - -Pas de touches — s'exécute et quitte. - ---- - -## `pbr` — PBR Metallic/Roughness + Normal Mapping (Étape 27) - -Démonstration du workflow PBR Cook-Torrance : GGX distribution + Smith visibility + -Schlick Fresnel + IBL hémisphérique + normal mapping. +Demonstrates the Cook-Torrance PBR workflow: GGX distribution + Smith geometry + +Schlick Fresnel + hemispheric IBL + normal mapping. ```sh cargo run -p wsg-lib --example pbr ``` -| Touche | Action | -|--------|--------| -| Drag (LMB) | Orbite caméra | -| Molette | Zoom | -| `R` | Reset caméra | +| Key | Action | +|-----|--------| +| Drag (LMB) | Orbit camera | +| Wheel | Zoom | +| `R` | Reset camera | -Scène : 6 matériaux PBR (métal miroir, plastique, rouillé, céramique, bump map, sol matte). -Le cube avec normal map montre des bumps procéduraux (sin wave). +Scene: 6 PBR materials (mirror metal, smooth plastic, rusty metal, ceramic, bump map, matte floor). +The bump-map cube shows procedural sin-wave surface detail. + +--- + +## `manual` — Low-level Workflow + +Demonstrates the API **without** the `App` facade: direct use of `Context`, +`Renderer`, `PipelineCache`, `Mesh`, `Material`. Renders a colored quad (unlit). + +Useful for understanding what the `App` facade encapsulates. + +```sh +cargo run -p wsg-lib --example manual +``` + +No keys — static render (unlit quad, 4 colors). + +--- + +## `import` — OBJ File Import + +**Non-graphical** example: parses a `.obj` file and prints statistics +(vertex count, normals, UVs, indices, bounding box) to stdout. + +```sh +# With a file: +cargo run -p wsg-lib --example import --features import-obj -- /path/to/model.obj + +# Without argument (demo triangle): +cargo run -p wsg-lib --example import --features import-obj +``` + +No keys — runs and exits.