Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit61a33237e0

README.md

terrainSplat (material)

The eight-layer terrain surface. Blends three layer atlases — albedo, normal, mask — by the two RGBA weight textures the landscape was baked with.

You normally do not apply this material yourself

terrainSurface.forLandscape(data) instances it per landscape: the .terrainData's control textures bound as the weight maps, its rect as field_min / field_size, its packed sheets as the three atlases, and its layer table as the per-layer tints and tilings. What you apply is the handle that call returns — and a Terrain component does even that for you.

This asset is the same shader with defaults, so it is inspectable and it is what a hand-authored variant starts from. Applied by hand it has no weight textures and no field rect, so it renders layer 0 everywhere — an unbound stack looks unbound rather than looking broken.

Properties

PropertyUnitWhat it does
field_min, field_sizemetresthe rect the weight textures cover; zero size means no weight map
weights_lo_data, weights_hi_data—layers 0-3 and 4-7, normalised to sum to one
layer_atlas_albedo / _normal / _mask—4x2 atlases, layer N at (N mod 4, N div 4)
atlas_sizetexelsatlas dimensions, for the half-texel inset between cells
layerN_tint—multiplies layer N's albedo sample
layerN_sizemetresworld metres one repeat of layer N's texture covers
weight_sharpen—1 is the stack as evaluated; higher narrows every transition without moving it
height_blend—0 blends by weight alone; higher gives a contested pixel to whichever layer's mask height stands proudest
normal_strength—scales the blended tangent-space XY; 0 is flat
smoothness, metallic—scale the mask atlas's alpha and red

The mask atlas

Each cell is a mask MAP in the usual channel order: R metallic, G ambient occlusion, B height, A smoothness. White is the neutral one — no occlusion, a level height that leaves height_blend with nothing to reorder, and metallic and smoothness left to the two scalars.

The height channel is what makes a transition look like two materials meeting rather than a crossfade: where the weights call a tie, height_blend gives the pixel to the layer standing proudest there, so sand settles into the gaps between cobbles instead of fading evenly across their faces. Layers at equal height are attenuated equally, so a white atlas comes through unchanged.

Three atlases, not twenty-four slots

A slot per layer per map would put twenty-four sampled textures in one bind group, which is past what WebGPU guarantees a shader stage. The atlas keeps the cost at three whatever the layer count is. terrain.write packs the sheets when it bakes a landscape — a layer names its own albedo / normal / mask textures and they are resampled into that layer's cell — so the packing is not something an author does by hand.

A stack with no atlas at all renders its eight layerN_tint colours, which is enough to see every rule working.