Shaders¶
A shader look in LunCoSim is data, not a Rust material type. A WESL source asset declares a parameter block and, optionally, a set of named texture layers. A USD material selects that source and authors its inputs. The renderer reflects the source, packs the parameters, binds the textures, and builds the pipeline.
This is why a new look is usually a new .wesl file and a material network, not
a new engine feature. Rust hardcodes no parameter names, ranges, or defaults.
The two halves¶
| What it is | Where it lives | Capacity | |
|---|---|---|---|
| Parameter | A scalar or vector uniform the shader reads | One opaque 512-byte uniform block | 128 f32 scalars or 32 vec4s, subject to layout alignment |
| Texture layer | A raster the shader samples | A bind-group entry | 13 named roles |
Parameters are cheap and open-ended. Texture slots are the scarce resource. Most "I need another layer" instincts should be spent on a packed channel before a new slot.
The data flow¶
- A
UsdShadenetwork binds a material to a prim. - The material's
Shaderprim names a WESL fragment source and, optionally, a vertex source. - The WESL source declares
struct Materialat@group(2) @binding(0)and//!@annotations. - The renderer reflects the struct into a
ParamSchemaand the annotations into Inspector metadata. - The USD projection publishes a
ShaderLookwith the source paths, parameter values, and texture handles. - The render binder validates the stage contract, packs the parameter block, binds the textures, and caches the material by look key.
A material is rejected visibly if the source is missing, the stage contract is
invalid, or a texture cannot load. Rust does not substitute StandardMaterial
or a guessed shader.
What a shader owns¶
| Fact | Authored as | Consumed by |
|---|---|---|
| Surface appearance | WESL fragment entry point | Render pipeline |
| Parameter set | struct Material and annotations |
Reflection and Inspector |
| Texture roles | Declared bindings | Material binder |
| Vertex deformation | Optional WESL vertex entry point | Mesh and shadow passes |
| Material selection | USD UsdShade network |
USD projection |
| Parameter values | USD inputs:<name> |
ShaderLook |
| Renderer budgets | RenderingQualitySettings |
All GPU-backed views |
A shader does not own scene composition, physics, mission policy, or the renderer-wide quality preset.
Sharing¶
Identical looks share one material and one bind group. The binder caches by the look key, which quantises floats so a rounding error cannot mint a second material. Vary a parameter per instance and you get a material per instance: a draw call and a bind group each. If instances must differ, bucket the value first.
Per-tile quality is a natural exception: terrain tiles carry their own look and texture handles, so a near tile can use a larger albedo than a far tile. Tiles at the same quality still share a material.
Reading this chapter¶
| Section | Question it answers |
|---|---|
| Parameters | How does a shader declare and receive its uniforms? |
| Texture layers | Which named rasters can a shader sample? |
| USD materials | How does a material bind a source and its inputs? |
| Terrain | How does the layered terrain material work? |
| Quality | Which renderer budgets change with the quality preset? |
| Authoring | How do I write, validate, and debug a shader look? |
Practical rules¶
- Declare every parameter in
struct Material; do not rely on undeclared uniforms. - Use
//!@ui,//!@default, and//!@engineto make the schema readable. - Keep the vertex stage's
Materiallayout identical to the fragment stage's. - Pack scalar layers into channels before consuming another texture slot.
- Bind textures and parameters through the composed
UsdShadenetwork. - Treat a rejected material as an authoring error, not as a reason to add a fallback.
- Measure rendering cost separately from a deterministic recording run.