a 'use gpu' function; it and shade carry the fills of what they read, such as tgpu.fn(swell).pipe(noise.inject()). The ribbons face the camera of the pass description they are drawn in, which width and shade read too
Trailsreturns
a set of trails you destroy
schemas
SampleRingSettings
constants
defaults
types
JoinTrails
Optionscreate · 11 fields
capacity
number
Sets the number of trails to an integer of at least 1.
length
number
Sets the number of points per trail to an integer of at least 2.
label?
string= trails
Sets the drawing pipeline label.
blend?
Exclude<lines.Blend, 'opaque'>= add
Selects add for light, over for smoke or ink, translucent for the passes' translucency targets, or a custom blend state.
width?
(point: Sample) => number= settings.width at the newest point, tapering with along to 0 at the oldest point of a full trail
Returns the trail width in world units at a point.
shade?
(point: Sample, across: number) => d.v4f= settings.color, with soft edges and a fade towards the oldest point
Returns premultiplied fragment colour.
head?
(trail: number) => d.v3f= the newest point
Returns where a trail's newest end is drawn, in place of its newest point, such as where a particle system draws the particle that pushes it: tgpu.fn(sparks.drawnAt).pipe(sparks.inject()).
join?
Join= round
Selects the join at a point, such as lines.joins.miter.
startCap?
Join= round
Selects the cap at the newest end, such as lines.caps.arrow.
endCap?
Join= round
Selects the cap at the oldest end.
settings?
Partial<d.InferInput<typeof Settings>>= defaults, with white colour and width 0.1
Sets initial colour and width for the default callbacks.
records
drawing.draw→render
clear→encoder
drawing, added to passes, after the passes that push; clear whenever you like.
owns
3 buffers ·a lines
The points, the rings and the settings, and the lines that draw them.
lifecycle
✓destroy()✓[Symbol.dispose]
injects
per-instance
push, reset, pointAt and held, for shaders piped with it.
binds
2 storage
push or pointAt, the points and the rings.
1 storage
held or reset alone, the rings.
2 storage · 2 uniform
drawing, the points and the rings, the settings and passes.pixels.
Drag to write a whole trail, keeping its newest length points, or compare the four kernel-pushed orbits. Fixed-step pushes preserve duration across displays; per-frame pushes shorten it at higher rates, and changing length, join, cap or blend rebuilds the set.
A wake, a drawn line or a comet’s path follows recent positions. Rebuilding ribbons on the CPU requires vertices for every trail each frame, with joins that can crack at sharp turns. Store each trail as a ring of recent points on the GPU. Push points from a kernel or write a whole line from the CPU. One draw turns all trails into lines with joins and caps.
Creates trails as rings of recent points, pushed by kernels or written on the CPU, and draws
them as lines. Throws unless capacity is an integer of at least 1 and length is an integer
of at least 2.
@injects ― per-instance push, reset, pointAt and held, for shaders piped with it
@binds ― 2 storage: push or pointAt, the points and the rings
@binds ― 1 storage: held or reset alone, the rings
@binds ― 2 storage, 2 uniform: drawing, the points and the rings, the settings and
passes.pixels
@owns ― 3 buffers, a lines: the points, the rings and the settings, and the lines that draw them
@records ― drawing, added to passes, after the passes that push; clear whenever you like.
@param ― options.width a 'use gpu' function; it and shade carry the fills of what they read,
such as tgpu.fn(swell).pipe(noise.inject()). The ribbons face the camera of the pass
description they are drawn in, which width and shade read too
@returns ― a set of trails you destroy
create(
constroot:TgpuRoot
root,{
Options.capacity: number
Sets the number of trails to an integer of at least 1.
capacity:64,
Options.length: number
Sets the number of points per trail to an integer of at least 2. Pushing beyond this count
drops the oldest point.
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
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.
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(
constwakes:trails.Trails
wakes.
Trails.drawing: passes.Drawing<d.WgslStruct<{
position:d.Vec4f;
world:d.Vec3f;
normal:d.Vec3f;
color:d.Vec4f;
along:d.F32;
segment:d.Vec3f;
radii:d.Vec2f;
rim:d.Vec4f;
ends:d.U32;
line:d.U32;
}>,{
world:d.v3f;
normal: d.v3f;
color: d.v4f;
}>&{
inject(): Inject;
}
Draws trails with at least two points, newest first, as lines when added to a pass
description. It tests depth without writing it.
Pushes a point to the newest end from a shader created from root.pipe(wakes.inject()).
Once the trail holds length points, it drops the oldest. Only one thread may push a given
trail per dispatch; concurrent pushes race.
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
Records a frame into one encoder and submits once, with a clock. Borrows the root. The
synchronous recorder borrows each frame's encoder.
@submits ― always once a frame after your recorder returns, and again at each frame.submit()
@param ― record your frame, recorded into each frame's encoder
@returns ― a loop you run with each frame's time and destroy
create(
constroot:TgpuRoot
root,(
frame:frames.Frame
frame)=>{
const
constcompute:TgpuComputePass
compute=
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.
Adds dt seconds and returns the number of steps to run. Each step lasts step seconds, so a
GPU kernel can use a fixed duration. All steps in a frame see the same queued CPU writes. Keep
a step counter on the GPU for values that must change per step.
advance(
frame:frames.Frame
frame.
Frame.dt: number
Reports seconds since the previous submitted frame. The first frame has a dt of zero.
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(
frame:frames.Frame
frame,
constdrawn:passes.Drawn
drawn);
}),
);
A push adds a point and drops the oldest once the trail holds length points. Push once per fixed step. Pushing once per frame makes trails shorter on faster displays.
Allow only one thread to push a given trail per dispatch. Multiple threads would race to update its ring.
reset(trail) clears one trail from a kernel and counts as its push for that dispatch. The same thread can push after resetting. Do this when respawning an object to avoid a streak between its old and respawn positions. clear(encoder) clears all trails.
canvas.add(wakes.drawing) builds the pipeline into a pass description, shaped for the camera the description injects. One indexed draw renders all trails with at least two points, using one instance per segment.
Ribbons blend and test depth without writing it. Draw them after opaque renderers, or pass blend: 'translucent' to draw them into the translucency targets in any order. See Passes for their target outputs.
The default ribbon starts at settings.width and tapers to zero at the oldest point a full trail can hold. It uses settings.color and is brightest at its centre. Change width with wakes.settings.patch({ width: 0.4 }).
Points land on fixed steps, while particles draw between them, ahead of the last step. head draws a trail’s newest end where its particle is drawn instead: head: tgpu.fn(sparks.drawnAt).pipe(sparks.inject()). The fireworks streaks push a point every few steps and stay on their stars this way.
The solar system example pushes each comet’s trail from the kernel that moves it.
write(trail, positions) replaces a trail with x, y and z values ordered oldest first. It keeps the newest length points. write(trail, []) clears it. Rewrite a player-drawn line whenever it changes:
Writes x, y and z coordinates from oldest to newest, keeping only the newest length
points. Use write(trail, []) to empty a trail. Queue writes run before frame commands, so
kernel pushes in that frame follow these points.
width(point) returns the width in world units. shade(point, across) returns premultiplied fragment colour, with across from -1 to 1 across the ribbon. Both receive a trails.Sample with position, trail and along. along is 0 at the newest point and 1 at the oldest point a full trail can hold.
Allocates memory on the GPU, allows passing data between host and shader.
Read-only on the GPU, optimized for large data. For a general-purpose buffer,
use
TgpuRoot.createBuffer
.
@param ― typeSchema The type of data that this buffer will hold.
@param ― initial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)
Creates trails as rings of recent points, pushed by kernels or written on the CPU, and draws
them as lines. Throws unless capacity is an integer of at least 1 and length is an integer
of at least 2.
@injects ― per-instance push, reset, pointAt and held, for shaders piped with it
@binds ― 2 storage: push or pointAt, the points and the rings
@binds ― 1 storage: held or reset alone, the rings
@binds ― 2 storage, 2 uniform: drawing, the points and the rings, the settings and
passes.pixels
@owns ― 3 buffers, a lines: the points, the rings and the settings, and the lines that draw them
@records ― drawing, added to passes, after the passes that push; clear whenever you like.
@param ― options.width a 'use gpu' function; it and shade carry the fills of what they read,
such as tgpu.fn(swell).pipe(noise.inject()). The ribbons face the camera of the pass
description they are drawn in, which width and shade read too
@returns ― a set of trails you destroy
create(
constroot:TgpuRoot
root,{
Options.capacity: number
Sets the number of trails to an integer of at least 1.
capacity:8,
Options.length: number
Sets the number of points per trail to an integer of at least 2. Pushing beyond this count
drops the oldest point.
Returns the trail width in world units at a point. Default settings.width at the newest
point, tapering with along to 0 at the oldest point of a full trail.
Returns premultiplied fragment colour. across ranges from -1 to 1 across the width. The
drawing multiplies it by how much of the pixel the trail covers. Default settings.color,
with soft edges and a fade towards the oldest point.
Both functions can read cameras.current.$. If one reads another module, fill that dependency. For example, a width function that samples cached Perlin noise uses width: tgpu.fn(swell).pipe(noise.inject()).
Creates trails as rings of recent points, pushed by kernels or written on the CPU, and draws
them as lines. Throws unless capacity is an integer of at least 1 and length is an integer
of at least 2.
@injects ― per-instance push, reset, pointAt and held, for shaders piped with it
@binds ― 2 storage: push or pointAt, the points and the rings
@binds ― 1 storage: held or reset alone, the rings
@binds ― 2 storage, 2 uniform: drawing, the points and the rings, the settings and
passes.pixels
@owns ― 3 buffers, a lines: the points, the rings and the settings, and the lines that draw them
@records ― drawing, added to passes, after the passes that push; clear whenever you like.
@param ― options.width a 'use gpu' function; it and shade carry the fills of what they read,
such as tgpu.fn(swell).pipe(noise.inject()). The ribbons face the camera of the pass
description they are drawn in, which width and shade read too
@returns ― a set of trails you destroy
create(
constroot:TgpuRoot
root,{
Options.capacity: number
Sets the number of trails to an integer of at least 1.
capacity:16,
Options.length: number
Sets the number of points per trail to an integer of at least 2. Pushing beyond this count
drops the oldest point.
Apply wakes.inject() to a shader to read trail points. held(trail) returns the point count. pointAt(trail, k) reads point k counting backward from the newest, with k below held. This kernel finds trail directions for arrowheads or sprites:
All trails in a set have the same length. Size it for the longest trail.
A stopped trail keeps its points until pushes or a write replace them. To fade it, keep each trail’s stop time in a buffer of your own and read it by point.trail in shade.
along counts points rather than distance. Distance-based dashes and textures need your own calculation.
Ribbons are joined on the screen, so they always face the camera.
A segment that crosses the near plane is cut there with no cap.
A point pushed where the last one lies ends the ribbon there with endCap and starts it again with startCap. Unless both caps are round, push only points that moved.