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
shadow?takes
the sun's shadow, with a depth pass per cascade
pointShadow?takes
shadows that cast gives to lights of points.table, with a depth pass per face
Offsets depth written by caster pipelines to prevent self-shadowing.
lift?
number= 1
Moves a shaded point along its normal before comparison, in texels at its distance from the light.
enabled?
() => boolean= always
Controls whether record draws the shadows in the current frame.
records
shadow.record→encoder
pointShadow.record→encoder
shadow.record and pointShadow.record with what was added to them, before the passes that shade.
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.
lifecycle
✓destroy()✓[Symbol.dispose]
injects
hook
The shading hooks its options name: reflect, ambient, fog, sunShadow and attenuation.
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.
2 storage · 1 uniform
shading.shade with a table of point lights.
3 storage · 1 uniform
shading.shade with a table of lights whose positions are on the device, the table's two parts.
1 uniform
shading.shade with points.light or points.spot, plus what the function binds.
none
shading.shade with points.candidates, plus what their functions bind, 2 storage for a neighbour grid's range and slot.
1 uniform
shading.shade with a shadow, the sun's light space and cascades, beside the depth array and its comparison sampler.
1 uniform
shading.shade with pointShadow, each shadow's light, layers and views, beside their depth array and the comparison sampler.
Lower roughness to shrink and brighten highlights. Select lambert to remove highlights and give even metals diffuse lighting.
Copying a light loop into each fragment shader duplicates work. Capturing lights as constants creates pipelines when lights move or are added. Store the sun, sky, fog and point lights as data in a lighting. Fill it into each pipeline and call shading.shade(material, point).
Allocates GPU buffers and a CPU copy of host rows. Throws unless capacity is an integer in [1,
2^20].
@param ― def the definition its shaders were written against, from table.define
@returns ― a table you destroy
create(
constroot:TgpuRoot
root,
import lighting
lighting.
constPointLight:table.TableDef<"PointLight",{
position:d.Vec3f;
range:d.F32;
color:d.Vec3f;
intensity:d.F32;
},{}>
export PointLight
PointLight,{
Options<T extends AnyTableDef>.capacity: number
Sets the table capacity for its lifetime. Must be an integer in [1, 2^20], with slots from 0
to capacity - 1. A row id uses enough bits for the slot and the rest for its generation. The
generation has 29 bits at 8 rows and 12 bits at 2^20 rows.
Sets values for fields omitted by add. Without defaults, every field starts at zero. Default
none.
defaults:{...
import lighting
lighting.
constWHITE:{
readonly intensity:number;
readonly color:d.v3f;
readonly range:number;
}
Holds white, intensity 1 and range 10, for the defaults of a table of point lights, since
table fields start at zero: table.create(root, lighting.PointLight, { capacity, defaults: { ...lighting.WHITE } }).
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];
readonly sunIntensity:2;
}&((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];
readonly sunIntensity: 2;
})
Sets initial sun, sky and fog values over defaults. Default defaults, which has no fog.
Enables point lights from a table or a function. Default none.
points:{
table:table.Table<table.TableDef<"PointLight",{
position:d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
},{}>>&lighting.LightTable
Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or
MovingSpotLight. Its fields start at zero, so give it defaults such as lighting.WHITE.
Flush and destroy it yourself. Default none, lights from light or spot.
Computes the final surface colour. It combines ambient light, shadowed sunlight, live point
lights and emission, then applies fog. Every channel stays at or below 65 504, the largest half
float. Create the pipeline from root.pipe(light.inject()) after injecting the kit camera.
shade(
constbrick:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
brick,
import shading
shading.
constpointAt:(position:d.v3f,normal:d.v3f)=>{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
Creates a point at position with surface normal normal, viewed from the kit camera.
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
Uploads changes since the last flush with queue.writeBuffer. Writes reach the queue before
the next submit. Call once per frame before passes that read the table. A HistoryTable takes
the frame's encoder and throws without it.
flush();// uploads lamps changed since the last frame
// Record passes here
}),
);
shade sums ambient sky light, sunlight past its shadow, live point lights and emission, and applies fog last. The result is linear and can exceed 1 for bloom and tone mapping. Every channel stays at or below 65 504, the largest half float.
shading.pointAt reads the kit’s cameras.current to find the direction toward the eye. Fill the camera into the pipeline too.
A lighting without a shadow or point lights holds only its settings uniform, so shading a surface of your own, such as a raymarched field, takes one small buffer.
Light from a panorama or a sky goes through the ambient hook as an environment. Supply other baked or changing light through it too. See Light from textures.
Intensity 1 lights a white matte surface facing the light to about 1, with no exposure, so daylight and lamps share units. Lights omit 1/π; divide intensities from a renderer that keeps it by π.
A material uses glTF’s metallic-roughness model, with linear colour, roughness, metallic factor, emission and occlusion. Presets run on the CPU as well as the GPU.
Preset
matte(color)
Diffuse reflection for chalk, clay and cloth.
glossy(color, roughness)
A dielectric with a highlight, such as paint, plastic or wet stone.
metal(color, roughness)
Tints its reflections and has no diffuse colour.
glow(color, strength)
Gives off strength times color, and reflects almost nothing.
Create a material with shading.Material({ color, roughness, emission, metallic, occlusion }), or copy a preset and change the fields you need:
const
constember:(heat:number)=>{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
ember=(
heat: number
heat:number)=>{
'use gpu';
const
constbase:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
base=
import materials
materials.
constmatte:(color:d.v3f)=>{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
Creates a diffuse material, such as chalk, clay or cloth.
matte(
import d
d.
constvec3f:d.Vec3f
(x:number,y:number,z:number)=>d.v3f(+5 overloads)
vec3f(0.3,0.1,0.05));
const
constlit:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
lit=
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(
constbase:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
base);
constlit:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
lit.
emission:d.v3f
Sets the emitted light in linear colour space. Shading adds it after lighting.
emission=
constbase:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
base.
emission:d.v3f
Sets the emitted light in linear colour space. Shading adds it after lighting.
emission+
import d
d.
constvec3f:d.Vec3f
(x:number,y:number,z:number)=>d.v3f(+5 overloads)
vec3f(1,0.4,0.1)*
heat: number
heat;
return
constlit:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
lit;
};
Convert screen colours to linear light first with srgbToLinear from @typegpu/color.
occlusion is the share of ambient light that reaches the surface, from 0 in a crevice to 1 in the open. hemisphere and an environment’s ambient apply it; presets set 1.
A shading.Point gives the shading position in world space, unit normal and view toward the eye, and uv and uv1 coordinates, the second from a mesh’s uvs1. shading.pointAt(position, normal) normalizes the normal and sets both to zero. An instance’s material function receives mesh UV coordinates.
Apply model textures with materials.textured, mappedNormal and masked. textured reads occlusion from the red channel of glTF’s occlusion texture. See mesh-pool textures.
A smooth surface’s highlight can be smaller than a pixel and sparkle as it moves. materials.antialiased(material, normal) widens roughness where the normal turns within a pixel. Kit renderers apply it unless their specularAntialiasing option is false; call it in your own fragment shaders.
Change range to set where light falls to zero. Each ball carries a row of a lighting.MovingPointLight table, and one kernel moves balls and lights without CPU position writes.
points reads lights from a table you own or from a function. Without points, the shader has no point-light loop. A table’s fields start at zero, so give it lighting.WHITE as defaults: white, intensity 1 and range 10.
Allocates GPU buffers and a CPU copy of host rows. Throws unless capacity is an integer in [1,
2^20].
@param ― def the definition its shaders were written against, from table.define
@returns ― a table you destroy
create(
constroot:TgpuRoot
root,
import lighting
lighting.
constPointLight:table.TableDef<"PointLight",{
position:Vec3f;
range:F32;
color:Vec3f;
intensity:F32;
},{}>
export PointLight
PointLight,{
Options<T extends AnyTableDef>.capacity: number
Sets the table capacity for its lifetime. Must be an integer in [1, 2^20], with slots from 0
to capacity - 1. A row id uses enough bits for the slot and the rest for its generation. The
generation has 29 bits at 8 rows and 12 bits at 2^20 rows.
Sets values for fields omitted by add. Without defaults, every field starts at zero. Default
none.
defaults:{...
import lighting
lighting.
constWHITE:{
readonly intensity:number;
readonly color:v3f;
readonly range:number;
}
Holds white, intensity 1 and range 10, for the defaults of a table of point lights, since
table fields start at zero: table.create(root, lighting.PointLight, { capacity, defaults: { ...lighting.WHITE } }).
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
Enables point lights from a table or a function. Default none.
points:{
table:table.Table<table.TableDef<"PointLight",{
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
},{}>>&lighting.LightTable
Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or
MovingSpotLight. Its fields start at zero, so give it defaults such as lighting.WHITE.
Flush and destroy it yourself. Default none, lights from light or spot.
Call lamps.flush() each frame before shading. Flush and destroy the table yourself.
A light falls off with the inverse square of distance, eased to zero at range.
shading.falloff and shading.beam run on the CPU as well as in shaders, so code outside shaders gets the GPU’s numbers for how much of a light reaches a point, before shadows.
A table of lighting.MovingPointLight keeps positions on the GPU for kernels to write. The balls in the figure use this. Build the kernel from root.pipe(lamps.inject()) and find slots with lamps.slotOf(id).
Sets the table capacity for its lifetime. Must be an integer in [1, 2^20], with slots from 0
to capacity - 1. A row id uses enough bits for the slot and the rest for its generation. The
generation has 29 bits at 8 rows and 12 bits at 2^20 rows.
Sets values for fields omitted by add. Without defaults, every field starts at zero. Default
none.
defaults:{...
import lighting
lighting.
constWHITE:{
readonly intensity:number;
readonly color:d.v3f;
readonly range:number;
}
Holds white, intensity 1 and range 10, for the defaults of a table of point lights, since
table fields start at zero: table.create(root, lighting.PointLight, { capacity, defaults: { ...lighting.WHITE } }).
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
Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or
MovingSpotLight. Its fields start at zero, so give it defaults such as lighting.WHITE.
Flush and destroy it yourself. Default none, lights from light or spot.
Click the sky to launch a shell, change the rate, or lower burst light. Each flash lights the castle and the smoke, and the lake reflects it; the readout counts live lights.
If another buffer already stores lights, supply points.light, a 'use gpu' function that returns a lighting.Light per slot, and count, the slots to visit. A range of 0 disables a slot. Use pipe to fill its dependencies. These flashes read their particles directly without a table or copy:
Returns whether slot i is alive in shaders created from root.pipe(system.inject()).
alive(
slot: number
slot)),
color:d.v3f
Sets the light colour in linear colour space.
color:
constflash:{
position:d.v3f;
age:number;
velocity:d.v3f;
life:number;
color:d.v4f;
size:number;
seed:number;
}
flash.
color:d.v4f
Holds linear colour with opacity in w.
color.
xyz:d.v3f
xyz,
intensity: number
Multiplies color.
intensity:
import std
std.
functionexp(value:number):number(+1overload)
export exp
exp(-
constflash:{
position:d.v3f;
age:number;
velocity:d.v3f;
life:number;
color:d.v4f;
size:number;
seed:number;
}
flash.
age: number
Holds particle age in seconds.
age*3)*
constflash:{
position:d.v3f;
age:number;
velocity:d.v3f;
life:number;
color:d.v4f;
size:number;
seed:number;
}
flash.
size: number
Sets the particle width in world units.
size*
constflash:{
position:d.v3f;
age:number;
velocity:d.v3f;
life:number;
color:d.v4f;
size:number;
seed:number;
}
flash.
size: number
Sets the particle width in world units.
size,
});
};
const
constlight:lighting.Lighting
light=
import lighting
lighting.
functioncreate<{
readonly points:{
readonly light:TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range:number;
color:d.v3f;
intensity:number;
}>;
readonly count:number;
};
}>(root:TgpuRoot,options?:({
readonly points:{
readonly light:TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range:number;
color:d.v3f;
intensity:number;
}>;
readonly count:number;
};
}&lighting.Options&{
readonly points:{
readonly light:TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range:number;
color:d.v3f;
intensity:number;
}>;
readonly count:number;
};
})|undefined):lighting.Lighting
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,{
points:{
readonly light: TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range: number;
color:d.v3f;
intensity: number;
}>;
readonly count: number;
}&lighting.LightPoints&{
readonly light: TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range: number;
color:d.v3f;
intensity: number;
}>;
readonly count: number;
}
Enables point lights from a table or a function. Default none.
points:{
light: TgpuGenericFn<(slot:number)=>{
position:d.v3f;
range: number;
color:d.v3f;
intensity: number;
}>&((slot:number)=>lighting.Light)
Returns the light at a slot. Default none, lights from table or spot.
Fills emit and pool resources for pipelines created from the configuration you pipe it
into.
inject()),
count: number
Sets how many slots, from 0, every lit point visits. Required unless candidates names them.
A light with range 0 lights nothing. Default none, the slots candidates names.
Gives the dispatch range for kernels over the pool. It includes every slot because any slot
can hold a particle and the CPU does not track live slots.
span},
});
Particles lit in a kernel, such as smoke beside the flashes, read shading.incoming(position) from a pipeline filled with light.inject(): the sun past its shadow, each point light through attenuation and the mean sky, arriving from every direction. Multiply a colour by it.
To light them by the point lights without the sun, fill shading.sunShadow with 0 on their pipeline alone. The fireworks’ smoke shows only in the bursts’ light this way, while the moon still lights the lake:
const
constsunless:shading.SunShadowHook
sunless:
import shading
shading.
typeSunShadowHook=(point:shading.Point)=>number
Returns the share of sunlight that reaches a point, as shadows.filtered does. A value of 0
means full shadow and 1 means full light. Values above 1 can model focused light.
SunShadowHook=()=>{
'use gpu';
return
import d
d.
functionf32(v?:number|boolean):number
export f32
A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)
Creates a GPU particle pool. CPU bursts and kernel emit calls allocate ring slots. Fixed
steps move particles, and live lists list only live particles for drawing.
@injects ― per-instance emit and the pool, for kernels piped with it
@binds ― 2 storage: emit, the pool and its ring counter
@binds ― 1 storage: alive or particle, the pool
@binds ― 1 storage, 1 uniform: drawnAt, the pool and the last compact's ahead
@owns ― 6 buffers, 2 pipelines, liveLists: the pool, its ring counter, the burst queue and three
uniforms; the live lists list the live particles. Each description drawing is added to builds
its pipeline.
@records ― spawn once a submission before compact, step once per fixed step, compact once
after the steps, then the draw a description added drawing as; clear whenever you like.
@param ― camera the camera the quads face unless place says otherwise; the callbacks read it too
@param ― options.update a 'use gpu' function; it and the other callbacks carry the fills of what
they read beside the camera, such as tgpu.fn(rise).pipe(noise.inject())
Sets the length of a step in seconds, such as a fixed-step clock's sim.step. Each step
advances by it.
step:
constsim:clock.FixedStep
sim.
FixedStep.step: number
Sets the step duration in seconds. A new value applies from the next advance: unstepped time
carries over, capped at maxSteps new steps, and time keeps the seconds already stepped.
Particles and machines take its value when created and keep it. Throws unless the value is
finite and above 0.
Updates every live particle at the start of each step, including its first, before gravity,
drag and motion. dt is the step duration in seconds. Use the slot for related data, such as
pushing a trail point. Return the particle to keep, or set life to 0 to end it. The step
kernel binds 1 storage buffer, the pool, so update and died share the other 7 of a stage.
Default none, which leaves it unchanged.
Returns the light arriving at position from every direction, for a particle or a volume lit
the same from all sides: the sun through sunShadow, each point light through attenuation,
and the mean of the sky and ground colours. Multiply a colour by it. Hooks receive a point with
a zero normal and view, so a lift along the normal does nothing. It runs in any stage, kernels
included, of a pipeline filled with light.inject(). Fill sunShadow with 0 on that pipeline to
leave the sun out.
Every lit pixel visits each light slot: up to a table’s highest live slot, or count slots of a function. Out-of-range lights still cost loads and a distance test, so size tables to the lights you use. If many lights each cover a small area, supply points.candidates to visit only slots that may reach the point.
A neighbour grid with sight at least the longest light range finds nearby lamps in 27 cells:
Allocates GPU buffers and a CPU copy of host rows. Throws unless capacity is an integer in [1,
2^20].
@param ― def the definition its shaders were written against, from table.define
@returns ― a table you destroy
create(
constroot:TgpuRoot
root,
import lighting
lighting.
constPointLight:table.TableDef<"PointLight",{
position:d.Vec3f;
range:d.F32;
color:d.Vec3f;
intensity:d.F32;
},{}>
export PointLight
PointLight,{
Options<T extends AnyTableDef>.capacity: number
Sets the table capacity for its lifetime. Must be an integer in [1, 2^20], with slots from 0
to capacity - 1. A row id uses enough bits for the slot and the rest for its generation. The
generation has 29 bits at 8 rows and 12 bits at 2^20 rows.
Sets values for fields omitted by add. Without defaults, every field starts at zero. Default
none.
defaults:{...
import lighting
lighting.
constWHITE:{
readonly intensity:number;
readonly color:d.v3f;
readonly range:number;
}
Holds white, intensity 1 and range 10, for the defaults of a table of point lights, since
table fields start at zero: table.create(root, lighting.PointLight, { capacity, defaults: { ...lighting.WHITE } }).
Creates a grid that sorts items by cell so kernels can find nearby items. Walks visit the home
cell first and choose directions at random, for boids, crowds or collisions. Throws unless
sight is above 0.
@injects ― per-instance walk, around, range, neighbour, slot, slotAt, neighbourAt,
count, holds and block, for shaders piped with it
@binds ― 1 uniform: walk or around, the box
@binds ― 2 storage: range and neighbour or neighbourAt, each cell's sorted range and each item's copy
@binds ― 1 storage: slot or slotAt, the sorted slots
@binds ― 1 storage: count, the sorted count
@binds ― 3 storage: holds and block, which blocks hold items, the sort count and their list
@owns ― buffers, a uniform, pipelines, bind groups, a sorter with radix: each cell's sorted
range, the blocks holding items, each item's copy in sorted order, the dispatches, the box, the
binning's resources
@records ― sort in a compute pass each step, before the kernels that walk
@param ― options.sight the cell side length. A walk finds every item within this distance
@param ― options.alive tests whether a slot holds an item. It, position and itemOf need fills for
the resources they read. Spread a table's rows.live() into the options to include its
live test, capacity and span
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
Enables point lights from a table or a function. Default none.
points:{
table:table.Table<table.TableDef<"PointLight",{
position:d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
},{}>>&lighting.LightTable
Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or
MovingSpotLight. Its fields start at zero, so give it defaults such as lighting.WHITE.
Flush and destroy it yourself. Default none, lights from light or spot.
Limits the loop to lights that may reach each shaded point. Default every live slot.
candidates:
constcells:grid.Grid<d.U32>
cells.
Grid<U32>.candidates(): grid.Candidates
Returns nearby ranges with the grid's fills piped in. Use it for a lighting's
points.candidates when the items are lights and sight covers every light range.
candidates()},
});
Each frame, call lamps.flush() and then cells.sort(pass) in a compute pass before shading.
cells.candidates() returns nine ranges covering the surrounding cells. Its range and slot add two storage buffers to each shading stage.
Lights beyond sight can be missed. Repeating a slot in candidates adds its light twice.
Custom candidates supply ranges, a range(position, r) function returning first and count, and slot(index) to map indices to light slots. They also work with a light function. For a single list, return one range with lighting.Span({ first: 0, count }).
A spot light is a point light with a cone. A table of lighting.SpotLight gives every light an aim and a cone, so points and spots share one table, one loop and one candidate list:
Allocates GPU buffers and a CPU copy of host rows. Throws unless capacity is an integer in [1,
2^20].
@param ― def the definition its shaders were written against, from table.define
@returns ― a table you destroy
create(
constroot:TgpuRoot
root,
import lighting
lighting.
constSpotLight:table.TableDef<"SpotLight",{
aim:Vec3f;
cone:Vec2f;
position:Vec3f;
range:F32;
color:Vec3f;
intensity:F32;
},{}>
export SpotLight
SpotLight,{
Options<T extends AnyTableDef>.capacity: number
Sets the table capacity for its lifetime. Must be an integer in [1, 2^20], with slots from 0
to capacity - 1. A row id uses enough bits for the slot and the rest for its generation. The
generation has 29 bits at 8 rows and 12 bits at 2^20 rows.
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
Enables point lights from a table or a function. Default none.
points:{
table:table.Table<table.TableDef<"SpotLight",{
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
},{}>>&lighting.LightTable
Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or
MovingSpotLight. Its fields start at zero, so give it defaults such as lighting.WHITE.
Flush and destroy it yourself. Default none, lights from light or spot.
Holds the cone's scale and offset from lighting.cone(inner, outer). The default [0, 1]
lights every direction.
cone:
import lighting
lighting.
constcone:(inner:number,outer:number)=>v2f
Returns a cone's scale and offset for a spot light's cone, from its inner and outer
half-angles in radians. Light falls from full at inner to none at outer, as glTF's spot
lights do. It runs on the CPU and in shaders.
cone(0.18,0.24),// inner and outer half-angles in radians
Light is full inside the inner half-angle and falls to none at the outer one, as in glTF. lighting.cone(inner, outer) stores glTF’s scale and offset, and runs on the CPU and in shaders.
lighting.WHITE_SPOT aims straight down with the cone [0, 1], which lights every direction.
The loop reads a light’s aim and cone only once its range test passes.
lighting.MovingSpotLight moves only the position to the device part.
points.spot takes a function that returns a lighting.Spot, a Light with aim and cone, beside count or candidates.
shading.beam(toLight, aim, cone) gives the share of a spot light along toLight, a unit direction from the point towards the light.
light.settings is a lighting.Settings uniform containing sun, sky and fog values. patch uploads supplied fields. A moving sun can update once per frame, and the shadow follows a new sunDirection at its next record:
const
constdirection:[number,number,number]
direction:[number,number,number]=[
var Math:Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.
Math.cos(x: number): number
Returns the cosine of a number.
@param ― x A numeric expression that contains an angle measured in radians.
cos(
constt:0.6
t),
var Math:Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.
Math.sin(x: number): number
Returns the sine of a number.
@param ― x A numeric expression that contains an angle measured in radians.
sin(
constt:0.6
t),0.3];
constlight:lighting.LightingOf<{
readonly shadow:{};
}>
light.
Lighting.settings: TgpuUniform<WgslStruct<{
sunDirection:Vec3f;
sunIntensity:F32;
sunColor:Vec3f;
skyIntensity:F32;
skyTop:Vec3f;
fogDensity:F32;
skyBottom:Vec3f;
fogFalloff:F32;
fogColor:Vec3f;
skyReflection:F32;
fogStart:F32;
}>>
Holds the sun, sky and fog values from defaults and the settings option. Use patch to
upload only the fields you supply. The shadow's next record follows a sunDirection written
here.
fogDensity is per world unit at y = 0, and falls by a factor of e per 1 / fogFalloff units of height. A falloff of 0 gives uniform fog. Negative falloff is treated as 0.
fogStart is the distance from the eye, in world units, where fog begins, so a near island stays clear while the far sea hazes. It is 0 by default.
skyReflection scales sky reflections in addition to skyIntensity. Values above 1 brighten glass and clear-plastic rims without changing matte surfaces.
points, shadow, pointShadow and shading are fixed at creation.
shade calls five functions, which the shading options replace:
Slot
Called
Type
Its arguments
Default
shading.reflect
For the sun and each point light
shading.ReflectHook
material, point, direction, radiance
shading.pbr
shading.ambient
Once, for the light from elsewhere
shading.AmbientHook
material, point
shading.hemisphere
shading.fog
Last, on the sum
shading.FogHook
color, point
shading.heightFog
shading.sunShadow
Once, for how much sun reaches
shading.SunShadowHook
point
shadows.filtered
shading.attenuation
For each point light in range
shading.AttenuationHook
point, toLight, distance, range, slot
shading.reaching
reflect, ambient and fog return linear d.v3f colours. direction is a unit vector toward the light. radiance is colour times intensity and attenuation, with the shadow also applied to the sun.
sunShadow returns 0 for shadow and 1 for full sunlight. Values above 1 can represent focused light. See your own sun shadow.
attenuation returns the share of a light’s intensity reaching the point. The loop reads the light’s colour and calls reflect only where it is above 0. reaching multiplies shading.falloff(distance, range), shading.beamOf(toLight, slot) and, where that is above 0, shadows.shadowed(point, toLight, distance, slot). Use slot to read data of your own, such as a flicker per light or a cookie texture.
Returns how much of the view fog hides between the camera and the point, from 0 to 1, from the
lighting's fogDensity, fogFalloff and fogStart. Density falls by a factor of e every 1 / fogFalloff world units above y = 0, and the fog begins fogStart world units from the eye.
Mix towards a fog colour of your own with it.
haze(
point:{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
point));
};
const
constlight:lighting.Lighting
light=
import lighting
lighting.
functioncreate<{
readonly shading:{
readonly reflect:shading.ReflectHook;
readonly fog:shading.FogHook;
};
readonly settings:{
readonly fogDensity:0.02;
readonly fogFalloff:0.2;
};
}>(root:TgpuRoot,options?:({
readonly shading:{
readonly reflect:shading.ReflectHook;
readonly fog:shading.FogHook;
};
readonly settings:{
readonly fogDensity:0.02;
readonly fogFalloff:0.2;
};
}&lighting.Options&{
readonly shading:{
readonly reflect:shading.ReflectHook;
readonly fog:shading.FogHook;
};
readonly settings:{
...;
};
})|undefined):lighting.Lighting
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,{
shading:{
readonly reflect:shading.ReflectHook;
readonly fog:shading.FogHook;
}&lighting.Shading&{
readonly reflect:shading.ReflectHook;
readonly fog:shading.FogHook;
}
Sets the functions that shading.shade calls. Default pbr, hemisphere, heightFog,
filtered and reaching.
shading:{
reflect:shading.ReflectHook
Computes how a surface reflects the sun and each point light. Default shading.pbr.
reflect:
consttoon:shading.ReflectHook
toon,
fog:shading.FogHook
Applies fog to the surface colour. Default shading.heightFog.
fog:
consthorizon:shading.FogHook
horizon},
settings:{
readonly fogDensity:0.02;
readonly fogFalloff:0.2;
}&((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 fogDensity:0.02;
readonly fogFalloff: 0.2;
})
Sets initial sun, sky and fog values over defaults. Default defaults, which has no fog.
settings:{
fogDensity:0.02
fogDensity:0.02,
fogFalloff:0.2
fogFalloff:0.2},
});
shading.haze(point) is how much the lighting’s height fog hides the point, from 0 to 1. heightFog mixes towards fogColor by it; mix towards a colour of your own, as above.
shading.settings() reads sun, sky and fog values from light.settings. Changing a value needs no new pipeline.
shading.lambert omits highlights for cheaper shading on phones and flat-shaded art. It tints all reflection with the surface colour, including metals.
shading.surrounded(material, point, irradiance, reflection) combines diffuse and reflected light for an ambient hook of your own. It applies the material’s occlusion to the reflected light only. See lightmaps.
shading.reflectionOf(point, roughness) returns the direction to sample reflected light along.
shading.reflectance and shading.specularOcclusion are the terms surrounded uses.
shading.unfogged, direct, sun and towardsSun are the parts of shade, for shaders that combine them differently.
Every pipeline built from light.inject() binds the resources its hooks read. A hook passed to lighting.create reaches every lit renderer, so a fog that reads a ground paint texture and its uniforms adds those bindings to every figure and prop pipeline too. Each stage has 8 storage and 12 uniform buffers to share, so give a hook that only some renderers need to those renderers alone:
Renderer pipe runs after the lighting, so its fill replaces the lighting’s. Fill your own pipelines the same way, root.pipe(light.inject()).with(shading.reflect, shading.lambert). Pipelines lists what each pipeline binds.
Glass that hides only a tenth of its background can still show full-strength highlights and sky reflections. Use shading.shadeClear(material, point, opacity) for this behavior. It returns premultiplied colour for pipelines.blend.over:
const
constpane:(world:d.v3f,normal:d.v3f)=>d.v4f
pane=(
world:d.v3f
world:
import d
d.
export v3f
Interface representing its WGSL vector type counterpart: vec3f or vec3.
A vector with 3 elements of type f32
v3f,
normal:d.v3f
normal:
import d
d.
export v3f
Interface representing its WGSL vector type counterpart: vec3f or vec3.
A vector with 3 elements of type f32
v3f)=>{
'use gpu';
const
constglass:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
glass=
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.
Shades a clear surface such as glass or clear plastic. The result is premultiplied for
pipelines.blend.over. opacity scales diffuse light, emission and fog. Reflections,
including highlights and sky light, contribute at full strength. A metal has only reflected
light, so opacity scales only its fog. Every channel stays at or below 65 504.
shadeClear(
constglass:{
color:d.v3f;
roughness:number;
emission:d.v3f;
metallic:number;
occlusion:number;
}
glass,
import shading
shading.
constpointAt:(position:d.v3f,normal:d.v3f)=>{
position:d.v3f;
normal:d.v3f;
view:d.v3f;
uv:d.v2f;
uv1:d.v2f;
}
Creates a point at position with surface normal normal, viewed from the kit camera.
pointAt(
world:d.v3f
world,
normal:d.v3f
normal),0.1);
};
opacity scales the diffuse light, the emission and the fog over the surface; the reflected light is added whole. w is opacity.
A metal reflects all it shows, so opacity only fades its fog.
An instances renderer with blend: 'clear' shades through it.
Call light.destroy() when nothing draws with its resources. It frees the settings uniform and the shadow maps. Methods that would use them for GPU work then throw The lighting was destroyed. A table of lights is yours to destroy.
Without candidates, every shaded pixel visits each light slot. There is no tile or cluster culling.
shadeClear is exact for dielectrics and metals. It approximates intermediate metallic values and custom fog hooks that do more than mix toward a colour.
Two lightings in one frame, such as a lit room seen through a window, need two sets of pipelines.