Builtin Library
README.md
shader
An author's own compute shaders as graph nodes. A .computeShader written for
anything else runs inside a graph unchanged: its bindings are the geometry's
columns, its params: are the node's knobs, and what it writes comes out as
columns every op after it reads.
procgen.searchNodes("", { pack = "shader" }) lists the family and procgen.describeNode("op.<id>")
describes any one of them.
Ops
shader.compute— run a.computeShaderover a geometry's elements, one invocation per element. A buffer binding is the column of its own name, or the onebindnames:access: readreads it,access: read_writewrites it and makes it when the domain has none. The shader'sparams:take their values fromuniforms, where each can be animated or wired, andcount,timeandframeare filled in when the shader declares them.shaderis a reference, writtenasset.resolve("<identity>", "computeShader"); a name alone is refused. The same shader placed as two nodes reads each node's own columns and values.sim.shader— a solver whose step is a.computeShader: the domain's columns are its state, the shader runs once per element at every fixed step writing itsread_writebindings in place, anddt,timeandframeare the solver's own.
Writing a shader for a graph
The element index is global_invocation_id.x. A column's lanes are packed
f32s: a Float column binds as array<f32>, a Vec3 column as array<f32> read
three to an element, and a Vec2 or Vec4 column as array<vec2<f32>> /
array<vec4<f32>>.
bindings:
- { name: P, access: read_write, element: f32 }
- { name: weight, access: read, element: f32 }
params:
- { name: lift, type: float, default: 1.0 }
- { name: count, type: int, default: 0 }
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
let i = id.x;
if (i >= u32(params.count)) { return; }
P[i * 3u + 1u] = P[i * 3u + 1u] + params.lift * weight[i];
}
local pts = g.op.attribute.set({ domain = g.points.grid({ countX = 4, countZ = 4 }), name = "weight", value = 1 })
local raised = g.op.shader.compute({ domain = pts, shader = asset.resolve("lift", "computeShader"),
uniforms = { lift = 2 } })
Finding the points near a point
A shader that looks at its neighbours — separation in a flock, the car ahead in
traffic, a density — declares the three read bindings of a spatial grid and
includes the grid shader module; the node gets a neighborRadius, builds a
grid of the domain's points with cells that wide before it runs (and again at
every step of a sim.shader), and binds it. The shader then walks the 27 cells
around a point instead of every point.
bindings:
- { name: P, access: read, element: f32 }
- { name: nb, access: read_write, element: f32 }
- { name: gcfg, access: read, element: f32 }
- { name: grange, access: read, element: u32 }
- { name: gorder, access: read, element: u32 }
#include "@builtin::systems.procgen.grid"
let c = zero_grid_cell(p);
for (var dz = -1; dz <= 1; dz = dz + 1) {
for (var dy = -1; dy <= 1; dy = dy + 1) {
for (var dx = -1; dx <= 1; dx = dx + 1) {
let cc = c + vec3<i32>(dx, dy, dz);
let span = zero_grid_range(cc);
for (var s = span.x; s < span.y; s = s + 1u) {
let j = gorder[s];
// j == i is the point itself; a j whose own cell is not cc
// shares a bucket with it and belongs to another cell
}
}
}
}
local birds = g.op.points.sphere({ count = 64, radius = 2 })
local flock = g.op.sim.shader({ domain = birds, shader = asset.resolve("flock_step", "computeShader"),
neighborRadius = 1.5 })
A node whose shader declares the grid's bindings and is given no
neighborRadius is refused, naming the input.
A step that reads its neighbours' state while every element writes its own
would read some of them half-updated. A read binding named prev_<column>
(prev_P, prev_v) is that column as it stood before the dispatch — a copy
sim.shader takes before every step — so each element reads the same
before-state and writes only its own:
bindings:
- { name: P, access: read_write, element: f32 }
- { name: prev_P, access: read, element: f32 }
points.neighbors answers the common questions — how many neighbours, the
nearest, their centroid and their mean of an attribute — without a shader.