4.1 KiB
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), with an unlit mode for flat rendering.
1. Registering the shader
app.scene
.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)
.unwrap();
Note
:
STANDARD_SHADER_PATHpoints 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 and the
shaders/standard_shader.wgsl file).
2. Creating materials
// 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):
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 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():
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 and cube.rs.
Two conditions for a texture to show up:
- the material is created via
add_material_texture(otherwise the 1×1 white placeholder is bound — no visual effect, no regression); - the
Geometrycarries 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:
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) 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.