Builtin Library
- ..
- materialGraph.module
- package.yaml496 B
- readme.md3.7 KB
readme.md
materialGraph
A material described as nodes and links, compiled to the engine's own WGSL surface DSL.
Authoring a material here means writing fn surface(input: FragmentData) -> PbrSurface and a properties.yaml. That is WGSL. A graph says the same thing
as a set of named nodes and a terminal, and the compiler writes the WGSL.
local materialGraph = require("@builtin::systems.materialGraph.materialGraph")
local graph = {
name = "veined_marble",
properties = {
{ name = "vein_color", type = "color", default = { 0.15, 0.15, 0.18, 1 } },
{ name = "scale", type = "float", default = 8 },
},
nodes = {
uv = { op = "uv" },
scaled = { op = "multiply", a = "uv", b = { op = "property", property = "scale" } },
ripple = { op = "sin", value = { node = "scaled", swizzle = "x" } },
veining = { op = "saturate", value = "ripple" },
vein = { op = "property", property = "vein_color" },
albedo = { op = "mix", a = { 0.9, 0.9, 0.88 }, b = { node = "vein", swizzle = "rgb" }, t = "veining" },
},
surface = {
albedo = "albedo",
roughness = 0.25,
metallic = 0,
},
}
local material = materialGraph.material(graph, { name = "marble" })
entity.spawn("pillar").component.add("Model", { model = "cube", material = material })
(A node can be written inline where an input is expected, as scaled.b does
above, or declared under its own name in nodes and referenced by that name.
Inline nodes are only reachable from the port they sit in.)
What is in a graph
| Key | What it holds |
|---|---|
name | The name of the shader asset the graph compiles to. |
properties | The material's parameters — what a material instance may set. Same declarations a hand-written properties.yaml carries: float, range, float2/3/4, color, int, bool, texture. |
nodes | A map of node name to { op = "...", <port> = <input>, ... }. |
surface | The terminal. Each key is a channel of the shading model; its value is an input like any port's. |
An input port takes a number, an array of 2-4 numbers, the name of another
node, { node = "<name>", swizzle = "rgb" }, or a node written inline. A
swizzle written beside any of the table forms reads part of what it resolves
to.
Types
Every node's type — float, vec2, vec3, vec4 — is worked out from its
inputs. A float meeting a vector is broadcast to the vector's width. Two
vectors of different widths are refused, naming the node: there is no reading
of that the author can have meant, and a silent guess is a material that is
subtly wrong rather than one that failed to compile.
materialGraph.nodeTypes() lists every operation with its ports and result,
and materialGraph.surfaceChannels() lists the terminal's channels.
What it compiles to
compile is pure: no asset, no renderer, no GPU. It returns the shader.wgsl
and properties.yaml text, the nodes it emitted, and the nodes the terminal
could not reach (declared and type-checked, but not in the shader).
install writes those two files as a real .shader asset, and material
installs and then makes a material wearing it. Both produce ordinary assets,
readable and hand-editable afterwards. Running either again over the same name
is how a graph is iterated on: the shader is rewritten, and material also
brings the material onto the rewritten shader's properties and applies
opts.values over it.
The shader itself is compiled at the next frame boundary, the way any shader
edit is. asset.ref(name, "shader"):compileStatus() is what reports that
outcome.