Skip to content

@typegpu/kitlighting · shading · materials

Lighting

A sun, a sky, fog and point lights, and the shading your own shaders call.

kit.lighting

module

Creates lighting with a sun, sky and fog.

lighting.create(root, { points?, shadow?, pointShadow?, … }?) → Lighting

rootborrows
the root
points?takes
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
Lightingreturns
lighting you destroy
GPU functions
cone
schemas
LightSpotSettingsSpan
constants
defaultsWHITE
helpers
MovingPointLightMovingSpotLightPointLightSpotLightWHITE_SPOT
types
ShadingLightTableTablePointsLightPointsSpotPointsCandidatesPointLightsMovingPointLightsSpotLightsMovingSpotLightsLightingLightingOf
Optionscreate · 5 fields
settings?

Partial<d.InferInput<typeof Settings>>= defaults, which has no fog

Sets initial sun, sky and fog values over defaults.

shading?

Shading= pbr, hemisphere, heightFog, filtered and reaching

Sets the functions that shading.shade calls.

points?

PointsOptions= none

Enables point lights from a table or a function.

shadow?

ShadowOptions= none

Enables and configures the shadow from the sun.

pointShadow?

PointShadowOptions= none

Gives up to count point or spot lights a shadow each, which light.pointShadow.cast assigns.

PointsOptionspoints in create · 5 fields
table?

LightTable= none, lights from light or spot

Supplies a table of lighting.PointLight, MovingPointLight, SpotLight or MovingSpotLight.

candidates?

Candidates= every live slot

Limits the loop to lights that may reach each shaded point.

light?

(slot: number) => Light= none, lights from table or spot

Returns the light at a slot.

spot?

(slot: number) => Spot= none, lights from table or light

Returns the spot light at a slot, whose cone comes from lighting.cone.

count?

number= none, the slots candidates names

Sets how many slots, from 0, every lit point visits.

ShadowOptionsshadow in create · 13 fields
extent?

number= 20

Sets the width of the square covered by the map in world units.

center?

Vec3= the origin

Centres the square here at first.

eye?

CpuView= none, a fixed map

Fits the map to this camera's view, in cascades layers that reach far from the eye.

cascades?

1 | 2 | 3 | 4= 1

Splits the view into 1 to 4 cascades, each a layer of the map drawn in a pass of its own.

far?

number= 50

Sets how far from the eye the cascades reach, in world units.

splits?

readonly number[]= (k / cascades)² after cascade k, 0.11 and 0.44 for three

Places the splits between cascades as fractions of far, cascades - 1 rising values between 0 and 1.

size?

number= 2048

Sets the width and height of each layer of the map in texels.

reach?

number= twice the extent, or far with an eye

Sets how far the map reaches towards the sun in world units: from the point it follows, or beyond each cascade with an eye.

filter?

1 | 4 | 9= 4

Sets the number of comparisons per shaded point to 1, 4 or 9.

format?

'depth16unorm' | 'depth24plus' | 'depth32float'= 'depth16unorm'

Sets the depth format of the map.

bias?

{ readonly constant?: number; readonly slope?: number }= { constant: 2, slope: 2 }

Offsets depth written by caster pipelines to prevent self-shadowing.

lift?

number= 1

Moves a shaded point along its normal before comparison, in texels.

enabled?

() => boolean= always

Controls whether record draws the shadow in the current frame.

PointShadowOptionspointShadow in create · 7 fields
count?

number= 1

Sets how many lights can cast a shadow at once, each with six layers of the map.

size?

number= 512

Sets the width and height of each layer in texels.

filter?

1 | 4 | 9= 4

Sets the number of comparisons per shaded point to 1, 4 or 9.

format?

'depth16unorm' | 'depth24plus' | 'depth32float'= 'depth16unorm'

Sets the depth format of the map.

bias?

{ readonly constant?: number; readonly slope?: number }= { constant: 2, slope: 2 }

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.recordencoder
  • pointShadow.recordencoder

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.

  • matte
  • glossy
  • metal
  • glow
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).

import {
import lighting
lighting
,
import materials
materials
,
import shading
shading
,
import table
table
} from '@typegpu/kit';
const
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
=
import table
table
.
function create<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>(root: TgpuRoot, def: table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>, options: table.Options<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>): table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>> (+1 overload)

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
(
const root: TgpuRoot
root
,
import lighting
lighting
.
const PointLight: 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.

capacity
: 16,
Options<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.defaults?: table.Values<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>> | undefined

Sets values for fields omitted by add. Without defaults, every field starts at zero. Default none.

defaults
: { ...
import lighting
lighting
.
const WHITE: {
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 } }).

WHITE
},
});
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.add(values?: table.Values<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>> | undefined): table.RowId<"PointLight">

Adds a row in the lowest free slot. Uses defaults for omitted fields. Throws when the table is full.

add
({
position?: ArrayLike<number> | d.InferInput<d.Vec3f> | undefined
position
: [1.5, 1, 1],
color?: ArrayLike<number> | d.InferInput<d.Vec3f> | undefined

Sets the light colour in linear colour space.

color
: [1, 0.6, 0.3],
intensity?: number | undefined

Multiplies color.

intensity
: 3,
range?: number | undefined

Sets the distance in world units where light intensity reaches zero.

range
: 5 });
const
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
readonly settings: {
readonly sunDirection: [0.4, 1, 0.3];
readonly sunIntensity: 2;
};
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
};
}>(root: TgpuRoot, options?: ({
readonly settings: {
readonly sunDirection: [0.4, 1, 0.3];
readonly sunIntensity: 2;
};
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
};
} & lighting.Options & {
...;
}) | 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
(
const root: 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.

settings
: {
sunDirection: [0.4, 1, 0.3]
sunDirection
: [0.4, 1, 0.3],
sunIntensity: 2
sunIntensity
: 2 },
points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
} & lighting.TablePoints & {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
}

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.

table
:
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
},
});
const
const fragment: TgpuFragmentFn<{
world: d.Vec3f;
normal: d.Vec3f;
}, d.Vec4f>
fragment
=
const tgpu: {
const: typeof import("node_modules/typegpu/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/tgpuUnstable");
}

@module ― typegpu

tgpu
.
fragmentFn: <{
world: d.Vec3f;
normal: d.Vec3f;
}, d.Vec4f>(options: {
in: {
world: d.Vec3f;
normal: d.Vec3f;
};
out: d.Vec4f;
}) => TgpuFragmentFnShell<{
world: d.Vec3f;
normal: d.Vec3f;
}, d.Vec4f> (+1 overload)
fragmentFn
({
in: {
world: d.Vec3f;
normal: d.Vec3f;
}
in
: {
world: d.Vec3f
world
:
import d
d
.
const vec3f: d.Vec3f
vec3f
,
normal: d.Vec3f
normal
:
import d
d
.
const vec3f: d.Vec3f
vec3f
},
out: d.Vec4f
out
:
import d
d
.
const vec4f: d.Vec4f
vec4f
,
})(({
world: d.v3f
world
,
normal: d.v3f
normal
}) => {
'use gpu';
const
const brick: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
brick
=
import materials
materials
.
const glossy: (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.

glossy
(
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(0.62, 0.24, 0.16), 0.4);
return
import d
d
.
const vec4f: d.Vec4f
(v0: AnyNumericVec3Instance, w: number) => d.v4f (+9 overloads)
vec4f
(
import shading
shading
.
const shade: (material: shading.Material, point: shading.Point) => d.v3f

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
(
const brick: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
brick
,
import shading
shading
.
const pointAt: (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
)), 1);
});
const
const pipeline: TgpuRenderPipeline<d.Vec4f>
pipeline
=
const root: TgpuRoot
root
.
WithBinding.pipe(transform: (cfg: Configurable) => Configurable): WithBinding
pipe
(
const view: cameras.OrbitCamera
view
.
CameraBase.inject(): Inject

Fills cameras.current for the pipelines created from the configuration it is piped into.

inject
())
.
WithBinding.pipe(transform: (cfg: Configurable) => Configurable): WithBinding
pipe
(
const light: lighting.Lighting
light
.
Lighting.inject(): Inject

Fills the shading hooks and resources for pipelines created from the configuration you pipe it into.

inject
())
.
WithBinding.createRenderPipeline<{}, {}, {
world: d.Vec3f;
normal: d.Vec3f;
}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {} | undefined;
vertex: TgpuVertexFn<{}, {
world: d.Vec3f;
normal: d.Vec3f;
}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<AnyAutoCustoms>);
fragment: TgpuFragmentFn<{
world: d.Vec3f;
normal: d.Vec3f;
} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<...>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
vertex: TgpuVertexFn<{}, {
world: d.Vec3f;
normal: d.Vec3f;
}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<AnyAutoCustoms>)
vertex
,
fragment: TgpuFragmentFn<{
world: d.Vec3f;
normal: d.Vec3f;
} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{
world: d.Vec3f;
normal: d.Vec3f;
}>>) => d.v4f | (AnyAutoCustoms & Partial<InferGPURecord<{
readonly $fragDepth: import("node_modules/typegpu/builtin").BuiltinFragDepth;
readonly $sampleMask: import("node_modules/typegpu/builtin").BuiltinSampleMask;
}>>))
fragment
,
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@default ― navigator.gpu.getPreferredCanvasFormat()

format
: 'rgba16float' } });
import frames
frames
.
function animate(loop: Pick<frames.Frames, "run"> & Partial<Pick<frames.Frames, "seconds">>, options?: frames.AnimateOptions): frames.Animation
export animate

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
(
import frames
frames
.
function create(root: TgpuRoot, record: Recorder): frames.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
(
const root: TgpuRoot
root
, (
frame: frames.Frame
frame
) => {
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.flush(encoder?: TgpuCommandEncoder): void

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 π.

kit.materials

shader

These 'use gpu' functions create materials for shading.shade, with colours or mesh textures.

kind
shader
GPU functions
matteglossymetalglowuvOftexturedantialiasedmaskedmappedNormal

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
const ember: (heat: number) => {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
ember
= (
heat: number
heat
: number) => {
'use gpu';
const
const base: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
base
=
import materials
materials
.
const matte: (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
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(0.3, 0.1, 0.05));
const
const lit: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
lit
=
import shading
shading
.
const Material: 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
(
const base: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
base
);
const lit: {
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
=
const base: {
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
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(1, 0.4, 0.1) *
heat: number
heat
;
return
const lit: {
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.

const
const lamps: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
=
import table
table
.
function create<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>(root: TgpuRoot, def: table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>, options: table.Options<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>): table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> (+1 overload)

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
(
const root: TgpuRoot
root
,
import lighting
lighting
.
const PointLight: 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.

capacity
: 16,
Options<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.defaults?: table.Values<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> | undefined

Sets values for fields omitted by add. Without defaults, every field starts at zero. Default none.

defaults
: { ...
import lighting
lighting
.
const WHITE: {
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 } }).

WHITE
},
});
const
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
}>(root: TgpuRoot, options?: ({
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
} & lighting.Options & {
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
}) | 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
(
const root: TgpuRoot
root
, {
points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
} & lighting.TablePoints & {
readonly table: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
}

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.

table
:
const lamps: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
} });
const
const torch: table.RowId<"PointLight">
torch
=
const lamps: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.add(values?: table.Values<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> | undefined): table.RowId<"PointLight">

Adds a row in the lowest free slot. Uses defaults for omitted fields. Throws when the table is full.

add
({
position?: ArrayLike<number> | InferInput<Vec3f> | undefined
position
: [0, 2, 0],
color?: ArrayLike<number> | InferInput<Vec3f> | undefined

Sets the light colour in linear colour space.

color
: [1, 0.55, 0.2],
intensity?: number | undefined

Multiplies color.

intensity
: 4 });
const lamps: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.fields: table.Fields<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>

Provides one handle per field. Keep a handle outside a loop instead of reading fields.x inside it.

fields
.
position: table.HostField<Vec3f, "PointLight">
position
.
function set(id: table.RowId<"PointLight">, x: number, y: number, z: number): void
set
(
const torch: table.RowId<"PointLight">
torch
, 0.5, 2, 0);
const lamps: table.Table<table.TableDef<"PointLight", {
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.remove(id: table.RowId<"PointLight">): void

Frees the row slot and makes its id stale.

remove
(
const torch: table.RowId<"PointLight">
torch
);
  • 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).

const {
const MovingPointLight: table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>
MovingPointLight
} =
import lighting
lighting
;
const
const lamps: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>
lamps
=
import table
table
.
function create<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>(root: TgpuRoot, def: table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>, options: table.Options<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>): table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>> (+1 overload)

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
(
const root: TgpuRoot
root
,
const MovingPointLight: table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>
MovingPointLight
, {
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.

capacity
: 64,
Options<TableDef<"MovingPointLight", { range: F32; color: Vec3f; intensity: F32; }, { position: Vec3f; }>>.defaults?: table.Values<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>> | undefined

Sets values for fields omitted by add. Without defaults, every field starts at zero. Default none.

defaults
: { ...
import lighting
lighting
.
const WHITE: {
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 } }).

WHITE
},
});
const
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
readonly points: {
readonly table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>;
};
}>(root: TgpuRoot, options?: ({
readonly points: {
readonly table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>;
};
} & lighting.Options & {
readonly points: {
readonly table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>;
};
}) | 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
(
const root: TgpuRoot
root
, {
points: {
readonly table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>;
} & lighting.TablePoints & {
readonly table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>;
}

Enables point lights from a table or a function. Default none.

points
: {
table: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>> & 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.

table
:
const lamps: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>
lamps
} });
const
const circle: TgpuComputeFn<{
id: d.BuiltinGlobalInvocationId;
}>
circle
=
const tgpu: {
const: typeof import("node_modules/typegpu/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/tgpuUnstable");
}

@module ― typegpu

tgpu
.
computeFn: <{
id: d.BuiltinGlobalInvocationId;
}>(options: {
in: {
id: d.BuiltinGlobalInvocationId;
};
workgroupSize: number[];
}) => TgpuComputeFnShell<{
id: d.BuiltinGlobalInvocationId;
}> (+1 overload)
computeFn
({
workgroupSize: number[]
workgroupSize
: [64],
in: {
id: d.BuiltinGlobalInvocationId;
}
in
: {
id: d.BuiltinGlobalInvocationId
id
:
import d
d
.
const builtin: {
readonly vertexIndex: d.BuiltinVertexIndex;
readonly instanceIndex: d.BuiltinInstanceIndex;
readonly clipDistances: d.BuiltinClipDistances;
readonly position: d.BuiltinPosition;
readonly frontFacing: d.BuiltinFrontFacing;
readonly fragDepth: d.BuiltinFragDepth;
readonly primitiveIndex: d.BuiltinPrimitiveIndex;
readonly sampleIndex: d.BuiltinSampleIndex;
... 11 more ...;
readonly numSubgroups: d.BuiltinNumSubgroups;
}
export builtin
builtin
.
globalInvocationId: d.BuiltinGlobalInvocationId
globalInvocationId
} })(({
id: d.v3u
id
,
}) => {
'use gpu';
if (
id: d.v3u
id
.
v3u.x: number
x
>=
const MovingPointLight: table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>
MovingPointLight
.
span: TgpuGenericFn
() => number

Returns the highest live slot plus one.

span
()) return;
const
const angle: number
angle
=
const time: TgpuUniform<d.F32>
time
.
TgpuUniform<F32>.$: number
$
+
import d
d
.
function f32(v?: number | boolean): number
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
(
id: d.v3u
id
.
v3u.x: number
x
);
const MovingPointLight: table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>
MovingPointLight
.
device: TgpuAccessor<table.Rows<{
position: d.Vec3f;
}>>

Provides GPU-written rows with read-write access in compute pipelines and read-only access in render pipelines.

device
.
TgpuAccessor<Rows<{ position: Vec3f; }>>.$: {
position: d.v3f;
}[]
$
[
id: d.v3u
id
.
v3u.x: number
x
]!.
position: d.v3f
position
=
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(
import std
std
.
function cos(value: number): number (+1 overload)
export cos
cos
(
const angle: number
angle
) * 4, 2,
import std
std
.
function sin(value: number): number (+1 overload)
export sin
sin
(
const angle: number
angle
) * 4);
});
const
const circling: TgpuComputePipeline
circling
=
const root: TgpuRoot
root
.
WithBinding.pipe(transform: (cfg: Configurable) => Configurable): WithBinding
pipe
(
const lamps: table.Table<table.TableDef<"MovingPointLight", {
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {
position: d.Vec3f;
}>>
lamps
.
Table<TableDef<"MovingPointLight", { range: F32; color: Vec3f; intensity: F32; }, { position: Vec3f; }>>.inject(): Inject

Fills the definition's accessors with this table's buffers.

inject
()).
WithBinding.createComputePipeline<{
id: d.BuiltinGlobalInvocationId;
}>(descriptor: TgpuComputePipeline.Descriptor<{
id: d.BuiltinGlobalInvocationId;
}>): TgpuComputePipeline
createComputePipeline
({
compute: TgpuComputeFn<{
id: d.BuiltinGlobalInvocationId;
}>
compute
:
const circle: TgpuComputeFn<{
id: d.BuiltinGlobalInvocationId;
}>
circle
});
  • A position supplied to add or write overwrites the kernel’s. Colour and intensity updates leave it unchanged.
  • The CPU can’t read a moving light’s position back.
fireworksEach burst is a point light over a castle reflected in a lake.
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:

const
const lightOf: (slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}
lightOf
= (
slot: number
slot
: number) => {
'use gpu';
const
const flash: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
flash
=
const flashes: particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>
flashes
.
Particles<WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.particle: (i: number) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}

Reads the particle in slot i, which must be below span. Create calling shaders from root.pipe(system.inject()).

particle
(
slot: number
slot
);
return
import lighting
lighting
.
function Light(props?: {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
} | undefined): {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}
export Light

This schema holds a point light with a position, range, linear colour and intensity.

Light
({
position: d.v3f
position
:
const flash: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
flash
.
position: d.v3f
position
,
range: number

Sets the distance in world units where light intensity reaches zero.

range
:
import std
std
.
function select(f: number, t: number, cond: boolean): number (+2 overloads)
export select

Returns t if cond is true, and f otherwise. Component-wise if cond is a vector.

@example select(1, 2, false) // returns 1 select(1, 2, true) // returns 2 select(vec2i(1, 2), vec2i(3, 4), true) // returns vec2i(3, 4) select(vec2i(1, 2), vec2i(3, 4), vec2b(false, true)) // returns vec2i(1, 4)

select
(
import d
d
.
function f32(v?: number | boolean): number
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
(0), 320,
const flashes: particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>
flashes
.
Particles<WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.alive: (i: number) => boolean

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
:
const flash: {
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
.
function exp(value: number): number (+1 overload)
export exp
exp
(-
const flash: {
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) *
const flash: {
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 flash: {
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
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
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
(
const root: 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.

light
:
const tgpu: {
const: typeof import("node_modules/typegpu/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/tgpuUnstable");
}

@module ― typegpu

tgpu
.
fn: <(slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}>(inner: (slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}) => TgpuGenericFn<(slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}> (+2 overloads)
fn
(
const lightOf: (slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}
lightOf
).
TgpuGenericFn<(slot: number) => { position: v3f; range: number; color: v3f; intensity: number; }>.pipe(transform: (cfg: Configurable) => Configurable): TgpuGenericFn<(slot: number) => {
position: d.v3f;
range: number;
color: d.v3f;
intensity: number;
}>

Applies a configuration transform to the slots and accessors this function reads, as root.pipe does to a pipeline's.

pipe
(
const flashes: particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>
flashes
.
Particles<WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.inject(): Inject

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.

count
:
const flashes: particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>
flashes
.
Particles<WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.span: number

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
const sunless: shading.SunShadowHook
sunless
:
import shading
shading
.
type SunShadowHook = (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
.
function f32(v?: number | boolean): number
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
(0);
};
const
const smoke: particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>
smoke
=
import particles
particles
.
function create<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>(root: TgpuRoot, camera: cameras.CameraBase, options: particles.Options<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>): particles.Particles<d.WgslStruct<{
position: d.Vec3f;
age: d.F32;
velocity: d.Vec3f;
life: d.F32;
color: d.Vec4f;
size: d.F32;
seed: d.F32;
}>>

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())

@returns ― a system you destroy

create
(
const root: TgpuRoot
root
,
const view: cameras.OrbitCamera
view
, {
Options<S extends Schema = WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.capacity: number

Limits the number of particles alive at once. Each emitted particle takes the next ring slot.

capacity
: 1024,
Options<S extends Schema = WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.step: number

Sets the length of a step in seconds, such as a fixed-step clock's sim.step. Each step advances by it.

step
:
const sim: 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.

step
,
Options<WgslStruct<{ position: Vec3f; age: F32; velocity: Vec3f; life: F32; color: Vec4f; size: F32; seed: F32; }>>.update?: ((particle: NoInfer<{
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}>, dt: number, slot: number) => NoInfer<{
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}>) | undefined

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.

update
:
const tgpu: {
const: typeof import("node_modules/typegpu/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/tgpuUnstable");
}

@module ― typegpu

tgpu
.
fn: <(puff: Particle) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}>(inner: (puff: Particle) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}) => TgpuGenericFn<(puff: Particle) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}> (+2 overloads)
fn
((
puff: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
puff
:
type Particle = {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}

This type describes a particle in shaders.

Particle
) => {
'use gpu';
let
let next: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
next
=
function Particle(props?: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
} | undefined): {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}

This type describes a particle in shaders.

Particle
(
puff: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
puff
);
let next: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
next
.
color: d.v4f

Holds linear colour with opacity in w.

color
=
import d
d
.
const vec4f: d.Vec4f
(v0: AnyNumericVec3Instance, w: number) => d.v4f (+9 overloads)
vec4f
(
import shading
shading
.
const incoming: (position: d.v3f) => d.v3f

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.

incoming
(
puff: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
puff
.
position: d.v3f
position
) * 0.6,
puff: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
puff
.
color: d.v4f

Holds linear colour with opacity in w.

color
.
v4f.w: number
w
);
return
let next: {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}
next
;
})
.
TgpuGenericFn<(puff: Particle) => { position: v3f; age: number; velocity: v3f; life: number; color: v4f; size: number; seed: number; }>.pipe(transform: (cfg: Configurable) => Configurable): TgpuGenericFn<(puff: Particle) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}>

Applies a configuration transform to the slots and accessors this function reads, as root.pipe does to a pipeline's.

pipe
(
const light: lighting.Lighting
light
.
Lighting.inject(): Inject

Fills the shading hooks and resources for pipelines created from the configuration you pipe it into.

inject
())
.
Withable<TgpuGenericFn<(puff: Particle) => { position: v3f; age: number; velocity: v3f; life: number; color: v4f; size: number; seed: number; }>>.with<shading.SunShadowHook>(slot: TgpuSlot<shading.SunShadowHook>, value: Eventual<shading.SunShadowHook>): TgpuGenericFn<(puff: Particle) => {
position: d.v3f;
age: number;
velocity: d.v3f;
life: number;
color: d.v4f;
size: number;
seed: number;
}> (+2 overloads)
with
(
import shading
shading
.
const sunShadow: TgpuSlot<shading.SunShadowHook>
sunShadow
,
const sunless: shading.SunShadowHook
sunless
),
});

The mean sky still arrives, as skyIntensity sets it.

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:

const
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
=
import table
table
.
function create<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>(root: TgpuRoot, def: table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>, options: table.Options<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>): table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>> (+1 overload)

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
(
const root: TgpuRoot
root
,
import lighting
lighting
.
const PointLight: 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.

capacity
: 64,
Options<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.defaults?: table.Values<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>> | undefined

Sets values for fields omitted by add. Without defaults, every field starts at zero. Default none.

defaults
: { ...
import lighting
lighting
.
const WHITE: {
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 } }).

WHITE
,
color?: ArrayLike<number> | d.InferInput<d.Vec3f> | undefined

Sets the light colour in linear colour space.

color
: [1, 0.8, 0.6],
range?: number | undefined

Sets the distance in world units where light intensity reaches zero.

range
: 8 },
});
const
const rows: Inject
rows
=
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.inject(): Inject

Fills the definition's accessors with this table's buffers.

inject
();
const
const cells: grid.Grid<d.U32>
cells
=
import grid
grid
.
function create<d.U32>(root: TgpuRoot, options: grid.Options<d.U32>): grid.Grid<d.U32>

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

@returns ― a grid you sort each step and destroy

create
(
const root: TgpuRoot
root
, {
...
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
.
Table<TableDef<"PointLight", { position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.live(): table.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
(),
GridOptions<T extends d.AnyWgslData>.low: d.v3f

Sets the initial lower corner of the grid box. move changes it. Items outside the box belong to the nearest cell.

low
:
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(-40, 0, -40),
GridOptions<T extends d.AnyWgslData>.size: d.v3f

Sets the box extent along x, y and z for the lifetime of the grid. A side within a thousandth of a cell of a whole number of cells takes that number.

size
:
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(80, 8, 80),
GridOptions<T extends d.AnyWgslData>.sight: number

Sets the cell side length. A walk finds every item within this distance.

sight
: 8, // must cover every lamp's range
item: d.U32
item
:
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
,
position: table.Getter<d.Vec3f>
position
:
import lighting
lighting
.
const PointLight: table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>
export PointLight
PointLight
.
position: table.Getter<d.Vec3f>
position
.
Getter<Vec3f>.pipe(transform: (cfg: Configurable) => Configurable): table.Getter<d.Vec3f>

Applies a configuration transform to the slots and accessors this function reads, as root.pipe does to a pipeline's.

pipe
(
const rows: Inject
rows
),
itemOf: (i: number) => number
itemOf
: (
i: number
i
) => {
'use gpu';
return
import d
d
.
function u32(v?: number | boolean): number
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
(
i: number
i
);
},
});
const
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
readonly candidates: grid.Candidates;
};
}>(root: TgpuRoot, options?: ({
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
readonly candidates: grid.Candidates;
};
} & lighting.Options & {
readonly points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
readonly candidates: {
...;
};
};
}) | 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
(
const root: TgpuRoot
root
, {
points: {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
readonly candidates: grid.Candidates;
} & lighting.TablePoints & {
readonly table: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>;
readonly candidates: {
readonly ranges: number;
readonly range: (position: d.v3f, r: number) => grid.Range;
readonly slot: (index: number) => number;
};
}

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.

table
:
const lamps: table.Table<table.TableDef<"PointLight", {
position: d.Vec3f;
range: d.F32;
color: d.Vec3f;
intensity: d.F32;
}, {}>>
lamps
,
candidates: grid.Candidates & lighting.Candidates & {
readonly ranges: number;
readonly range: (position: d.v3f, r: number) => grid.Range;
readonly slot: (index: number) => number;
}

Limits the loop to lights that may reach each shaded point. Default every live slot.

candidates
:
const cells: 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:

import {
import lighting
lighting
,
import table
table
} from '@typegpu/kit';
const
const lamps: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
=
import table
table
.
function create<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>(root: TgpuRoot, def: table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>, options: table.Options<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>): table.Table<...> (+1 overload)

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
(
const root: TgpuRoot
root
,
import lighting
lighting
.
const SpotLight: 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.

capacity
: 32,
Options<TableDef<"SpotLight", { aim: Vec3f; cone: Vec2f; position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.defaults?: table.Values<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> | undefined

Sets values for fields omitted by add. Without defaults, every field starts at zero. Default none.

defaults
: { ...
import lighting
lighting
.
const WHITE_SPOT: {
readonly intensity: number;
readonly color: v3f;
readonly range: number;
} & {
readonly aim: v3f;
readonly cone: v2f;
}

Holds WHITE with an aim straight down and a full cone, for a table of spot lights.

WHITE_SPOT
},
});
const
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
readonly points: {
readonly table: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
}>(root: TgpuRoot, options?: ({
readonly points: {
readonly table: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
} & lighting.Options & {
readonly points: {
readonly table: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
};
}) | 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
(
const root: TgpuRoot
root
, {
points: {
readonly table: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
} & lighting.TablePoints & {
readonly table: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>;
}

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.

table
:
const lamps: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
} });
const
const lamp: table.RowId<"SpotLight">
lamp
=
const lamps: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"SpotLight", { aim: Vec3f; cone: Vec2f; position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.add(values?: table.Values<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> | undefined): table.RowId<"SpotLight">

Adds a row in the lowest free slot. Uses defaults for omitted fields. Throws when the table is full.

add
({
position?: ArrayLike<number> | InferInput<Vec3f> | undefined
position
: [0, 7, 3],
aim?: ArrayLike<number> | InferInput<Vec3f> | undefined

Points the cone's axis away from the light. It can have any length.

aim
: [0, -1, -0.4], // any length
cone?: ArrayLike<number> | InferInput<Vec2f> | undefined

Holds the cone's scale and offset from lighting.cone(inner, outer). The default [0, 1] lights every direction.

cone
:
import lighting
lighting
.
const cone: (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
color?: ArrayLike<number> | InferInput<Vec3f> | undefined

Sets the light colour in linear colour space.

color
: [1, 0.92, 0.8],
intensity?: number | undefined

Multiplies color.

intensity
: 60,
range?: number | undefined

Sets the distance in world units where light intensity reaches zero.

range
: 16,
});
const lamps: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"SpotLight", { aim: Vec3f; cone: Vec2f; position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.fields: table.Fields<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>

Provides one handle per field. Keep a handle outside a loop instead of reading fields.x inside it.

fields
.
aim: table.HostField<Vec3f, "SpotLight">

Points the cone's axis away from the light. It can have any length.

aim
.
function set(id: table.RowId<"SpotLight">, x: number, y: number, z: number): void
set
(
const lamp: table.RowId<"SpotLight">
lamp
, 0.2, -1, -0.4);
const lamps: table.Table<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>>
lamps
.
Table<TableDef<"SpotLight", { aim: Vec3f; cone: Vec2f; position: Vec3f; range: F32; color: Vec3f; intensity: F32; }, {}>>.add(values?: table.Values<table.TableDef<"SpotLight", {
aim: Vec3f;
cone: Vec2f;
position: Vec3f;
range: F32;
color: Vec3f;
intensity: F32;
}, {}>> | undefined): table.RowId<"SpotLight">

Adds a row in the lowest free slot. Uses defaults for omitted fields. Throws when the table is full.

add
({
position?: ArrayLike<number> | InferInput<Vec3f> | undefined
position
: [4, 1, 0],
intensity?: number | undefined

Multiplies color.

intensity
: 2 }); // the default cone: a point light
  • 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
const direction: [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
(
const t: 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
(
const t: 0.6
t
), 0.3];
const light: 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.

settings
.
TgpuBufferBindingBase<WgslStruct<{ sunDirection: Vec3f; sunIntensity: F32; sunColor: Vec3f; skyIntensity: F32; skyTop: Vec3f; fogDensity: F32; skyBottom: Vec3f; fogFalloff: F32; fogColor: Vec3f; skyReflection: F32; fogStart: F32; }>>.patch(data: {
sunDirection?: InferInput<Vec3f> | undefined;
sunIntensity?: number | undefined;
sunColor?: InferInput<Vec3f> | undefined;
skyIntensity?: number | undefined;
skyTop?: InferInput<Vec3f> | undefined;
fogDensity?: number | undefined;
skyBottom?: InferInput<Vec3f> | undefined;
fogFalloff?: number | undefined;
fogColor?: InferInput<Vec3f> | undefined;
skyReflection?: number | undefined;
fogStart?: number | undefined;
} | undefined): void
patch
({
sunDirection?: InferInput<Vec3f> | undefined

Points towards the sun. It can have any length; shading normalizes it.

sunDirection
:
const direction: [number, number, number]
direction
,
sunColor?: InferInput<Vec3f> | undefined

Sets the sun colour in linear colour space.

sunColor
: [1, 0.7, 0.45],
fogDensity?: number | undefined

Sets fog density per world unit at y = 0.

fogDensity
: 0.04,
fogFalloff?: number | undefined

Sets how fog density falls with height. Density falls by a factor of e every 1 / fogFalloff world units. A value of 0 gives uniform fog.

fogFalloff
: 0.3,
});
  • Shaders normalize the direction and multiply colours by their intensities. Pass slider values directly.
  • A zero direction turns the sun off and leaves the shadow where it was.
  • environment.sunAt(hour, latitude, declination) gives the sun’s direction at an hour of the day, and environment.sunColor(direction) its colour through the air.
  • 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.

kit.shading

shader

These 'use gpu' functions shade surfaces with the lighting injected into their pipeline.

kind
shader
GPU functions
pointAtsettingstowardsSunfalloffbeambeamOfpbrlambertreflectancespecularOcclusionreflectionOfsurroundedhemispherehazeheightFogreachingsunSharesundirectunfoggedshadeshadeClearincoming
accessors
reflectambientfogsunShadowattenuation
schemas
MaterialPoint
types
ReflectHookAmbientHookFogHookSunShadowHookAttenuationHook

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.
const
const toon: shading.ReflectHook
toon
:
import shading
shading
.
type ReflectHook = (material: shading.Material, point: shading.Point, direction: d.v3f, radiance: d.v3f) => d.v3f

Computes reflection from one light direction, as pbr and lambert do.

ReflectHook
= (
material: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
material
,
point: {
position: d.v3f;
normal: d.v3f;
view: d.v3f;
uv: d.v2f;
uv1: d.v2f;
}
point
,
direction: d.v3f
direction
,
radiance: d.v3f
radiance
) => {
'use gpu';
const
const lit: number
lit
=
import std
std
.
function step(edge: number, x: number): number (+1 overload)
export step
step
(0.3,
import std
std
.
dot<d.v3f>(lhs: d.v3f, rhs: d.v3f): number
export dot
dot
(
point: {
position: d.v3f;
normal: d.v3f;
view: d.v3f;
uv: d.v2f;
uv1: d.v2f;
}
point
.
normal: d.v3f

Holds a unit surface normal.

normal
,
direction: d.v3f
direction
));
return
material: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
material
.
color: d.v3f

Sets the diffuse colour of a dielectric or the reflection colour of a metal, in linear colour space.

color
*
radiance: d.v3f
radiance
*
const lit: number
lit
;
};
const
const horizon: shading.FogHook
horizon
:
import shading
shading
.
type FogHook = (color: d.v3f, point: shading.Point) => d.v3f

Applies fog to the point colour, as heightFog does.

FogHook
= (
color: d.v3f
color
,
point: {
position: d.v3f;
normal: d.v3f;
view: d.v3f;
uv: d.v2f;
uv1: d.v2f;
}
point
) => {
'use gpu';
const
const away: number
away
=
import std
std
.
function saturate(value: number): number (+1 overload)
export saturate
saturate
(
point: {
position: d.v3f;
normal: d.v3f;
view: d.v3f;
uv: d.v2f;
uv1: d.v2f;
}
point
.
view: d.v3f

Holds a unit direction from the point towards the eye.

view
.
v3f.y: number
y
* -4 + 0.5);
const
const air: d.v3f
air
=
import std
std
.
mix<d.v3f>(e1: d.v3f, e2: d.v3f, e3: number): d.v3f (+2 overloads)
export mix
mix
(
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(0.9, 0.6, 0.4),
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(0.4, 0.55, 0.8),
const away: number
away
);
return
import std
std
.
mix<d.v3f>(e1: d.v3f, e2: d.v3f, e3: number): d.v3f (+2 overloads)
export mix
mix
(
color: d.v3f
color
,
const air: d.v3f
air
,
import shading
shading
.
const haze: (point: shading.Point) => number

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
const light: lighting.Lighting
light
=
import lighting
lighting
.
function create<{
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
(
const root: 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
:
const toon: shading.ReflectHook
toon
,
fog: shading.FogHook

Applies fog to the surface colour. Default shading.heightFog.

fog
:
const horizon: 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:

const ground = terrain.renderer().pipe((cfg) => cfg.with(shading.fog, paintedFog));

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
const pane: (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
const glass: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
glass
=
import materials
materials
.
const glossy: (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.

glossy
(
import d
d
.
const vec3f: d.Vec3f
(x: number, y: number, z: number) => d.v3f (+5 overloads)
vec3f
(0.8, 0.9, 1), 0.05);
return
import shading
shading
.
const shadeClear: (material: shading.Material, point: shading.Point, opacity: number) => d.v4f

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
(
const glass: {
color: d.v3f;
roughness: number;
emission: d.v3f;
metallic: number;
occlusion: number;
}
glass
,
import shading
shading
.
const pointAt: (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.