Files
wsg/docs/user/materials.md
T
Jérôme Bousquié 5ae978da23 doc
2026-09-21 10:07:32 +02:00

101 lines
4.1 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.
# Materials & textures
A **`Material`** describes a mesh's appearance: it references a shader (by id) and
optionally a **diffuse texture**. Several materials pointing at the same shader share the
same compiled GPU pipeline (the `PipelineCache` held by the scene).
The engine ships a single shader: **`standard`** — multi-light Phong lighting (see
[Lights](lights.md)), with an **unlit** mode for flat rendering.
## 1. Registering the shader
```rust
app.scene
.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)
.unwrap();
```
> **Note**: `STANDARD_SHADER_PATH` points to an optional file on disk; if it is missing
> (the normal case for the embedded library), loading falls back to the shader **embedded at
> compile time** (`include_str!`, byte-identical). The fallback message you may see is
> therefore **expected and harmless**.
For a custom shader: register your `.wgsl` file path under an id of your choice (it must
expose the same bind groups as `standard` — frame @0, object @1, texture @2, shadow @3 — see
[ARCHI_RENDU](../tech/ARCHI_RENDU.md) and the
[`shaders/standard_shader.wgsl`](../../lib/src/shaders/standard_shader.wgsl) file).
## 2. Creating materials
```rust
// Textureless material: the color comes from per-vertex colors (or white by default).
app.scene.add_material_shader("mat", "standard").unwrap();
// Textured material: the texture must first be registered in the scene (below).
app.scene.add_material_texture("mat_textured", "standard", "my_texture").unwrap();
```
Binding a material to a mesh happens at mesh creation (see [Meshes](meshes.md)):
```rust
app.scene.create_mesh("cube_mesh", cube(1.0), Some("mat_textured")).unwrap();
```
A mesh created with `material = None` is rendered with the scene's **default material**
(`standard`, built once then cached) — that is the behavior of the
[`simple`](../../lib/examples/simple.rs) example.
## 3. Diffuse textures
`Texture` is a GPU image in `Rgba8UnormSrgb` (linear sampler, repeat addressing).
Four constructors:
| Constructor | Usage |
|--------------|-------|
| `Texture::from_rgba8(device, queue, w, h, rgba, label)` | raw RGBA8 bytes (procedural) |
| `Texture::from_bytes(device, queue, label, bytes)` | encoded data (PNG/JPEG… via the `image` crate) |
| `Texture::from_file(device, queue, label, path)` | image file on disk |
| `Texture::white_placeholder(device, queue)` | 1×1 white — used internally when a material has no texture |
You get `device`/`queue` in `setup()` via `app.context()`:
```rust
let (device, queue) = {
let ctx = app.context();
(ctx.device.clone(), ctx.queue.clone())
};
let texture = Texture::from_rgba8(&device, &queue, 8, 8, &my_rgba, "checker").unwrap();
app.scene.add_texture("checker_texture", texture).unwrap();
app.scene.add_material_texture("ground_mat", "standard", "checker_texture").unwrap();
```
The exact snippet (8×8 checkerboard + stripes generation) is in
[`demo.rs`](../../lib/examples/demo.rs) and [`cube.rs`](../../lib/examples/cube.rs).
Two conditions for a texture to show up:
1. the material is created via `add_material_texture` (otherwise the 1×1 white placeholder
is bound — no visual effect, no regression);
2. the `Geometry` carries **UVs** (`.with_uvs(…)`). Without UVs, sampling is constant.
The procedural primitives (`uv_sphere`, `cube`, …) already provide them.
## 4. Unlit mode (flat / 2D rendering)
"Flat" rendering (vertex colors as-is, no lighting) is a **renderer switch**, not a material:
```rust
app.renderer_mut().set_unlit(true); // in setup()
```
This is the mode of the `simple` example (2D quad). In this mode the scene's lights are
ignored; per-vertex colors (or white) are rendered directly. 2D is a special case of 3D:
the single `standard` pipeline serves both.
> `clear_lights()` (see [Lights](lights.md)) gives a similar result but keeps the lit
> pipeline: only ambient stays active. Use it when you want to "turn off the lights" without
> switching to unlit.
## Links
- [User README](README.md) · [Meshes](meshes.md) · [Lights](lights.md) · [Examples](examples.md)
- [Root README](../../README.md) · [ARCHI_RENDU](../tech/ARCHI_RENDU.md)