Controls visibility in every renderer and pass, including shadows, without removing the instance.
pose?
(instance: InstanceOf<H, D, M>) => number= none, which uses the bind pose
Selects the pose that moves the vertices by their joints.
deform?
(vertex: Vertex, instance: InstanceOf<H, D, M>) => d.v3f= none, which uses the mesh or posed position
Moves a vertex in object space before the instance transform.
deformShadow?
boolean= true
Controls whether the shadow pass applies deform.
bounds?
(instance: InstanceOf<H, D, M>, base: Sphere) => Sphere= base
Returns the world-space sphere for culling.
carry?
(vertexIndex: number, instance: InstanceOf<H, D, M>) => d.v4f= none, which omits four floats per vertex and sets carried to zero
Returns four values to interpolate between vertices and pass to material or surface as carried.
material?
( point: Point, instance: InstanceOf<H, D, M>, base: Material, carried: d.v4f, ) => Material= base
Returns the material at a point.
surface?
( point: Point, instance: InstanceOf<H, D, M>, base: Material, carried: d.v4f, ) => Shaded= none, which uses the mesh surface
Replaces the mesh surface at each pixel, as an impostor does.
coverage?
( point: Point, instance: InstanceOf<H, D, M>, base: Material, carried: d.v4f, ) => number= none: the instance covers every pixel it rasterises, and opaque renderers keep early depth testing
Returns how much of the pixel the instance covers, from 0 to 1, to cut holes in its shape, such as lace, or leaves on a card.
records
flush→encoder
renderer().cull→compute
What each description built of a renderer, in its pass; flush() once a frame, before them, uploads what changed; a culling renderer's cull in a compute pass after the flush.
owns
a table ·2 buffers ·culling buffers ·culling pipelines ·bind groups
The rows, the live ones per mesh and, once a render bundle draws them, their draws; with culling, a uniform of how many rows it lists from; without indirect-first-instance, a uniform and a bind group per mesh with instances; and a culling renderer's 3 buffers and 2 kernels for each description it is added to, which own its pipelines, colour, with a depth pipeline before it for a kind with a surface, or depth alone.
lifecycle
✓destroy()✓[Symbol.dispose]
injects
definition
Its kind's table, which your kernels read and write.
binds
1 storage
A field of its kind's table, per half, as a table's getters bind.
1 uniform
The mesh of an indirect draw, without indirect-first-instance.
1 storage
A renderer's vertex stage, the live rows per mesh or a culling renderer's visible rows.
2 storage · 1 uniform
A culling renderer's marking kernel, where place, visible and bounds run: the listing, its scratch and the listing's length.
cull: true, and a render bundle, draw indirectly; without it, each indirect draw binds a group holding its mesh, or draws through a pipeline per mesh where the pipeline has no group left. A renderer drawn into a pass without cull draws directly and needs none.
Change the count to add boxes and spheres without increasing the two draws. Positions update every frame, while live lists update only after the count changes.
Drawing each object separately spends CPU time on JavaScript calls, especially on phones. Store a transform and material per object in a set of instances: a table whose rows are meshes from a mesh pool. Its renderer lights and draws the live rows with one draw per mesh. Adding instances increases data without increasing draws.
Creates lighting with a sun, sky and fog. It can also read point lights from a table or a
function and cast shadows. Pipe its injection into each pipeline that uses it. Throws if
points names no source or more than one, a light function has neither count nor
candidates, a table comes with count, or candidates.ranges is anything other than a
positive integer.
@injects ― hook the shading hooks its options name: reflect, ambient, fog, sunShadow and
attenuation
@injects ― definition the sun, sky and fog, the shadows and the point lights, which shading
reads
@binds ― 1 uniform: shading.shade, the sun, sky and fog
@binds ― 2 storage, 1 uniform: shading.shade with a table of point lights
@binds ― 3 storage, 1 uniform: shading.shade with a table of lights whose positions are on the
device, the table's two parts
@binds ― 1 uniform: shading.shade with points.light or points.spot, plus what the function
binds
@binds ― 0 storage: shading.shade with points.candidates, plus what their functions bind, 2
storage for a neighbour grid's range and slot
@binds ― 1 uniform: shading.shade with a shadow, the sun's light space and cascades, beside the
depth array and its comparison sampler
@binds ― 1 uniform: shading.shade with pointShadow, each shadow's light, layers and views,
beside their depth array and the comparison sampler
@owns ― 1 to 3 buffers, a sampler, targets, a cameras.layered, passes: the sun, sky and fog; with a
shadow, its light space, the views of its cascades, a comparison sampler, the depth array and
the passes that draw it; with pointShadow, the same for the lights' faces
@records ― shadow.record and pointShadow.record with what was added to them, before the passes
that shade
@param ― options.shadow the sun's shadow, with a depth pass per cascade
@param ― options.pointShadow shadows that cast gives to lights of points.table, with a depth
pass per face
@param ― options.points point lights from a table or a 'use gpu' function. Every lit point visits
each slot up to the table's highest live one, or count with a function, unless candidates
names the slots that may reach it
@returns ― lighting you destroy
create(
constroot:TgpuRoot
root,{
settings:{
readonly sunDirection:[0.4,1,0.3];
}&((Partial<d.InferInput<d.WgslStruct<{
sunDirection:d.Vec3f;
sunIntensity:d.F32;
sunColor:d.Vec3f;
skyIntensity:d.F32;
skyTop:d.Vec3f;
fogDensity:d.F32;
skyBottom:d.Vec3f;
fogFalloff:d.F32;
fogColor:d.Vec3f;
skyReflection:d.F32;
fogStart:d.F32;
}>>>|undefined)&{
readonly sunDirection:[0.4,1,0.3];
})
Sets initial sun, sky and fog values over defaults. Default defaults, which has no fog.
settings:{
sunDirection:[0.4,1,0.3]
sunDirection:[0.4,1,0.3]}});
const
constscene:passes.Passes<{
readonly hdr:typeofpasses.color;
readonly depth:typeofpasses.depth;
}>
scene=
import passes
passes
.
functioncreate<{
readonly hdr:{
readonly format:"rgba16float";
};
readonly depth:{
readonly format:"depth24plus";
};
},{
readonly hdr:typeofpasses.color;
readonly depth:typeofpasses.depth;
}>(root:TgpuRoot,screen:targets.Targets<{
readonly hdr:{
readonly format:"rgba16float";
};
readonly depth:{
readonly format:"depth24plus";
};
}>,writes:{
readonly hdr:typeofpasses.color;
readonly depth:typeofpasses.depth;
},options?:passes.TargetOptions<{
readonly hdr:{
readonly format:"rgba16float";
};
readonly depth:{
readonly format:"depth24plus";
};
}>|undefined):passes.Passes<...>(+1overload)
Describes the targets and outputs for scene passes. With a surface such as the canvas, writes
only each renderer's colour. With a target set, writes colour into the target marked color,
depth into the target marked depth, and other outputs into their named targets. Multisampled
targets resolve according to resolve. Targets with layers draw one pass per layer, and every
target the description draws into needs the same layers. add builds a renderer's drawing.
begin starts a pass. pipe supplies fills such as a camera.
@param ― screen the surface, or the targets, which give the formats; each one the description
names is drawn into
@param ― writes what fills each target, by its key in screen
@returns ― a description you destroy, which holds the pipelines it builds
create(
constroot:TgpuRoot
root,
constscreen:targets.Targets<{
readonly hdr:{
readonly format:"rgba16float";
};
readonly depth:{
readonly format:"depth24plus";
};
}>
screen,{
hdr:typeofpasses.color
hdr:
import passes
passes.
constcolor:typeofpasses.color
Marks the target that receives each renderer's colour.
Adds fill after existing fills and returns a description of the same passes. Piped fills
take precedence over a drawing's own. For example, passes.create(…).pipe(view.inject()) uses
the camera from view.
pipe(
constview:cameras.OrbitCamera
view.
CameraBase.inject(): Inject
Fills cameras.current for the pipelines created from the configuration it is piped into.
Adds fill after the fills given to earlier beneath calls and returns a description of the
same passes. A drawing's own fills take precedence over these, and piped fills over both, so
beneath supplies defaults such as a lighting. A compound drawing that passes its own from
to pipeline gets them by starting from with inject().
beneath(
constlight:lighting.Lighting
light.
Lighting.inject(): Inject
Fills the shading hooks and resources for pipelines created from the configuration you pipe
it into.
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
create(
constroot:TgpuRoot
root,
constpool:meshPool.MeshPool<{}>
pool,{
Options<K extends AnyKind = Plain>.capacity: number
Limits the number of rows. A mesh instance uses one row; a model instance uses one row per
mesh.
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
Adds an instance of a mesh, model or group of meshes from the pool. Each mesh uses a row,
and all rows move and change together under one id. values may be a function of each mesh,
to give a model's meshes values of their own. Throws if a mesh belongs to another pool, the
group is empty, a posed kind receives a mesh without joints or the set is full.
Adds an instance of a mesh, model or group of meshes from the pool. Each mesh uses a row,
and all rows move and change together under one id. values may be a function of each mesh,
to give a model's meshes values of their own. Throws if a mesh belongs to another pool, the
group is empty, a posed kind receives a mesh without joints or the set is full.
add(
constball:meshPool.Mesh
ball,{
scale?: number |d.InferInput<d.Vec3f>| ArrayLike<number>|undefined
Sets one scale factor for all axes or a separate factor per axis.
scale:0.5,...
import materials
materials.
constglow:(color:d.v3f,strength:number)=>{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
Creates an emissive material that gives off strength times color and reflects almost
nothing.
glow(
import d
d.
constvec3f:d.Vec3f
(x:number,y:number,z:number)=>d.v3f(+5 overloads)
vec3f(1,0.85,0.6),2)});
const
constorbit:HostField<d.Vec3f,"Instance">
orbit=
constthings:instances.Instances<Plain>
things.
Instances<Plain>.fields: instances.Fields<Plain>
Provides one handle per field, which writes without allocating. Keep a handle outside a loop
instead of reading fields.x inside it.
Uploads changed rows. When instances are added or removed, it also uploads live rows per
mesh and updates draw counts. Call this once per frame before drawing. HistoryInstances
take the frame's encoder and throw without it.
Begins a pass, draws drawn or each drawing in a list in order, then ends the pass. Reuse the
list to avoid allocating each frame. Call begin yourself when you need to choose drawings
per frame. Records nothing while enabled is false.
Runs the loop on each scheduled frame. Time advances by at most maxDt per frame, never steps
back, and stops while the surface is paused. Destroying the animation keeps the loop alive.
Another animation on the same loop continues from its recorded time. A frame error stops the
animation.
@param ― loop the loop to run on each unpaused frame. Continues from its seconds if it ran before
@returns ― an animation you destroy to stop the frames
animate(
constloop:frames.Frames
loop);
things.renderer() returns a drawing, lit by the lighting its pass description gives through beneath. scene.add builds pipelines for its pass description and returns the draw commands. Each mesh uses a direct draw. Meshes with matching side, mask and texture settings share a pipeline. This needs no optional device feature. A finished look for model scenes draws one set of glTF models with shadows, ambient occlusion, TAA and bloom.
Adds an instance of a mesh, model or group of meshes from the pool. Each mesh uses a row,
and all rows move and change together under one id. values may be a function of each mesh,
to give a model's meshes values of their own. Throws if a mesh belongs to another pool, the
group is empty, a posed kind receives a mesh without joints or the set is full.
A row starts with its mesh’s material. Shapes default to white chalk. Models use the file’s factors, multiplied by textures. Supplied values and the set’s defaults replace factors. To tint relative to the file, start with mesh.material.
Spread a materials preset into the values, for example { position, ...materials.glossy(color, 0.3), opacity: 0.2 }.
write changes only supplied fields. undefined leaves a field unchanged. write and remove take effect at the next flush. Removal invalidates the id, and has(id) checks whether it is live.
In shaders, instances.rotate(q, v) rotates v by a unit quaternion. instances.facing(direction) turns +z toward direction with +y up. facing also runs on the CPU.
The set is a table of its kind, and everything a table offers works on it. Field handles write without allocating, live() spreads into a picker, neighbour grid or live lists, and idAt names the instance at a slot the GPU found:
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
create(
constroot:TgpuRoot
root,
constpool:meshPool.MeshPool<{}>
pool,{
Options<K extends AnyKind = Plain>.capacity: number
Limits the number of rows. A mesh instance uses one row; a model instance uses one row per
mesh.
capacity:1024,
Options<Plain>.defaults?:{
readonly position?: FieldInput<Vec3f>;
readonly rotation?: FieldInput<Vec4f>;
readonly scale?: number | FieldInput<Vec3f>;
readonly color?: FieldInput<Vec3f>;
readonly roughness?: number;
readonly metallic?: number;
readonly emission?: FieldInput<Vec3f>;
readonly opacity?: number;
}|undefined
Sets values for fields that add omits. They replace the mesh's material factors. Default
none: the identity transform, the mesh's material and zero for custom fields.
Adds an instance of a mesh, model or group of meshes from the pool. Each mesh uses a row,
and all rows move and change together under one id. values may be a function of each mesh,
to give a model's meshes values of their own. Throws if a mesh belongs to another pool, the
group is empty, a posed kind receives a mesh without joints or the set is full.
add(
constbox:meshPool.Mesh
box);
const
constspin:HostField<Vec4f,"Instance">
spin=
constcrates:instances.Instances<Plain>
crates.
Instances<Plain>.fields: instances.Fields<Plain>
Provides one handle per field, which writes without allocating. Keep a handle outside a loop
instead of reading fields.x inside it.
Finds a candidate under a screen point on the GPU. Projects every candidate with the camera and
keeps the one within reach with the best rank. Throws unless capacity is an integer from 1 to
2^24.
@injects ― per-instance found, for shaders that act on the pick in the same frame
@binds ― 1 storage: found, the pick's key
@owns ― 3 buffers, a pipeline, a readback: the pick's key, the pointer and the settings; the
readback brings the key to the CPU
@records ― pick once in a frame that picks, in a compute pass after whatever moves the
candidates; result.capture(frame) after that pass, which copies what a pick found.
@param ― camera the camera the candidates are drawn with
@param ― options.position where each candidate is; it, radius, alive and rank carry the fills
of what they read, such as Star.position.pipe(stars.inject())
@returns ― a picker you destroy
create(
constroot:TgpuRoot
root,
constview:cameras.OrbitCamera
view,{
...
constcrates:instances.Instances<Plain>
crates.
functionlive():Live
Returns the capacity and live test for live lists, a grid, a picker or state machines. Use
liveLists.create(root, { ...coins.live(), draws }).
live(),// the capacity and the live test
Options.size: SizeSource
Supplies the view size at each pick, such as the canvas surface. pixelRatio scales reach from
screen points to pixels. Without it, one point equals one pixel.
size,
Options.position: (i:number)=> v3f
Returns the candidate center in world space.
position:
import instances
instances.
constInstance:Plain
The plain kind, with a transform and material, that a set without kind stores. Its getters
read a plain set in shaders filled by the set's inject().
Applies a configuration transform to the slots and accessors this function reads,
as root.pipe does to a pipeline's.
pipe(
constcrates:instances.Instances<Plain>
crates.
functioninject():Inject
Fills the definition's accessors with this table's buffers.
inject()),
});
// Once a pick is read back
const
constslot:number|null|undefined
slot=
consthits:picker.Picker
hits.
Picker.result: Readback<number |null>
Reads the picked candidate index to the CPU, or null if nothing was found. Capture after the
picker pass, then call take() or await next(). Without hz, each pick requests a copy for the
next capture.
result.
Readback<number |null>.take(): number |null|undefined
Returns latest once per completed copy, then undefined until the next copy completes. Each
value goes to one caller. Callers sharing a readback split the values between them.
take();
if(typeof
constslot:number|null|undefined
slot==='number')
constcrates:instances.Instances<Plain>
crates.
Instances<Plain>.write(id: IdOf<Plain>, values: {
readonly position?: FieldInput<Vec3f>;
readonly rotation?: FieldInput<Vec4f>;
readonly scale?: number | FieldInput<Vec3f>;
readonly color?: FieldInput<Vec3f>;
readonly roughness?: number;
readonly metallic?: number;
readonly emission?: FieldInput<Vec3f>;
readonly opacity?: number;
}): void
Writes the supplied fields and keeps the rest. At the next flush, device fields written
here overwrite kernel writes.
write(
constcrates:instances.Instances<Plain>
crates.
Instances<Plain>.idAt(slot: number): IdOf<Plain>
Returns the id of the instance that holds slot, such as a slot a picker found.
A set without a kind stores instances.Instance rows, whose getters its shaders read.
buffers, slotOf(id) and clear() also work as on a table. count counts instances, while capacity and span count rows.
things.add(model) adds one row per mesh under one id. write, field handles and remove act on every row of the model. idAt gives the model’s id for any of its slots, and count counts it once. A field handle’s get reads the model’s first mesh.
Use things.add({ meshes }) to group any pool meshes, including a character and held items.
Pass a function of each mesh to give a model’s meshes values of their own, such as a coat colour per part: things.add(horse, (mesh, i) => ({ color: coats[i] })).
defaults sets values for fields add omits, including a kind’s own fields.
Call things.flush() to upload changed rows. When instances are added or removed, it also uploads per-mesh live lists. A set with history is typed HistoryInstances, whose flush takes the frame’s encoder.
Call renderer.cull(pass) in a compute pass for a culling renderer.
For a shadow, call light.shadow.record(frame, [cast]). Create cast = light.shadow.add(renderer) while loading.
Call drawn.draw(pass) in the scene pass or use scene.record(frame, drawn). Direct rendering uses one drawIndexed per mesh with instances. Culling uses one drawIndexedIndirect per mesh the set has used. Meshes sharing a pipeline and textures draw consecutively with the pipeline and groups set once.
Await scene.initAsync() and light.shadow.initAsync() while loading to compile renderer pipelines. Otherwise each pipeline compiles on its first draw. The GPU debug panel names pipelines by kind, with qualifiers such as cast, cover for a prepass, two-sided or masked.
Outputs such as antialiasing over time’s motion run the renderer’s vertex code a second time with last frame’s rows. Create the set with history: true to keep them, as a table does. Each instance then moves in the motion output, whether write or a kernel moved it:
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
Selects a kind from instances.define. Default a plain kind with a transform and material.
kind:
constFish:instances.Kind<"Fish",{},{
velocity:d.Vec3f;
},"device">
Fish,
Options<K extends AnyKind = Plain>.capacity: number
Limits the number of rows. A mesh instance uses one row; a model instance uses one row per
mesh.
capacity:1024,
history:true
Keeps last frame's rows, as a table's history does, so motion follows every instance that
moves, whether the CPU or a kernel moves it. create then returns HistoryInstances, whose
flush takes the frame's encoder. Default false.
Uploads changed rows, as Instances.flush does, and records into encoder the copies that
keep last frame's rows. Call this once per frame before drawing.
flush(
frame:frames.Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
encoder);// keeps last frame's rows, then uploads
// ... kernels that move the school, then the passes
});
Without history, the motion output sees only the camera move.
things.renderer(options) draws the set. Add it to multiple pass descriptions to build pipelines for each. Multiple renderers can draw the same set. drawn.destroy() removes a drawing from its description and frees its resources.
Option
Default
light
none
A lighting in place of the one the description gives through beneath; a renderer whose shade reads none needs neither
blend
'opaque'
'translucent' covers what is drawn by each instance’s opacity and writes no depth; 'clear' is glass
transmit
none
Under 'clear', the share of each colour behind that shows through: a tint
textured
true
false ignores mesh textures and masks, drawing textured meshes with their factors alone
specularAntialiasing
true
false keeps sharp highlights on flat-shaded faces
cull
false
true draws into each description only what its camera may see
from
0
Draws only the instances at least this far from eye; a number or a 'use gpu' function of the row’s slot
to
every distance
Draws only the instances nearer than this to eye, as from
fade
0
Metres around from and to across which it hands its samples over
eye
none
The camera from and to measure from; a renderer with either needs it
shade
shading.shade
Computes outgoing light from the material. Supply unlit, toon or debug shading. A blend: 'clear' renderer always uses shadeClear
prepass
false
true draws an opaque renderer’s depth first, with coverage and masks alone, then shades where the depth is equal
With cull: true, each pass draws only instances its camera may see. The scene uses view, and the shadow uses the sun. Record renderer.cull(pass) after things.flush() and movement kernels, before shadow and scene passes:
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer({
RendererOptions.cull?: boolean |undefined
Enables GPU culling against each pass camera using spheres around mesh boxes. Call cull
each frame to list visible instances in set order. Throws if the kind has deform without
bounds. Default false.
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
add(
constrenderer:instances.Renderer
renderer);
const
constcast:passes.Drawn
cast=
constlight:lighting.LightingOf<{
readonly shadow:{};
}>
light.
shadow: Shadow |(Cascades & Shadow)
Provides the sun shadow when enabled in the options. Throws after destroy.
shadow.
ShadowPasses.add<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>(drawing:passes.Compound<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>):passes.Drawn(+1overload)
Builds a caster's depth pipeline with the shadow's bias and returns its draw methods.
add(
constrenderer:instances.Renderer
renderer);// each keeps a list of its own
// After things.flush() each frame
const
constculling:TgpuComputePass
culling=
constframe:Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
Records visible-instance lists for each enabled description that contains this renderer.
Call this after flush() and any kernels that move instances, before drawing. Throws unless
the renderer has cull: true.
cull(
constculling:TgpuComputePass
culling);
constculling:TgpuComputePass
culling.
TgpuComputePass.end(): void
Completes the recording of this compute pass
end();
constlight:lighting.LightingOf<{
readonly shadow:{};
}>
light.
shadow: Shadow |(Cascades & Shadow)
Provides the sun shadow when enabled in the options. Throws after destroy.
Clears the map in a depth pass per cascade that redraw picks and draws the casters built by
add. Call this while recording each frame, before drawing shaded surfaces. When disabled, it
draws nothing and makes every point sunlit. A frame that draws no cascade writes a timer's
timestamps from an empty compute pass.
Begins a pass, draws drawn or each drawing in a list in order, then ends the pass. Reuse the
list to avoid allocating each frame. Call begin yourself when you need to choose drawings
per frame. Records nothing while enabled is false.
record(
constframe:Frame
frame,
constdrawn:passes.Drawn
drawn);
Culling reads draw counts written by GPU kernels and uses indirect draws. Request indirect-first-instance with tgpu.init({ device: { optionalFeatures: ['indirect-first-instance'] } }) to share pipelines across meshes without a group per mesh. Without the feature, each indirect draw binds a group that holds its mesh, and its pipeline reads the mesh from there. Where a pipeline has no bind group to spare, each mesh gets a pipeline with its mesh compiled in.
Test each row with the sphere around its mesh box, scaled and transformed by the row. Model meshes and held items each use their own boxes.
Visible rows retain set order across frames so blended objects do not flicker.
cull dispatches two kernels over live rows per active pass description. It skips shadow work while shadows are off. Multiple renderers can share a compute pass.
In a description with layers, each layer keeps a list of its own, seen through its view. The two kernels dispatch once, over every layer at once, so the work grows with rows times layers and the dispatch count stays the same. Each layer’s pass draws its own list.
If deform moves vertices beyond the mesh box, supply a bounds sphere.
Use cameras.sees(cameras.current.$, center, radius) for the same visibility test in your shaders, such as a live listsalive callback.
Change the fade around 7 m from the bright eye marker. One renderer draws nearby spheres and another draws distant boxes, splitting pixels across the fade and culling from the view above.
Use less detail for distant objects. A culling renderer with from and to draws instances at least from and less than to metres from eye. Split a set between renderers, or use separate sets for near meshes and distant impostors:
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer({
RendererOptions.cull?: boolean |undefined
Enables GPU culling against each pass camera using spheres around mesh boxes. Call cull
each frame to list visible instances in set order. Throws if the kind has deform without
bounds. Default false.
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer({
RendererOptions.cull?: boolean |undefined
Enables GPU culling against each pass camera using spheres around mesh boxes. Call cull
each frame to list visible instances in set order. Throws if the kind has deform without
bounds. Default false.
cull:true,...
constfar:{
from:number;
fade:number;
eye:cameras.OrbitCamera;
}
far}).
Renderer.pipe(fill: Inject): instances.Renderer
Returns this renderer with fill applied to every pipeline and kernel it builds. The fill
runs after lighting and before the pass camera. Use it for resources read by kind callbacks,
as in school.renderer().pipe(water.inject()).
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map((
renderer:instances.Renderer
renderer)=>
constlight:lighting.LightingOf<{
readonly shadow:{};
}>
light.
shadow: Shadow |(Cascades & Shadow)
Provides the sun shadow when enabled in the options. Throws after destroy.
shadow.
ShadowPasses.add<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>(drawing:passes.Compound<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>):passes.Drawn(+1overload)
Builds a caster's depth pipeline with the shadow's bias and returns its draw methods.
add(
renderer:instances.Renderer
renderer));
Measure distance from eye to each row’s position. The same distance applies in every pass description, including shadows.
from and to can be shader callbacks taking the row slot, with their dependencies filled. Read uniforms to change thresholds without recompiling. Keep from below to for every row.
Across fade metres around a threshold, the renderer draws a share of each instance that falls with its distance. Give the adjoining renderer the same fade to draw the rest. With multisampling an instance keeps whole samples, the same ones in every pixel, so the two blend evenly across it in steps of one sample. Without it they split pixels in a 4 × 4 pattern, which shows as a screen door where the two look different. Shadows switch without fading.
Fading discards pixels or samples, which prevents early rejection of hidden pixels and increases shading work.
With blend: 'translucent', instances draw translucent from premultiplied colour and their opacity. In a description with translucency targets they need no order; elsewhere they blend over in draw order. They write no depth and cast no shadows. The renderer accepts only pass descriptions with colour. Put blended objects in a separate set and draw them after opaque objects:
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
create(
constroot:TgpuRoot
root,
constpool:meshPool.MeshPool<{}>
pool,{
Options<K extends AnyKind = Plain>.capacity: number
Limits the number of rows. A mesh instance uses one row; a model instance uses one row per
mesh.
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
create(
constroot:TgpuRoot
root,
constpool:meshPool.MeshPool<{}>
pool,{
Options<K extends AnyKind = Plain>.capacity: number
Limits the number of rows. A mesh instance uses one row; a model instance uses one row per
mesh.
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer();
// Drawn in this order, the ghosts over the blocks
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
Selects how instances write colour and depth. 'opaque' writes depth. 'translucent' draws
from premultiplied opacity, as a drawing with translucent: true does: in any order into a
description with translucency targets, over in draw order elsewhere. It leaves depth unchanged
and draws only in colour passes, after the opaque renderers. 'clear' draws in the same way
but adds reflections at full strength through shading.shadeClear, so glass and clear
plastic retain highlights at any opacity. With transmit, it blends over in draw order.
Default 'opaque'.
blend:'translucent'}))];
const
constcasters:passes.Drawn[]
casters=[
constlight:lighting.LightingOf<{
readonly shadow:{};
}>
light.
shadow: Shadow |(Cascades & Shadow)
Provides the sun shadow when enabled in the options. Throws after destroy.
shadow.
ShadowPasses.add<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>(drawing:passes.Compound<{
color:v4f;
},{
position:v4f;
world:v3f;
normal:v3f;
uv:v2f;
uv1:v2f;
row:number;
}>):passes.Drawn(+1overload)
Builds a caster's depth pipeline with the shadow's bias and returns its draw methods.
blend: 'clear' draws as 'translucent' does, with shading.shadeClear. Highlights and sky reflections stay bright at any opacity. Opacity scales diffuse light and emission. On metals, it fades only fog.
Use transmit to tint the background through glass. { blend: 'clear', transmit: [0.88, 0.92, 0.94] } transmits those fractions of each channel at any opacity. Opacity controls the milky appearance. The tint belongs to the renderer and is set as the pass blend constant at each draw, so a transmit renderer blends over in draw order.
The renderer supplies an instances.Corner at each vertex and an instances.Lit at each pixel. Outputs use these values, including motion for antialiasing over time. See Passes for target outputs.
A kind adds row fields and replaces shader functions with your own 'use gpu' functions. The fish tank’s fish keep velocity, beat their tails and have pale bellies:
Defines an instance kind with custom fields, transform ownership and callbacks for vertex
motion or materials. Each kind uses colour and shadow pipelines per renderer. name brands
set ids, as in Id<'Fish'>, and labels the table and WGSL. Throws if both material and
surface are supplied, or a custom field duplicates a built-in or reserved name.
@param ― name a label for ids and WGSL
@returns ― a kind to give instances.create as kind
Moves a vertex in object space before the instance transform. Colour and shadow passes both
apply this function. The shadow pass must then read the whole vertex instead of its position
alone. Normals remain as the mesh or pose supplies them. Default none, which uses the mesh
or posed position.
Returns the material at a point. base combines the instance material fields with the mesh
textures, as materials.textured does. carried contains interpolated values from carry.
Default base.
material:(
point:{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
point,
fish:{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
mesh: number;
opacity: number;
belly:d.v3f;
phase: number;
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
velocity:d.v3f;
}
fish)=>{
'use gpu';
const
constup:number
up=
import std
std.
functioncos(value:number):number(+1overload)
export cos
cos(
point:{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
point.
uv:d.v2f
Holds texture coordinates at the point. A point created with pointAt has zero coordinates.
uv.
v2f.y: number
y*
constTAU:number
TAU);
return
import materials
materials.
constglossy:(color:d.v3f,roughness:number)=>{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
Creates a dielectric with specular highlights, such as paint, plastic or wet stone.
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
Adds an instance of a mesh, model or group of meshes from the pool. Each mesh uses a row,
and all rows move and change together under one id. values may be a function of each mesh,
to give a model's meshes values of their own. Throws if a mesh belongs to another pool, the
group is empty, a posed kind receives a mesh without joints or the set is full.
Call instances.define at module scope without a root. Pass the result to create as kind.
The name brands set ids, such as instances.Id<'Fish'>, and names the WGSL.
Supply custom fields alongside built-in fields in add and write. Reusing a built-in name such as color or a reserved table name such as host, device, row, alive, span or slotOf is a type error. define also throws.
All functions are optional. instance gives the whole row. Functions can read uniforms and buffers, such as time.$ above:
Function
Takes and returns
What it replaces
place
(instance) => instances.Placement
Where the instance is, from data of your own, in every pass, in culling and for from and to. The row’s own transform goes unread.
visible
(instance) => bool
Whether it draws, in every renderer and pass. A culling renderer leaves it out of its lists; one that doesn’t cull collapses its triangles.
pose
(instance) => u32, a pose’s id
The mesh as bound: vertices and normals follow the pose’s joints, in every pass, before deform. Poses come from the poses piped into the renderer.
deform
(vertex: instances.Vertex, instance) => vec3f
Where the vertex is, in object space, in every pass. The normal stays the mesh’s, or the pose’s.
How much of the pixel the instance covers, from 0 to 1, in every pass, shadows included: holes such as lace, or the leaves on a card.
With deform, the shadow pass reads 20 bytes per vertex rather than 12, or 28 rather than 20 with a pose. Set deformShadow: false for deformations too small to see in shadows, such as tail motion.
A culling renderer of a kind with a deform needs bounds.
Supply either material or surface. A custom surface shades at mesh depth and writes its supplied point depth to shadows.
coverage runs before material or surface. With multisampling, it selects the share of samples covered; without, pixels below half coverage are omitted. Blended renderers scale opacity by it. A kind without coverage keeps early depth testing, so each mesh draws once and hidden pixels go unshaded.
A heavy material or surface that overlaps itself, such as a forest of impostor cards, can take prepass: true on its renderer: the depth goes first, running only coverage and the mesh masks, then the colour shades each visible pixel once. It costs a second pass over the vertices and needs a description with depth.
Each kind compiles a colour pipeline per pass description and a depth pipeline for shadows. Put per-instance differences in row fields to avoid extra pipelines.
With transform: 'device', the transform joins the kind’s device fields. A kernel created from root.pipe(school.inject()) reads through the kind’s getters and writes Fish.device.$[i]. Dispatch it over school.span after school.flush(), skipping free slots:
If a simulation has its own position buffer, read it in place instead of copying positions to rows every frame. Fill the dependency through the renderer’s pipe:
Defines an instance kind with custom fields, transform ownership and callbacks for vertex
motion or materials. Each kind uses colour and shadow pipelines per renderer. name brands
set ids, as in Id<'Fish'>, and labels the table and WGSL. Throws if both material and
surface are supplied, or a custom field duplicates a built-in or reserved name.
@param ― name a label for ids and WGSL
@returns ― a kind to give instances.create as kind
Returns the instance transform from custom data, such as a simulation buffer. All passes use
it, including culling and distance limits from from and to. It runs for each vertex in
every pass; when it does heavy work, a kernel that writes device rows once a frame costs less.
Attach its fills with pipe or the renderer pipe method. Default the instance position,
rotation and scale.
place:(
horse:{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
mesh: number;
opacity: number;
body: number;
fallen: number;
}
horse)=>{
'use gpu';
const
constbody:d.v4f
body=
constbodies:TgpuAccessor<d.WgslArray<d.Vec4f>>
bodies.
TgpuAccessor<WgslArray<Vec4f>>.$: d.v4f[]
$[
horse:{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
mesh: number;
opacity: number;
body: number;
fallen: number;
}
horse.
body: number
body]!;
return
import instances
instances.
functionPlacement(props?:{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
}|undefined):{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
}
export Placement
This schema holds a unit quaternion, position and scale per axis. Instance transforms use
world space; joint transforms use parent space.
Controls visibility in every renderer and pass, including shadows, without removing the
instance. Culling renderers omit hidden instances from their lists. Other renderers collapse
their triangles. Default always.
Creates a set of mesh instances with transforms, materials and optional custom fields. Each
instance uses a row, and renderers draw one list of live rows per mesh.
@param ― pool the mesh pool whose meshes it draws
@param ― options.kind a kind from instances.define
@returns ― a set of instances you flush every frame and destroy
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer().
Renderer.pipe(fill: Inject): instances.Renderer
Returns this renderer with fill applied to every pipeline and kernel it builds. The fill
runs after lighting and before the pass camera. Use it for resources read by kind callbacks,
as in school.renderer().pipe(water.inject()).
add and write still accept row position, rotation and scale, but place ignores them. place runs for each vertex in every pass; when it does heavy work, a kernel that writes rows once a frame costs less.
Fill dependencies read by kind functions through the renderer’s pipe. For example, the fish tank reads a noise cache. See shader dependencies:
const
constnoise:StaticPerlin3DCache
noise=
(alias)namespaceperlin3d
import perlin3d
perlin3d.
index.staticCache(options: {
root: TgpuRoot;
size: d.v3u;
}): StaticPerlin3DCache
exportindex.staticCache
A statically-sized cache for perlin noise generation, which reduces the amount of redundant calculations
if sampling is done more than once. If you'd like to change the size of the cache at runtime, see perlin3d.dynamicCacheConfig.
Creates a renderer for colour and shadow pass descriptions, lit by the lighting each
description gives through beneath unless options.light replaces it. Use pipe to fill
resources read by kind callbacks.
renderer().
Renderer.pipe(fill: Inject): instances.Renderer
Returns this renderer with fill applied to every pipeline and kernel it builds. The fill
runs after lighting and before the pass camera. Use it for resources read by kind callbacks,
as in school.renderer().pipe(water.inject()).
Builds a pipeline for drawing that writes every described output and returns its draw
methods. If the drawing cannot supply the fields an output needs, type checking fails with
that output's name. Use without to leave it out or instead to replace it.
add(
constrenderer:instances.Renderer
renderer);
A custom fill takes a configuration, for example .pipe((cfg) => cfg.with(clock, time)).
Defines an instance kind with custom fields, transform ownership and callbacks for vertex
motion or materials. Each kind uses colour and shadow pipelines per renderer. name brands
set ids, as in Id<'Fish'>, and labels the table and WGSL. Throws if both material and
surface are supplied, or a custom field duplicates a built-in or reserved name.
@param ― name a label for ids and WGSL
@returns ― a kind to give instances.create as kind
define('House',{
KindOptions<{ glow:F32;},{},"host">.host?:({
glow:d.F32;
}&{
readonly rotation?:"is a field of every instance"|undefined;
readonly position?:"is a field of every instance"|undefined;
readonly scale?:"is a field of every instance"|undefined;
readonly color?:"is a field of every instance"|undefined;
readonly roughness?:"is a field of every instance"|undefined;
readonly emission?:"is a field of every instance"|undefined;
readonly metallic?:"is a field of every instance"|undefined;
readonly mesh?:"is a field of every instance"|undefined;
readonly opacity?:"is a field of every instance"|undefined;
}&{
...;
})|undefined
Adds fields written on the CPU, as in table.define. Default none.
host:{
glow:d.F32
glow:
import d
d.
constf32:d.F32
export f32
A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)
Returns the material at a point. base combines the instance material fields with the mesh
textures, as materials.textured does. carried contains interpolated values from carry.
Default base.
material:(
point:{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
point,
house:{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
mesh: number;
opacity: number;
glow: number;
}
house,
base:{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
occlusion: number;
}
base)=>{
'use gpu';
// Window panes occupy column 6, row 1 of the 8 by 4 atlas
Use carry to read per-vertex data, such as a mesh-pool extra attribute. material and surface receive the interpolated value as their last argument. This example uses vertex colours:
const
constpool:meshPool.MeshPool<{
color:d.Vec4f;
}>
pool=
import meshPool
meshPool.
functioncreate<{
color:d.Vec4f;
}>(root:TgpuRoot,options:meshPool.Options<{
color:d.Vec4f;
}>):meshPool.MeshPool<{
color:d.Vec4f;
}>
Creates shared vertex storage and an index buffer, with bounds and textures per mesh. One
pipeline can draw any mesh. Each vertex uses 12 bytes for position and 8 for normal and UV
coordinates, and 8 for a second uv set with uvs1. Skinned models add 8 bytes for joints and
weights. Extras add the size of their struct per vertex.
@injects ― definition the vertices vertexAt, positionAt and uv1At read, the joints and
weights influencesAt reads, and the boxes boundsOf reads
@injects ― per-instance the extras pool.extra reads
@binds ― 1 storage: positionAt, the positions
@binds ― 2 storage: vertexAt, the positions and the normals and uvs
@binds ― 1 storage: uv1At, the second uvs, in a pool made with uvs1
@binds ― 1 storage: influencesAt, the joints and weights
@binds ― 1 storage: boundsOf, every mesh's box
@binds ― 1 storage: pool.extra, every vertex's extras
@binds ― 1 uniform: pool.texturesOf(index) for materials, beside its textures and sampler
@submits ― setup a shape added without an encoder bakes in a compute pass of its own, submitted at
once, and a model's images generate their mips as they decode
@owns ― 5 to 7 buffers, a uniform per way of reading textures, a pipeline per shape, textures,
samplers, bind groups, what images makes: positions, normals and uvs, indices and boxes,
second uvs with uvs1, joints and weights once a skinned model comes, and the extras when
extra names any; each image decoded once per format it is read in, and a group per set of a
mesh's textures
@returns ― a pool you add meshes and models to and destroy
create(
constroot:TgpuRoot
root,{
Options<E extends Extras ={}>.capacity: number
Limits the number of meshes to at most 256.
capacity:16,
Options<{ color: Vec4f;}>.extra?:{
color:d.Vec4f;
}|undefined
Declares custom vertex attributes, for example { tint: d.vec4f }. Model meshes supply
their data in extra; shaders read it through pool.extra.tint(i). These attributes use a
separate buffer with one struct per vertex. Shapes read as zero. Default none.
Defines an instance kind with custom fields, transform ownership and callbacks for vertex
motion or materials. Each kind uses colour and shadow pipelines per renderer. name brands
set ids, as in Id<'Fish'>, and labels the table and WGSL. Throws if both material and
surface are supplied, or a custom field duplicates a built-in or reserved name.
@param ― name a label for ids and WGSL
@returns ― a kind to give instances.create as kind
Returns four values to interpolate between vertices and pass to material or surface as
carried. For example, read an extra attribute with pool.extra.tint(vertexIndex).
vertexIndex indexes the pool, as in meshPool.vertexAt. Default none, which omits four
floats per vertex and sets carried to zero.
Returns the material at a point. base combines the instance material fields with the mesh
textures, as materials.textured does. carried contains interpolated values from carry.
Default base.
material:(
_point:{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
_point,
_instance:{
rotation:d.v4f;
position:d.v3f;
scale:d.v3f;
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
mesh: number;
opacity: number;
}
_instance,
base:{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
occlusion: number;
}
base,
carried:d.v4f
carried)=>{
'use gpu';
const
constpainted:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
painted=
import shading
shading.
constMaterial:d.WgslStruct
(props?:{
color:d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}|undefined)=>{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
occlusion: number;
}
This schema describes a surface with the glTF metallic-roughness model.
Material(
base:{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
occlusion: number;
}
base);
constpainted:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
painted.
color:d.v3f
Sets the diffuse colour of a dielectric or the reflection colour of a metal, in linear
colour space.
color=
base:{
color:d.v3f;
roughness: number;
emission:d.v3f;
metallic: number;
occlusion: number;
}
base.
color:d.v3f
Sets the diffuse colour of a dielectric or the reflection colour of a metal, in linear
colour space.
color*
carried:d.v4f
carried.
rgb:d.v3f
rgb;
return
constpainted:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
painted;
},
});
vertexIndex uses pool indices, as meshPool.vertexAt does. Without carry, carried is zero and vertices pass four fewer floats.
pool.extra belongs to one pool. Define kinds that read it after creating that pool, and use them only with its meshes.
Two sets of one kind with the same renderer options compile a pipeline pair each.
Renderers compile a pipeline for each combination of doubleSided, masked and textured present in the pool, including unused meshes. initAsync compiles combinations present at that time. Adding a mesh with another combination, such as an impostor card, causes compilation on its first draw.
A renderer draws an alpha: 'blend' material as opaque; blending is the renderer’s blend option.
Render bundles cannot set a blend constant. For a transmit renderer in a bundle, set it on the enclosing pass with pass.setBlendConstant([...transmit, 1]).
A bundle draws the meshes that had instances when it was recorded. Record it again after a mesh’s first instance, and once a model’s ready resolves.
Back-face lighting uses the interpolated normal relative to the eye. A front face can therefore receive back lighting near an edge where that normal turns away.
A kind with a surface ignores masks: it says what it covers itself.
deform can’t bend the normal or move the uv.
facing applies no y rotation for straight-up or straight-down directions. Rotate models that do not face +z when reading them with rotation.