Documentation · reference

What the engine hands a shader

Every name a material or a screen effect can read, for sprites, models and the whole screen, on both graphics backends.

A material is a small function you write: the engine runs it for every pixel (and, on a model, for every corner) and hands it what it knows about that place. This page lists all of it. The names are a promise: a shader written against them keeps working in later versions, so none is ever renamed. The engine's tests check that every name on this page exists on both graphics backends.

To see each input at work, every one drawing something you can see, open the Shader inputs example.

The hooks are written in WGSL for WebGPU (fragment, vertex) and in GLSL for WebGL2 (fragmentGlsl, vertexGlsl). Every name is the same in both; only the types are written the GLSL way (vec4 for vec4<f32>). If you build the material with the shader composer, it writes both for you.

In every shader: mu

A material's parameters arrive in mu. The engine's come first and always in the same order, then yours, by the name you gave them: uniforms: { charge: 0 } is read as mu.charge.

NameTypeWhat it is
mu.timef32Game seconds. timeScale speeds it up or slows it down, and a pause stops it.
mu.resolutionvec2<f32>The size of what is being drawn into, in pixels.
mu.progressf32Screen effects only: how far through its half a scene change is, from 0 to 1.
mu.phasef32Screen effects only: 0 while a scene change covers the screen, 1 while it uncovers it.

Outside a scene change, progress and phase are both 0. Those four names are taken: a parameter of your own cannot use them.

A parameter is one of four types: f32, vec2<f32>, vec3<f32> or vec4<f32>. The whole block holds 64 numbers, the engine's included. A vec3 or a vec4 starts on a multiple of four, so a single number just before one leaves a small gap. A material that declares more than fits is drawn with the built-in look, and the console says why.

Sprites, text and map layers

A material with shader: 'sprite2d' (the default) has one hook:

fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
    return color;
}

The same material works on a map layer without changing a word.

Models

A material with shader: 'mesh3d' has two hooks. Both are optional: the one you leave out does what the engine does.

fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32> {
    return pos;
}
 
fn effect(surface: vec4<f32>, ctx: FragContext) -> vec4<f32> {
    return vec4<f32>(surface.rgb * ctx.light + ctx.shine + ctx.emissive, surface.a);
}

That effect is the engine's own, word for word: a good place to start from.

What the corner hook is told:

VertContextTypeWhat it is
normalvec3<f32>Which way the corner faces, in the model's space.
uvvec2<f32>The picture's place.
colorvec4<f32>The colour painted on the corner, white if none.
tangent, bitangentvec3<f32>Which way across and down the picture runs, in the model's space.
uv2vec2<f32>A second picture coordinate: the file's own, or uv again.

What the colour hook is told:

FragContextTypeWhat it is
uvvec2<f32>The picture's place, already stretched if the material is affine.
normalvec3<f32>Which way the surface faces, in the world.
worldPosvec3<f32>Where the pixel is, in the world.
lightvec3<f32>The light that falls here, shadows and ambient included.
shinevec3<f32>The highlight.
emissivevec3<f32>The material's own glow, through its glow picture.
viewDirvec3<f32>From the surface towards the camera.
colorvec4<f32>The painted colour on its own. It is already in surface.
screenvec2<f32>Where on the screen, in game pixels, (0, 0) at the top left.
tangent, bitangentvec3<f32>Which way across and down the picture runs, in the world.
uv2vec2<f32>The second picture coordinate.
frontFacingboolFalse only on the back of a material with cull: 'front' or 'none'.
carryvec4<f32>What vertex wrote in carry, blended across the triangle. Zero if nothing.

light and shine are worked out at the corners and blended across the triangle, not worked out again for every pixel. That is how the consoles of the era lit a model, and it is the look the engine is after.

Helpers a model's shader can call:

Screen effects

An effect over the whole screen (usePostProcess({ fragment })) has the same hook as a sprite. color is the frame already read at uv.

HelperWhat it reads
sampleTexture(uv)The frame, pixel by pixel.
sampleTextureSmooth(uv)The frame, blended between pixels: for blurs, bends and smaller passes.
sampleInput(uv)The frame the effect was handed. It is only different from sampleTexture in an effect with passes.
sampleHistory(uv)What the effect showed last frame: black at first, white if the effect did not ask for it.
paletteSize(), paletteColor(i)The palette given to the effect: how many colours, and colour i.
lutSize(), lutTexel(x, y, slice, size), lutColor(rgb)The colour table given to it. lutColor grades a colour through it.

An effect reads one data picture: a palette or a colour table, never both.