Shader composer
Volver a la referencia

Shader composer

Writing a shader as typed values, and the node graph a .shader file holds.

74 símbolos

compileShaderfunctionnacatamalon

compileShader(graph: TShaderGraph<S>) => TCompiledShader<S>

Turns a composed graph into the fields a material takes, in both languages.

The result spreads straight into createMaterial, and whichever backend draws it takes the half it speaks. Nothing at the call site names a backend, and that is the point: the values are typed, so the second language is a second walk over the graph rather than a translation, and the two halves cannot drift apart because nobody wrote either.

On the way it works out anything used twice only once, reads each leaf the way its stage offers it (and refuses one read where it does not exist), gathers every parameter into uniforms, and writes each noise function once.

The family comes back in the type, so the result spreads into a material for sprites or for models and the material knows which it is.

A sprite shader needs color and has no vertex stage. A model shader takes color, position or both, and a stage left out keeps the engine's own. color may be a vec3 (alpha 1) or a vec4; position is a vec3.

Parámetros

graphTShaderGraph<S>

The family, and the results.

Ejemplo

const Level = () => {
    const glow = compileShader({
        shader: 'mesh3d',
        color: composerMix(composerSurface(), composerVec4(0.3, 0.7, 1, 1), composerFresnel(3)),
        position: composerAdd(composerVertexPosition(), composerMul(composerVertexNormal(), composerUniform('swell', 0.1))),
    });
    const material = createMaterial({ tint: getColor('#e08a3a'), ...glow });

    createMesh({ geometry: useIcoSphereGeometry(), material });

    return createScene();
};
Ver el código · shader_composer/compile_shader.ts:120

compileShaderGraphfunctionnacatamalon

compileShaderGraph(doc: TShaderGraphDoc, src: string) => TParsedShader

Compiles a .shader file into what a material needs: its hooks in both languages, its parameters and their types. The same shape reading a .wgsl gives, so nothing that uses a shader file can tell the two apart.

It writes no shader code of its own. It turns the file into the same values the composer functions build and hands them to compileShader, so a graph drawn on a canvas and the same graph written in code cannot drift, and every check the composer functions make applies to a wired graph too.

Only what reaches an output is built: a node nothing is wired to adds nothing, so a canvas with a few loose nodes on it still compiles. A loop is caught and named. An output left unwired keeps the engine's own for that stage, so a new file is valid and its material draws with the engine's shader until something is connected.

Parámetros

docTShaderGraphDoc

The graph, from parseShaderGraph.

srcstring

Where it came from, for the error messages.

Ejemplo

const src = '/shaders/glow.shader';
const parsed = compileShaderGraph(parseShaderGraph(await (await fetch(src)).text(), src), src);
Ver el código · shader_composer/compile_shader_graph.ts:42

composerFbmfunctionnacatamalon

composerFbm(pos: TComposerNode, octaves: number) => TComposerNode

Layers of noise, each twice as fine and half as strong as the one before (f32): the detailed, natural kind, for terrain, smoke or lava. octaves is written into the shader, so it is a small fixed number (4 unless you say). Takes a vec2 or a vec3.

Parámetros

posTComposerNode

Where to read it: a vec2 or a vec3.

octavesnumber

How many layers. Default 4.

Ver el código · shader_composer/noise.ts:140

composerFresnelfunctionnacatamalon

composerFresnel(power: TComposerInput) => TComposerNode

The rim term, from 0 where the surface faces the camera to 1 where it turns away (f32): the start of a rim light, a hologram or a force field. A higher power pulls the rim in to the silhouette.

It reads which way the surface faces and where the camera is, so it only exists in a model's colour stage, and the compiler says so anywhere else.

Parámetros

powerTComposerInput

How tightly the rim hugs the silhouette. Default 3.

Ver el código · shader_composer/effects.ts:24

composerLightfunctionnacatamalon

composerLight() => TComposerNode

How much light reaches this point, and of what colour (vec3).

It is handed over on its own so a graph can light things its own way, in hard bands or with a rim. The engine's own look is the surface times this, plus composerEmissive. The light is worked out at the corners and blended in between, as the consoles of the era did. Models only, colour stage.

Ver el código · shader_composer/inputs.ts:95

composerModvariablenacatamalon

composerMod: TComposerBinaryOp

The remainder the way shader authors expect it, x - y * floor(x / y), which always takes the sign of y. Built from the pieces above, because WGSL's own % keeps the sign of x instead and the two backends would disagree on negative numbers. A number as y spreads over x.

Ver el código · shader_composer/math.ts:426

composerPipefunctionnacatamalon

composerPipe(seed: TComposerInput, steps: TComposerStep[]) => TComposerNode

Passes a value through a list of steps, left to right, so a long expression reads in the order it happens instead of from the inside out.

composerPipe(x, composerMul(2), composerAdd(1), composerSin) reads "take x, double it, add one, then the sine", and is exactly composerSin(composerAdd(composerMul(x, 2), 1)). Any one-operand function is a step as it is, and the two-operand ones that put the value on the left (composerAdd, composerMul, composerPow...) become one when given a single operand. For anything else, a small function (n) => ... is a step too.

Parámetros

seedTComposerInput

The value to start with.

stepsTComposerStep[]

What to do to it, in order.

Ejemplo

// 0.5 + 0.5 * sin(uv.y * lineCount + time * speed)
const bands = composerPipe(
    composerY(composerUv()),
    composerMul(composerUniform('lineCount', 120)),
    composerAdd(composerMul(composerTime(), composerUniform('speed', 6))),
    composerSin,
    composerMul(0.5),
    composerAdd(0.5),
);
Ver el código · shader_composer/pipe.ts:34

composerSimplexNoisefunctionnacatamalon

composerSimplexNoise(pos: TComposerNode) => TComposerNode

Smooth noise at a position (f32, 0 to 1): the usual source of anything organic, like clouds, flowing water or a dissolve mask. Takes a vec2 or a vec3. Scale the position to change how busy it is, and add composerTime() to move it.

Parámetros

posTComposerNode

Where to read it: a vec2 or a vec3.

Ver el código · shader_composer/noise.ts:125

composerSurfacefunctionnacatamalon

composerSurface() => TComposerNode

The colour the engine would have drawn here (vec4): the picture, already read and already tinted (and on a model, painted by its corners' colour), before any light. Where most colour effects start. Colour stage only.

Ver el código · shader_composer/inputs.ts:48

composerSwizzlefunctionnacatamalon

composerSwizzle(value: TComposerNode, mask: string) => TComposerNode

Picks components of a vector by their letters ('bgr', 'xy'). The number of letters, one to four, is the size of the result, and a single letter gives a plain number.

The letters come from one set, xyzw or rgba, never both in one mask, and each must exist in the value it reads: both are checked here rather than left to the card.

Parámetros

valueTComposerNode

The vector to read.

maskstring

One to four letters from xyzw or rgba.

Ver el código · shader_composer/constructors.ts:105

composerUniformfunctionnacatamalon

composerUniform(name: string, value: number | number[]) => TComposerNode

A parameter with a name, which the game can change while it runs.

What value looks like sets its type (a number, or a list of two, three or four) and is what it starts at. compileShader gathers every parameter of the graph into the material's uniforms, so writing material.uniforms.<name> moves it on the next frame. time and resolution are taken: the engine writes those, and they are read with composerTime and composerResolution.

Parámetros

namestring

What the game calls it: material.uniforms.<name>.

valuenumber | number[]

What it starts at. A number, or a list of two, three or four.

Ver el código · shader_composer/inputs.ts:173

composerVertexColorfunctionnacatamalon

composerVertexColor() => TComposerNode

The colour painted on the model's corners (vec4), smeared across each triangle. Models only, in either stage.

It is already part of composerSurface. On its own it is for using the paint as something other than colour: how much a corner sways in the wind, or how much of a second picture shows through, which is how the era blended grass into a dirt path without a second texture pass.

Ver el código · shader_composer/inputs.ts:143

parseShaderGraphfunctionnacatamalon

parseShaderGraph(source: unknown, src: string) => TShaderGraphDoc

Reads a .shader file and checks its shape.

It takes the text of the file or JSON already parsed, because both callers exist: a game loading it has text in hand, and an editor holds a graph it has just changed and wants checked before it writes it. A broken shape (a node with no id, a place that is not two numbers, an unknown family) is refused here, naming the node. What the graph means (a kind of node nobody knows, a wire into an input that does not exist, a loop) is compileShaderGraph's, which knows the catalogue.

Parámetros

sourceunknown

The file's text, or its JSON.

srcstring

Where it came from, for the error messages.

Ejemplo

const graph = parseShaderGraph(await (await fetch('/shaders/glow.shader')).text(), '/shaders/glow.shader');
Ver el código · shader_composer/document.ts:164

SHADER_GRAPH_FORMATvariablenacatamalon

SHADER_GRAPH_FORMAT: 1

The version of the .shader format. It only goes up for a change an older reader could not survive; a new optional field does not move it.

Ver el código · shader_composer/document.ts:19

TCompiledShadertypenacatamalon

What compileShader gives back: exactly the fields createMaterial takes, so it is spread straight in, createMaterial({ ...compiled }).

A stage left at the engine's own is missing, the same as not writing it in createMaterial. uniforms holds every parameter found in the graph, ready to be changed while the game runs.

Propiedades

shaderS
fragmentopcionalstring

The colour hook in WGSL, missing when the graph has no color.

vertexopcionalstring

The vertex hook in WGSL, missing when the graph has no position.

fragmentGlslopcionalstring

The same two hooks in GLSL, always, never on request.

That is what makes a composed shader work on both backends by construction rather than when somebody remembers to ask. The values are typed, so the second language is a second walk over the same graph and not a translation of the first one's text.

vertexGlslopcionalstring
Ver el código · shader_composer/types/t_shader_graph.ts:34

TComposerNodetypenacatamalon

One value in a composed shader: what kind of value it is, and how to build it from the values it depends on.

It is plain data, never a closure. kind picks how it is written out, params carries its literal operands and deps the values it is made from. That is what lets a graph be written to JSON and read back, the same rule every other piece of state in the engine follows.

Never built by hand: the composer* functions fill these fields consistently and check their operands as they go.

Propiedades

kindstring

How it is written out: literal, input, uniform, texture, construct, swizzle, binop or call.

typeTUniformType

What it evaluates to. Every operation checks its operands against this.

depsTComposerNode[]

The values it is made from, in argument order.

paramsopcionalnumber | string[]

Literal operands: a number, an operator, a swizzle mask or the name of the function called.

inputopcionalTInputKind

For a leaf, which value of the running shader it reads.

uniformopcional{ name, value }

For a parameter, the name the shader reads it by and the value it starts at.

helpersopcionalTHelperDef[]

Functions this node needs written at the top of the shader.

Ver el código · shader_composer/types/t_composer_node.ts:63

TGraphCommentDoctypenacatamalon

A note on the canvas: a titled rectangle behind the nodes, for grouping part of a graph and saying what it is for.

It has an array of its own rather than being a kind of node, because it is not one: no wires, no value, no place in the graph. As a node, the compiler, the type check and the previews would each need an exception for the one entry that is not part of the shader.

Propiedades

idstring
textstring

The note. May be empty while it is being written.

pos[number, number]

Top-left corner, in the same space as a node's pos.

size[number, number]

Width and height: a note is resized, a node is not.

coloropcional[number, number, number]

An accent, [r, g, b] from 0 to 1. Left out, the editor picks.

Ver el código · shader_composer/types/t_shader_graph_doc.ts:87

TGraphNodeDoctypenacatamalon

One node in a saved graph: which kind of node it is, where it sits on the canvas, and its literals.

It does not describe itself. Its inputs, outputs and code all come from the catalogue, looked up by type, so a file stays small and a node learns something new when the catalogue changes rather than when every file is rewritten.

pos belongs to the editor and is in the file on purpose, like a box's place in a scene: how a graph is laid out is part of what it says to whoever reads it.

Propiedades

idstring
typestring

Which entry of the catalogue this is.

pos[number, number]

Where it sits on the canvas, [x, y].

paramsopcionalRecord<string, TParamValue>

Its literals. One left out takes the catalogue's value.

Ver el código · shader_composer/types/t_shader_graph_doc.ts:28

TGraphTargettypenacatamalon

TGraphTarget: Exclude<TMaterialShader, 'post'>

The families a graph file can be for: the two that draw things, deliberately not every family a material has.

'post' is missing because no node writes a screen-wide effect, so a graph claiming it would compile to nothing. Spelling it as its own type makes that gap a compile error in the one place that could open it, and the reader refuses the same value, so the two cannot disagree.

Ver el código · shader_composer/types/t_shader_graph_doc.ts:73

THelperDeftypenacatamalon

A function a node needs written once at the top of the shader (the noise functions, for instance), kept only once however many nodes ask for it.

Both languages ride on the node rather than living in a table per language looked up by name. A helper exists because a node needs it, and keeping the two apart would let a graph ask for a helper one language never defined: a failure that shows up as a shader that does not compile in somebody's browser, rather than as a missing entry while writing it.

Propiedades

namestring
wgslstring
glslstring
Ver el código · shader_composer/types/t_composer_node.ts:42

TInputKindtypenacatamalon

TInputKind: 'uv' | 'time' | 'resolution' | 'surface' | 'worldNormal' | 'worldPos' | 'viewDir' | 'light' | 'emissive' | 'vertexPosition' | 'vertexNormal' | 'vertexColor'

A value a leaf reads from the running shader: a function argument (uv, pos), one of the values the engine writes for every material (time), or what the model's colour hook is told about the place it is colouring (worldNormal, light).

Each one is only there in some stages, and the compiler refuses a leaf read where it does not exist (light in a vertex graph, say) instead of emitting code the card then rejects.

Ver el código · shader_composer/types/t_composer_node.ts:15

TShaderGraphtypenacatamalon

What compileShader is handed: the family the shader is for, and up to two results.

color is what the colour hook returns and position is where the vertex hook moves a corner to (models only). Leave one out and that stage keeps the engine's own.

The family is kept in the type, so what comes out is known to be a sprite's or a model's.

Propiedades

shaderS
coloropcionalTComposerNode
positionopcionalTComposerNode
Ver el código · shader_composer/types/t_shader_graph.ts:17

TShaderGraphDoctypenacatamalon

A .shader file: a shader drawn as nodes on a canvas.

It sits next to a hand-written .wgsl and next to a graph composed in code, and all three end up as the same thing, so a material cannot tell which one it was given. The file holds no shader source in any language, only nodes and wires as plain JSON.

target means what @shader means at the top of a .wgsl: it picks the hooks, so it is not a preference but part of what gets compiled.

Propiedades

formatnumber
kind'shadergraph'
commentsopcionalTGraphCommentDoc[]

The notes on the canvas. A graph written by hand or by a tool has none, and then the field is not there at all: it is a graph nobody annotated, not a missing field.

Ver el código · shader_composer/types/t_shader_graph_doc.ts:121