Files
wsg/docs/user/meshes/materials.md
T
Jérôme Bousquié e5f3636b42 examples: apply real texture assets to multi-mesh examples
- meshes/cube: procedural checkerboard -> uv_texture.jpg (8x8 UV grid)
- meshes/pbr: floor -> ground.jpeg, bump cube -> cave.jpg + caveNormal.jpg
  (normal map pre-encoded via sRGB OETF to cancel the GPU sRGB decode)
- lights/shadow: ground -> ground.jpeg, cube -> uv_texture.jpg
- effects/demo: ground -> ground.jpeg, cube -> uv_texture.jpg
- effects/fog: ground -> ground.jpeg (tiled 80x80), cubes -> stonewall.jpg
- effects/dof: ground -> ground.jpeg, cubes -> uv_texture.jpg
- cameras/culling: shared cube mesh -> uv_texture.jpg
- add lib/examples/assets/textures/ (19 assets, 6.5 MB)
- document assets + usage in examples READMEs, docs/user/examples.md,
  docs/user/meshes/materials.md (CARGO_MANIFEST_DIR pattern, sRGB caveat)
2026-09-26 10:49:13 +02:00

125 lines
5.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/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/meshes/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()` (clone them out of the
borrow before touching `app.scene` again — see the pattern in every textured example):
```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/effects/demo.rs).
For **file textures** (the pattern used by `cube`, `demo`, `shadow`, `fog`, `dof`,
`culling` and `pbr`), load from `assets/textures/` and resolve the path against
`CARGO_MANIFEST_DIR` so the example works from any working directory:
```rust
const TEXTURES: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/assets/textures");
let texture = Texture::from_file(
&device,
&queue,
"uv_atlas",
&format!("{TEXTURES}/uv_texture.jpg"),
)
.unwrap();
```
The sampler is `Linear` + `Repeat`, so textures tile automatically when UVs exceed
[0,1] (e.g. the 80×80 fog floor tiles `ground.jpeg`).
> **Normal maps**: `Texture` is always `Rgba8UnormSrgb`, so the GPU sRGB-decodes on
> sample. A normal map is *linear* data — pre-encode its channels with the sRGB OETF
> before upload so the round-trip is the identity. See `load_normal_map` in
> [`pbr.rs`](../../../lib/examples/meshes/pbr.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/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/lights.md) · [Examples](../examples.md)
- [Root README](../../../README.md) · [ARCHI_RENDU](../../tech/ARCHI_RENDU.md)