This commit is contained in:
Jérôme Bousquié
2026-09-25 20:06:10 +02:00
parent 24fbafc810
commit d4c2d93fc5
31 changed files with 988 additions and 881 deletions
+53 -51
View File
@@ -1,70 +1,72 @@
# Examples
Sixteen examples live in [`lib/examples/`](../../lib/examples/), organized into
**four category folders** — [`meshes/`](../../lib/examples/meshes/README.md),
[`lights/`](../../lib/examples/lights/README.md),
[`cameras/`](../../lib/examples/cameras/README.md),
[`effects/`](../../lib/examples/effects/README.md) — each folder with its own
`README.md` (per-example details: keys, what to observe). All launch with
`cargo run -p wsg-lib --example <name>` (names are stable, run from the repo
root). They are **self-contained**: no assets on disk (procedural textures,
hard-coded geometries).
16 examples in **4 folders** (mirroring the topic folders of this documentation), covering the
full range of the engine — from a 2D quad to GPU-driven rendering.
| Folder | Example | What it shows | Corresponding page |
|--------|---------|---------------|--------------------|
| `meshes/` | `simple` | The minimal declarative workflow: a two-tone 2D quad, **unlit**, rendered automatically. The "15 lines, no wgpu" model | [Quickstart](quickstart.md), [Materials](materials.md) (§ unlit) |
| `meshes/` | `cube` | The 3D MVP: a textured (checkerboard) cube, lit (directional + point + spot), spinning | [Meshes](meshes.md), [Materials](materials.md), [Lights](lights.md) |
| `meshes/` | `pbr` | PBR metallic/roughness + normal mapping (6 materials) | [Materials](materials.md) |
| `meshes/` | `import` | OBJ file import (non-graphical, prints stats to stdout) | [Meshes](mesh.md) |
| `meshes/` | `manual` | The **advanced** workflow: `Context`/`Renderer`/`PipelineCache` driven by hand, without the `App` facade | below |
| `lights/` | `shadow` | Shadow mapping in isolation (4 objects on a floor) | [Shadows](shadows.md) |
| `lights/` | `shadow_test` | Isolated shadow mapping: a cube casts a PCF-softened shadow on the ground (`clear_lights` technique → caster at index 0) | [Shadows](shadows.md) |
| `lights/` | `spot_test` | Isolated spot (ambient nearly zero): the directed beam, the penumbra, the attenuation | [Lights](lights.md) |
| `lights/` | `emissive` | Emissive materials (intensities 0 → 4.0) + runtime exposure | [Emissive & exposure](emissive-exposure.md) |
| `cameras/` | `culling` | GPU-driven frustum culling: 15×15 grid, off-frustum cubes skipped (zero CPU cost) | [GPU-driven](gpu-driven.md) |
| `effects/` | `demo` | The full showcase: ground + 6 LOD primitives, textures, 3 lights, **shadows**, **orbital camera** on keyboard/mouse, HDR/ACES, bloom | [All pages](README.md) |
| `effects/` | `bloom` | Post-process bloom (threshold → blur → composite) | [Bloom](bloom.md) |
| `effects/` | `hdr` | HDR + tone mapping (ACES) + runtime exposure control | [HDR](hdr.md) |
| `effects/` | `msaa` | MSAA 4× (smooth edges vs stair-stepped) | [MSAA](msaa.md) |
| `effects/` | `fog` | Distance fog (3 modes: linear, exp, exp²) | [Fog](fog.md) |
| `effects/` | `dof` | Depth of field (cinematic bokeh, focus presets) | [effects README](../../lib/examples/effects/README.md) |
All examples are in [`lib/examples/`](../../lib/examples/README.md); each folder has its own
README (description + how to run): [`meshes/`](../../lib/examples/meshes/README.md),
[`lights/`](../../lib/examples/lights/README.md), [`cameras/`](../../lib/examples/cameras/README.md),
[`effects/`](../../lib/examples/effects/README.md).
## The `manual` workflow (advanced)
| Example | Folder | What it shows | How to run | Corresponding page |
|---------|--------|---------------|------------|--------------------|
| `simple` | meshes | A 2D quad with vertex colors, unlit mode (~30 lines) | `cargo run -p wsg-lib --example simple` | [Quickstart](quickstart.md), [Materials](meshes/materials.md) |
| `cube` | meshes | A rotating cube: point + spot light, checkerboard texture, procedural normal map, orbit/zoom | `cargo run -p wsg-lib --example cube` | [Meshes](meshes/meshes.md), [Materials](meshes/materials.md), [Lights](lights/lights.md) |
| `pbr` | meshes | A procedural PBR material (metal/roughness) + a checker diffuse | `cargo run -p wsg-lib --example pbr` | [Materials](meshes/materials.md) |
| `import` | meshes | Wavefront **OBJ** import (CLI: file path as argument, procedural cube as fallback) | `cargo run -p wsg-lib --example import --features import-obj -- model.obj` | [Geometry sources](meshes/sources.md) |
| `manual` | meshes | **Advanced**: the full manual workflow — buffers, pipelines, command encoding, no helpers | `cargo run -p wsg-lib --example manual` | [ARCHI_APP](../tech/ARCHI_APP.md), [FRAME_LOOP](../tech/FRAME_LOOP.md) |
| `shadow` | lights | Shadow mapping: the classic pitfall — the packed-index shadow caster | `cargo run -p wsg-lib --example shadow` | [Shadows](lights/shadows.md) |
| `shadow_test` | lights | Shadow mapping in isolation (cleared list → your light is index 0) | `cargo run -p wsg-lib --example shadow_test` | [Shadows](lights/shadows.md) |
| `spot_test` | lights | A single spotlight (cone + penumbra), ambient nearly zero | `cargo run -p wsg-lib --example spot_test` | [Lights](lights/lights.md) |
| `emissive` | lights | Emissive materials + HDR glow, runtime exposure (+/-/0 keys) | `cargo run -p wsg-lib --example emissive` | [Emissive & exposure](lights/emissive-exposure.md) |
| `culling` | cameras | **GPU-driven**: world matrices + indirect draws on the GPU, opt-in frustum culling, LOD | `cargo run -p wsg-lib --example culling` | [GPU-driven](cameras/gpu-driven.md) |
| `demo` | effects | The full showcase: all features combined (shadows, HDR, bloom, MSAA, fog, lights, orbital camera) | `cargo run -p wsg-lib --example demo` | [All pages](README.md) |
| `bloom` | effects | HDR + bloom: threshold → blur → composite | `cargo run -p wsg-lib --example bloom` | [Bloom](effects/bloom.md) |
| `hdr` | effects | HDR + tone mapping (ACES / Reinhard), emissive showcase | `cargo run -p wsg-lib --example hdr` | [HDR](effects/hdr.md) |
| `msaa` | effects | 4x MSAA anti-aliasing on the swapchain | `cargo run -p wsg-lib --example msaa` | [MSAA](effects/msaa.md) |
| `fog` | effects | Distance fog, 3 modes switchable at runtime (linear / exponential / exp²) | `cargo run -p wsg-lib --example fog` | [Fog](effects/fog.md) |
| `dof` | effects | Depth of field: Gaussian blur scaled by defocus distance, cinematic bokeh; focus presets 1-4 + continuous zoom | `cargo run -p wsg-lib --example dof` | [DoF](effects/dof.md) |
When the `App` facade doesn't fit (fine-grained loop control, integration into an existing
framework, experimentation), you bypass `App` and drive directly:
## The `manual` example: bypassing the helpers
- `Context` (*Manager* layer): GPU lifecycle — `Instance`/`Surface`/`Adapter`/`Device`/
`Queue`, `configure()` for the swapchain, `get_next_frame()`.
- `Renderer` (*Executor* layer): `render(view, mesh, material)` = one object per submission;
`present(frame)`.
- `PipelineCache`: `register_shader(id, path)` then `Material::new(format, id, &mut cache)`.
[`manual.rs`](../../lib/examples/meshes/manual.rs) renders a rotating cube with **no
high-level helper at all** — no `Scene`, no `Renderer` convenience API, no `AppHandler`
default `render()`. It shows the full pipeline:
The window and GPU are created in winit 0.30's `resumed()` callback (`run_app` +
`ApplicationHandler`), as in `app.rs`. The reference file is
[`manual.rs`](../../lib/examples/meshes/manual.rs); the two-layer architecture is detailed in
[ARCHI_APP](../tech/ARCHI_APP.md) and [FRAME_LOOP](../tech/FRAME_LOOP.md).
1. **`setup`**: manual creation of vertex/index buffers, bind groups, render/compute
pipelines, the swapchain.
2. **`render` (overridden)**: manual command encoding per frame (clear, draw, present) —
the handler controls **every** `CommandEncoder` operation.
3. **Uniforms written by hand** with `queue.write_buffer` (projection, view, model matrices).
> **Tip**: start with the declarative workflow. The manual workflow doesn't render more
> pixels — it gives more control over command encoding.
This is the reference for what the high-level API does under the hood, and the starting
point for features that don't exist yet in the engine (custom pipelines, post-processes,
custom compute). The technical details are in [ARCHI_APP](../tech/ARCHI_APP.md) and
[FRAME_LOOP](../tech/FRAME_LOOP.md).
Rule of thumb: **use `AppHandler` + `Scene` for everything the engine already does, and drop
to `manual` style only when you need what it doesn't** — the two styles can be mixed in the
same app (e.g. `Scene` for the scene, a manual post-process pass in `render()`).
## Adding your own example
Repo conventions (see [`lib/examples/README.md`](../../lib/examples/README.md)):
1. Create `lib/examples/<folder>/<name>.rs` — pick the folder it belongs to
(`meshes` / `lights` / `cameras` / `effects`).
2. Declare the `[[example]]` entry in `lib/Cargo.toml` (the folder structure means Cargo
no longer auto-discovers examples):
1. Create `lib/examples/<folder>/my_example.rs` (pick the matching category —
`meshes/`, `lights/`, `cameras/`, `effects/` — or add a new folder + README).
2. Declare it in `lib/Cargo.toml` — examples live in subfolders, so Cargo does
**not** discover them automatically:
```toml
[[example]]
name = "my_example"
path = "examples/<folder>/my_example.rs"
name = "<name>"
path = "examples/<folder>/<name>.rs"
```
3. Keep it **self-contained**: procedural textures, hard-coded geometries, no external assets.
4. Document the example in the folder's `README.md` (and here, `docs/user/examples.md`).
3. Required features: the base crate has no primitives by default in examples — declare
`required-features` if your example uses them (e.g. `required-features = ["prim-cube"]`).
4. Register it in the folder's README and in the table above.
5. Verify: `cargo build --workspace --examples` + run it.
## Links
- [User README](README.md) · [Quickstart](quickstart.md) · [Camera & input](camera-input.md)
- [User README](README.md) · [Quickstart](quickstart.md)
- [Root README](../../README.md) · [ROADMAP](../ROADMAP.md)