Shader Graph
A material can go beyond the fixed PBR set through a shader graph: a
closed, loop-free node graph compiled host-side into WGSL.
A script or asset can never supply raw shader text.
Every graph is built from a fixed set of node kinds (Add, Lerp,
Fresnel, TextureSample, and others), and each node references only
earlier nodes in the same network, so a cycle cannot be constructed and
cost is bounded before the graph runs: at most 128 nodes and 4 texture
samples per network.
Structure
A graph has two independent networks.
The surface network computes the fragment-stage look, either Lit
(feeding a standard PBR pass) or Unlit (written straight to the output,
for beams, holograms, and other emissive looks a lighting pass cannot
produce).
The optional displacement network computes a vertex-stage position or
normal offset, for effects like curved beams or billboarding.
Public Inputs
A graph exposes up to 16 public inputs — values a prim can override
per-instance without recompiling the graph.
Two prims can share one compiled graph and still look different, and
because the compiled material:graph_data bytes are identical between
them, they dedupe in the blob store regardless of how many prims use them.
Authoring
A graph is written as an .hss (Hyper-Space Shader) file and referenced
from a prim’s material_graph attribute in .hsda:
// glow.hss — an unlit rim glow, tinted by public input 0
(
public_inputs: [Color((0.1, 0.6, 1.0, 1.0))],
surface: (
nodes: [
Fresnel(power: Const(Float(2.0))),
Lerp(a: Const(Color((0.0, 0.0, 0.0, 1.0))), b: Input(0), t: Node(0)),
],
output: Unlit((color: Node(1))),
),
)
// asset.hsda — this prim overrides input 0 to red
(attributes: (name: "red_glow", material_graph: (
path: "./glow.hss",
overrides: {0: Color((1.0, 0.0, 0.0, 1.0))},
)))