Texture layers¶
A texture layer is a raster the shader samples. The engine exposes a fixed set of 13 named roles. A shader opts in by declaring the binding; a shader that does not declare it is unaffected by that layer being set. One role set serves every shader, so a new look does not create a new material type.
The roles¶
| Role | Content | Typical format |
|---|---|---|
Height |
World heights for ray-marched sun shadows | R32Float, non-filterable |
Albedo |
Body-wide colour raster, such as a lunar mosaic | RGBA8, linear light |
Mineral |
Class-id or composition raster, tinted through a palette LUT | RGBA8 |
Surface |
Packed scalars: R roughness, G ambient occlusion, B rock density, A hazard | RGBA8 |
Normal |
Tangent- or world-space normals from DEM relief | RGBA8 |
ShadowCache |
Pre-baked sun visibility | R8Unorm |
ContinuationAlbedo |
Site-local albedo for a lunar-surface continuation shader | RGBA8 |
ContinuationSurface |
Site-local packed roughness/AO for a continuation shader | RGBA8 |
SurfaceAnnotations |
Sparse terrain-local vector annotations, read with textureLoad |
Typed |
DetailAlbedo |
Repeatable colour detail, independent of the georeferenced raster | RGBA8 |
DetailNormal |
Repeatable tangent-space normal detail | RGBA8 |
DetailSurface |
Repeatable packed scalars: R roughness, G ambient occlusion | RGBA8 |
Displacement |
Authored scalar displacement, separate from the runtime shadow heightfield | Scalar |
Height is a slot precisely because R32Float is non-filterable and cannot be
packed with filterable colour data. ShadowCache is a separate slot because it
has its own single-channel format and refresh cadence.
Authorable inputs¶
A USD material addresses the authorable roles through asset-typed inputs:
properties:
| USD input | Role |
|---|---|
inputs:albedo_map |
Albedo |
inputs:mineral_map |
Mineral |
inputs:surface_map |
Surface |
inputs:normal_map |
Normal |
inputs:detail_albedo_map |
DetailAlbedo |
inputs:detail_normal_map |
DetailNormal |
inputs:detail_surface_map |
DetailSurface |
inputs:displacement_map |
Displacement |
height_map and shadow_cache are deliberately not addressable from USD.
They are engine-filled by the horizon-shadow system; authoring them would fight
the engine writer.
ContinuationAlbedo, ContinuationSurface, and SurfaceAnnotations are also
runtime-owned. The DEM-to-globe continuation compositor clones the body look and
supplies the site-local maps. Terrain annotations publish sparse vector data
through the annotation path.
A connected inputs: property is skipped by the texture reader; it is fed by a
producer node, not by an authored file.
Spend a channel before a slot¶
Surface is one texture carrying four independent scalar layers:
| Channel | Layer |
|---|---|
| R | Roughness |
| G | Ambient occlusion |
| B | Rock density |
| A | Hazard |
A new scalar layer should take a spare channel, not a new slot. Four scalars for one binding is a 4× win on the scarce resource, and it costs one texture fetch instead of four.
DetailSurface follows the same rule for repeatable detail: R is roughness and
G is ambient occlusion. A single-channel roughness image requires
detail_ao_weight = 0.
Reach for a new slot only when the layer is genuinely not a scalar: a colour, a normal, something with its own format, or something with different filtering rules.
Formats and filtering¶
The role determines the prepared image contract:
| Family | Filtering |
|---|---|
Albedo, Mineral, ContinuationAlbedo |
Linear light, sRGB decode on load |
Surface, ContinuationSurface, DetailSurface |
Scalar channels averaged |
Normal, DetailNormal |
Vectors averaged and renormalized |
Height, ShadowCache, SurfaceAnnotations |
Dedicated native-image access |
Displacement |
Authored scalar range |
Prepared RGBA8 rasters are filtered inside the native asynchronous asset
pipeline. Each role has its own native root type and labeled image identity, so
one source file can supply different roles without settings races. Requests use
native asset paths and ImageLoaderSettings; decoding stays in Bevy's native
loader.
The CPU mip chain follows the GPU extent rule: every axis becomes
max(1, floor(previous / 2)). Non-power-of-two authored and derived maps are
valid inputs and remain renderable.
Albedo is not a grayscale orthophoto¶
An authored grayscale orthophoto is not intrinsic albedo. Its broad brightness
field contains acquisition illumination, and the runtime would light it a second
time. The asset pipeline has a distinct kind = "albedo" processor that removes
the low-frequency illumination field, anchors the remaining local variation at
an authored neutral regolith value, and sRGB-encodes the result.
A generic kind = "map" output is analysis or display data and must not be bound
directly as inputs:albedo_map. A calibrated reflectance raster may use the
texture pipeline when its colour-space contract is declared by the asset.
Detail maps¶
The canonical DEM material can combine georeferenced site rasters with optional repeatable material scans:
detail_albedo_mapmultiplies the site albedo by scan luminance relative todetail_albedo_meanin linear light.detail_normal_mapperturbs the existing DEM or procedural normal. It uses +X/-Z tangent axes and fades below the resolved pixel footprint.detail_surface_mapsupplies independent roughness and ambient-occlusion weights.
detail_tile_size_m defines one repeat in terrain-local metres. Coordinates
remain stable as tiles and the camera move, and explicit gradients select mip
levels across wrap boundaries. Detail maps never replace the DEM normal map or
change height queries and colliders.
All detail weights default to zero, so an existing scene does not request detail maps or change appearance.
Continuation roles¶
A body fragment shader that accepts site-local maps declares:
and the USD Shader prim authors the same identifier as info:wesl:interface.
The DEM collar supplies ContinuationAlbedo and ContinuationSurface while the
body-wide Albedo and Surface remain the globe appearance. The exterior
composes the site base colour with its authored raster weight before fading to
the body appearance.
This is visualization only. It never resamples, blends, edits, or replaces DEM heights, terrain queries, or colliders.
Per-tile quality¶
Terrain is tiled, and each tile carries its own ShaderLook, hence its own
texture handles. Per-place quality is therefore a matter of giving the near tile
a larger albedo and the far tile a smaller one. The same roles are used; only
the images differ. Tiles at the same quality still share one material.
This is the answer for "higher quality near the rover" and for "this landing site has a real orbital raster, the rest of the Moon does not."
When 13 roles are not enough¶
In order of cost:
- Channel-pack. Almost always the right answer for a scalar.
- Add a role. The set is a conservative choice, not a hardware wall. A new
role is a change to
TextureLayerplus a binding in the material; every existing shader ignores it unless it declares the binding. - Use a
texture_2d_array. One binding, N layers of the same kind and format. This converts the budget from per-layer to per-kind. - Virtual texturing or a clipmap. A fixed physical cache plus an indirection table. This is a serious streaming and eviction project; do not reach for it until the simpler options are exhausted.
Thirteen sampled roles remain within the portable sixteen-texture limit. Check the actual binding budget before adding a role that must run on every target.
Do not bake overlays into a texture¶
Bake the data. Derive the view.
Normals, heights, and DEM-derived slope are expensive and do not change when a user drags a slider: bake them. Hazard colours, traversability bands, and science-zone tints are views: derive them in the shader from a data layer, gated by uniforms.
Baking an overlay makes a slider drag a multi-second re-bake of every resident tile. It also freezes a presentation choice into the data, so the same raster cannot be re-interpreted for a different analysis.
Related sections¶
- Parameters — the uniform half of the contract
- USD materials — how texture inputs are authored
- Terrain — the layered terrain material
- Quality — renderer budgets and per-tile quality