Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

materialGraph

Compiles a material node graph into the engine's WGSL surface DSL.

local materialGraph = require("@builtin::systems.materialGraph.materialGraph")
CallWhat it does
compile(graph, name?)Pure. Returns { name, wgsl, properties, nodes, unused } — the two files a .shader asset holds, the nodes emitted, and the nodes the terminal could not reach.
check(graph)compile with the error handed back: (true, result) or (false, message).
install(graph, opts?)Writes the compiled graph out as a real .shader asset. opts.name names it; opts.into places it as it is created, and installing again writes wherever the asset already is.
material(graph, opts?)Installs, then makes a material wearing the shader. opts.values sets the material's own property values — on the first call and on every later one, so calling it again is how a graph is iterated on.
nodeTypes()Every operation, with the inputs a node of it is written with, which of those it cannot be written without, its result type and a line of prose.
surfaceChannels()Every channel of the terminal, and the shading-model field it drives.

Inputs

A port takes any of:

Written asMeans
0.5A float constant.
{ 1, 0.5, 0.25 }A vector constant, 2 to 4 components.
"other"The output of the node named other.
{ node = "other", swizzle = "rgb" }Part of that node's output.
{ op = "fresnel", power = 3 }A node written where it is used.

swizzle reads part of whatever the table it is written in resolves to — a node reference, an inline node, or a constant vector. A table carrying both an op and a node is refused: a port says where its value comes from once.

Types

float, vec2, vec3, vec4. A node's type is worked out from its inputs. In an arithmetic node the widest input decides the result and a float input is broadened to it; two inputs of different vector widths are refused, naming the node. At the terminal a float broadens into a colour channel, and a wider value is refused with a note to swizzle it.

Every number a graph carries is written into the shader as an f32, so one past what an f32 holds is refused where it is written.

Operations

GroupOps
Sourcesuv lightmapUv affineUv worldPosition worldNormal worldTangent viewDirection viewPosition vertexColor time
Valuesconstant property
TexturessampleTexture normalMap
Arithmeticadd subtract multiply divide min max pow step mix clamp smoothstep
Per-componentabs floor ceil fract sign sin cos sqrt exp log saturate oneMinus negate normalize
Vectorlength distance dot cross combine
Shadingfresnel

Properties

graph.properties holds the same declarations a hand-written properties.yaml carries: float, range (with min / max), float2, float3, float4, color, int, bool and texture. A property node reads one, and sampleTexture / normalMap sample a texture one through the texture and sampler pair the engine generates for it. int and bool are converted where they are read, so they compose with everything else.

A texture declaration becomes a global of its own name in the interface the engine generates, so s, input and material — the names the generated body already stands on — and anything starting with mg_ are refused as texture names.

The terminal

graph.surface names channels of the shading model:

albedo normal metallic roughness emissive occlusion alpha alphaCutoff ior reflectance clearcoat clearcoatRoughness anisotropy transmission thickness attenuationColor attenuationDistance sheenColor sheenRoughness iridescence iridescenceThickness iridescenceIor subsurfaceColor subsurfaceRadius fibreColor fibreSpecular fibreShift fibreRoughness

A channel left unset keeps the engine's own neutral default, so a graph names only the channels it has something to say about.