the depth of the same picture, read at every record
pull?takes
how fast the focus follows aim, per second
region?takes
the part of the source that holds the picture, read at every record
Dofreturns
depth of field you record every frame and destroy
GPU functions
distanceAtthinLensfocuscircle
schemas
Settings
constants
defaults
types
RadiusOutputDofRecordOptions
Optionscreate · 7 fields
source
(() => FloatTexture) | Rendered
Provides the linear picture: a rendered picture, such as aa, or a function returning a texture.
depth
() => Depth
Returns a sampled depth texture of the same picture, of any size, such as a half-resolution prepass.
radius?
Radius= thinLens at focus()
Returns the signed blur radius at an eye distance and a point of the picture, from 0 to 1 over its region, as a fraction of the picture height: negative in front of the focus, positive behind it.
pull?
number= Infinity, which focuses at once from the record after aim
Sets how fast the focus follows aim, per second: each second closes 1 - e^-pull of the way, evenly in blur rather than in distance.
rings?
2 | 3= 3
Sets the rings of taps in the gather, 3 for 49 taps or 2 for 25.
region?
SizeSource= the region of a rendered source, else the whole texture
Selects the source region from its top-left corner, such as regions.hdr when drawing below full resolution.
settings?
Partial<d.InferInput<typeof Settings>>= defaults, with focus 10, aperture 0.05 and limit 0.02
Sets initial values over defaults.
records
record→encoder
record after the TAA resolve or whatever wrote the source, before bloom and the final pass; then output holds the picture.
owns
targets ·2 buffers ·a sampler ·4 pipelines ·5 bind groups
The half-resolution picture, the tiles, the blur and the output; the settings and the regions; a group for each pass and one for the depth. With pull or after aim, also the focus and its step, the kernel moving it and its groups.
allocates
on resize
Targets for the new size and the bind groups of its passes; so does a new source or depth.
lifecycle
✓initAsync()✓destroy()✓[Symbol.dispose]
injects
hook
The half-resolution picture and regions dof.circle reads, from the part.
The potions stay sharp while the lights behind them blur into discs. Tap the seller or the street to pull the focus there: the potions blur and spread over what is behind them. Turn depth of field off to compare.
A lens is sharp at one distance. In front of it and behind it, each point spreads into a disc, the circle of confusion, that grows with the distance from the focus. Blurring by distance needs a radius at each pixel, and a blur that spreads a near object over what is sharp behind it while a far one never bleeds onto what is in front. dof.create reads the picture and a depth of it and records 4 passes:
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
Creates depth of field. A prepare pass halves the source and stores a signed blur radius from
radius. A tiles pass finds the largest radius in each 16 × 16 pixels. A gather pass scales
rings of taps to the largest radius around each tile, keeps the far field from bleeding onto
what is sharper and spreads the near field over it, as Jimenez describes it in 2014. A composite
pass mixes the blur into the full-size source by the radius at each pixel.
@injects ― hook the half-resolution picture and regions dof.circle reads, from the part
@binds ― 0 storage, 1 uniform: dof.circle, the regions, beside the texture
@allocates ― on resize: targets for the new size and the bind groups of its passes; so does a new
source or depth
@owns ― targets, 2 buffers, a sampler, 4 pipelines, 5 bind groups: the half-resolution picture,
the tiles, the blur and the output; the settings and the regions; a group for each pass and one
for the depth. With pull or after aim, also the focus and its step, the kernel moving it and
its groups
@records ― record after the TAA resolve or whatever wrote the source, before bloom and the final
pass; then output holds the picture.
@param ― view the camera that drew depth
@param ― options.source the linear picture, read at every record
@param ― options.depth the depth of the same picture, read at every record
@param ― options.region the part of the source that holds the picture, read at every record
@param ― options.pull how fast the focus follows aim, per second
@returns ― depth of field you record every frame and destroy
Provides the linear picture: a rendered picture, such as aa, or a function returning a
texture. record reads it on every call, so the texture can be rebuilt.
Provides the current textures. A size change replaces them. Throws after destroy.
textures.
hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>
hdr,
Options.depth: ()=>targets.Depth
Returns a sampled depth texture of the same picture, of any size, such as a half-resolution
prepass. record reads it on every call. Its drawn part is the source's share of its texture.
Creates bloom from a chain of half-size textures. Each level downsamples with 13 taps, then a
tent filter adds it to the next larger level. The result is mip 0 of output, at half the
source size. With region, passes draw and sample only that region; changing the region
allocates nothing.
@allocates ― on resize: a new chain and the bind groups of its passes; so does a new source
@owns ― targets, a sampler, 3 pipelines, 2 bind groups and a buffer per level: the chain of glow
levels, the settings, a group for each pass, and the region each pass reads
@records ― record after whatever wrote the source; then output holds the glow.
@param ― options.source the HDR picture, read at every record
@param ― options.region the part of the source that holds the picture, read at every record
@returns ― a bloom you record every frame and destroy
Provides the HDR picture: a rendered picture, such as aa, or a function returning a
texture, such as () => screen.textures.hdr. record reads it on every call, so the texture
can be rebuilt.
Creates the final pass of a frame. It reads the source, adds glow, tone maps, grades, applies
the vignette, encodes and dithers. It draws one triangle into the caller pass.
@allocates ― on resize: a bind group for a new source or glow
@owns ― 2 buffers, a sampler, a pipeline, 2 bind groups: the settings, the regions it reads, and a
group each for the source and the glow
@records ― record into a pass of its own on the view you give, usually the canvas's, or draw
into the caller's pass, after the bloom's record.
@param ― options.source the picture, rendered or a texture, read at every draw
@param ― options.region the part of the source that holds the picture, read at every draw
@param ― options.glow a bloom, or a glow of your own, added before tone mapping
@param ― options.exposure an adapted exposure, read on the GPU
@returns ― a final pass you draw every frame and destroy
create(
constroot:TgpuRoot
root,{
Options.format: GPUTextureFormat
Sets the output format, such as the canvas format.
Provides the picture at any size: a rendered picture, such as aa or blur, or a function
returning a texture, such as () => screen.textures.hdr. draw reads it on every call, so
the texture can be rebuilt.
source:
constblur:dof.Dof
blur,
Options.glow?:finish.Rendered|undefined
Adds a bloom, or a glow of your own such as an emission target, before tone mapping. draw
reads its output and region on every call. Default none.
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
Rebuilds all textures if the source size changed, destroying the old textures. Updates
regions for the current resolution. Returns true after a rebuild so callers can update bind
groups that hold old textures. Call before recording passes that use the targets. Throws after
destroy.
Records the prepare, tiles, gather and composite passes after the source is written. After an
aim, it first moves the focus by dt seconds. Optional timer writes measure the passes
together.
Opens a pass on view, such as surface.view(frame), draws into it and ends it. A
viewport draws the picture into that rectangle and clears the rest, transparent with
alpha: 'premultiplied' and black otherwise. Optional timer writes measure the pass.
record(
frame:frames.Frame
frame,
constsurface:web.Surface
surface.
Surface.view(frame: frames.Frame): GPUTextureView
Returns a view of the current canvas texture, created once per texture. Applies pending size
changes first, which a pass using context would skip.
view(
frame:frames.Frame
frame));
}),
);
Record it after TAA, with source: aa, and before bloom. Bloom reading blur.output turns out-of-focus highlights into glowing discs.
The radius is a fraction of the picture height, negative in front of the focus and positive behind it. By default it is the thin-lens circle, 0.5 × aperture × k × (1 / focus − 1 / distance), where k is half the camera’s vertical projection scale, its focal length over the sensor height. limit caps it.
aperture and the distances share world units. A 50 mm lens at f/2 in a scene in metres opens 0.025. Larger openings make a scene look smaller, as a macro lens does.
depth can be any size and is read by UV. A multisampled depth can’t be read, so draw a single-sampled depth prepass, as hex isle does at half resolution under its MSAA scene. Radius edges then step at the prepass’s resolution.
To turn it off, skip record and give bloom and the final pass the picture it read. Point their source at a function that picks one.
record takes the frame or an encoder. record(frame, { timestampWrites }) times the 4 passes under one profiler timer.
Field
Default
focus
10
Distance in focus, in world units along the view direction.
aperture
0.05
Diameter of the lens opening, in world units.
limit
0.02
Largest blur radius, as a fraction of the picture height.
blur.aim(x, y) focuses on what the depth holds at a point of the picture, from 0 to 1 over it, such as under a tap. pull sets how fast the focus gets there, per second:
Creates depth of field. A prepare pass halves the source and stores a signed blur radius from
radius. A tiles pass finds the largest radius in each 16 × 16 pixels. A gather pass scales
rings of taps to the largest radius around each tile, keeps the far field from bleeding onto
what is sharper and spreads the near field over it, as Jimenez describes it in 2014. A composite
pass mixes the blur into the full-size source by the radius at each pixel.
@injects ― hook the half-resolution picture and regions dof.circle reads, from the part
@binds ― 0 storage, 1 uniform: dof.circle, the regions, beside the texture
@allocates ― on resize: targets for the new size and the bind groups of its passes; so does a new
source or depth
@owns ― targets, 2 buffers, a sampler, 4 pipelines, 5 bind groups: the half-resolution picture,
the tiles, the blur and the output; the settings and the regions; a group for each pass and one
for the depth. With pull or after aim, also the focus and its step, the kernel moving it and
its groups
@records ― record after the TAA resolve or whatever wrote the source, before bloom and the final
pass; then output holds the picture.
@param ― view the camera that drew depth
@param ― options.source the linear picture, read at every record
@param ― options.depth the depth of the same picture, read at every record
@param ― options.region the part of the source that holds the picture, read at every record
@param ― options.pull how fast the focus follows aim, per second
@returns ― depth of field you record every frame and destroy
create(
constroot:TgpuRoot
root,
constview:cameras.OrbitCamera
view,{
Options.source: Rendered |(()=> FloatTexture)
Provides the linear picture: a rendered picture, such as aa, or a function returning a
texture. record reads it on every call, so the texture can be rebuilt.
Provides the current textures. A size change replaces them. Throws after destroy.
textures.
hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>
hdr,
Options.depth: ()=>targets.Depth
Returns a sampled depth texture of the same picture, of any size, such as a half-resolution
prepass. record reads it on every call. Its drawn part is the source's share of its texture.
Provides the current textures. A size change replaces them. Throws after destroy.
textures.
depth:targets.Texture<{
readonly format:"depth32float";
readonly sampled:true;
}>
depth,
Options.pull?: number |undefined
Sets how fast the focus follows aim, per second: each second closes 1 - e^-pull of the
way, evenly in blur rather than in distance. With it, each record runs the kernel that moves
the focus. Default Infinity, which focuses at once from the record after aim.
pull:5,
});
// On a tap, at a point of the picture
constblur:dof.Dof
blur.
Dof.aim(x: number, y: number): void
Focuses on what the depth holds at a point of the picture, from 0 to 1 over its region, such
as under a tap. The next record reads the distance there, and from then on each record
moves settings.focus towards it on the GPU at pull.
Records the prepare, tiles, gather and composite passes after the source is written. After an
aim, it first moves the focus by dt seconds. Optional timer writes measure the passes
together.
record(
constframe:Frame
frame);
The next record reads the distance at the aimed point, and each record from then on moves settings.focus towards it on the GPU by the frame’s dt, in one more compute pass. Nothing is read back.
Each second closes 1 - e^-pull of the way, as clock.ease does, evenly in blur rather than in distance, so a pull from far to near looks as smooth as one from near to far. Without pull, the focus jumps.
settings.focus sets the focus until the first aim. A radius of your own reads the moving focus with dof.focus().
With an encoder in place of the frame, give the seconds yourself, blur.record(encoder, { dt }).
radius(distance, uv) replaces the thin lens. It is a 'use gpu' function that returns the signed radius, and it carries its fills with pipe. Three functions work only inside it:
dof.thinLens(distance, focus) returns the thin-lens radius with the settings’ aperture.
dof.focus() returns the distance in focus, which follows aim.
dof.distanceAt(uv) returns the distance along the view direction to what the depth holds at a point of the picture, from cameras.distanceOf.
Hex isle uses both, through a uniform that picks one. By default it blurs above and below a band, so the island looks like a miniature:
Allocates memory on the GPU, allows passing data between host and shader.
Read-only on the GPU, optimized for small data. For a general-purpose buffer,
use
TgpuRoot.createBuffer
.
@param ― typeSchema The type of data that this buffer will hold.
@param ― initial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)
Returns the thin-lens blur radius at an eye distance with focus in focus and the settings'
aperture, as a fraction of the picture height. It is negative in front of the focus. It reads
the settings, so call it only from the radius callback.
thinLens(
distance: number
distance,
import dof
dof.
constdistanceAt:(uv:d.v2f)=>number
Returns the distance along the view direction to what the depth holds at a point of the
picture, from 0 to 1 over its region. Call it only from the radius callback, such as
dof.thinLens(distance, dof.distanceAt(cursor.$)) to focus under a pointer.
distanceAt(
constcursor:TgpuUniform<d.Vec2f>
cursor.
TgpuUniform<Vec2f>.$: d.v2f
$));
};
// A miniature: sharp in a band, blurred above it and in front below it
Creates depth of field. A prepare pass halves the source and stores a signed blur radius from
radius. A tiles pass finds the largest radius in each 16 × 16 pixels. A gather pass scales
rings of taps to the largest radius around each tile, keeps the far field from bleeding onto
what is sharper and spreads the near field over it, as Jimenez describes it in 2014. A composite
pass mixes the blur into the full-size source by the radius at each pixel.
@injects ― hook the half-resolution picture and regions dof.circle reads, from the part
@binds ― 0 storage, 1 uniform: dof.circle, the regions, beside the texture
@allocates ― on resize: targets for the new size and the bind groups of its passes; so does a new
source or depth
@owns ― targets, 2 buffers, a sampler, 4 pipelines, 5 bind groups: the half-resolution picture,
the tiles, the blur and the output; the settings and the regions; a group for each pass and one
for the depth. With pull or after aim, also the focus and its step, the kernel moving it and
its groups
@records ― record after the TAA resolve or whatever wrote the source, before bloom and the final
pass; then output holds the picture.
@param ― view the camera that drew depth
@param ― options.source the linear picture, read at every record
@param ― options.depth the depth of the same picture, read at every record
@param ― options.region the part of the source that holds the picture, read at every record
@param ― options.pull how fast the focus follows aim, per second
@returns ― depth of field you record every frame and destroy
create(
constroot:TgpuRoot
root,
constview:cameras.OrbitCamera
view,{
Options.source: Rendered |(()=> FloatTexture)
Provides the linear picture: a rendered picture, such as aa, or a function returning a
texture. record reads it on every call, so the texture can be rebuilt.
Provides the current textures. A size change replaces them. Throws after destroy.
textures.
hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>
hdr,
Options.depth: ()=>targets.Depth
Returns a sampled depth texture of the same picture, of any size, such as a half-resolution
prepass. record reads it on every call. Its drawn part is the source's share of its texture.
Provides the current textures. A size change replaces them. Throws after destroy.
textures.
depth:targets.Texture<{
readonly format:"depth32float";
readonly sampled:true;
}>
depth,
Options.radius?:dof.Radius|undefined
Returns the signed blur radius at an eye distance and a point of the picture, from 0 to 1 over
its region, as a fraction of the picture height: negative in front of the focus, positive
behind it. This 'use gpu' function runs once per half-resolution texel and once per pixel.
It can call thinLens, distanceAt and focus and carry fills with pipe. Default
thinLens at focus().
Write the pointer into cursor as it moves, in picture coordinates from 0 to 1. The focus jumps to each new surface. To ease it, pull the focus with aim instead.
The radius runs once per half-resolution texel and once per pixel, so keep it cheap.
Depth of field keeps its blur radius where a final pass’s grade can’t read it. dof.circle(uv) reads it through blur.part, shared as in Another target. This grade tints the near field orange and the far field blue:
Combines parts from modules or your own code in one layout. Call it before creating pipelines.
Set its group on each pass with pass.setBindGroup(shared.layout, shared.group()), or give it to
passes.create as shared, which sets it in every pass it begins. Every pipeline in that pass
that reads a part then uses the shared group.
@param ― parts parts from any modules and your own, by key
@returns ― a layout you set on each pass, with the group for the parts' resources
share(
constroot:TgpuRoot
root,{
blur:bindings.Part<bindings.Entries>
blur:
constblur:dof.Dof
blur.
Dof.part: bindings.Part<bindings.Entries>
Provides the half-resolution picture and its regions for dof.circle. Share it before
creating the pipelines that read it, bindings.share(root, { blur: blur.part }), so a new
source size replaces the texture without replacing pipelines.
part});// before `finish.create`
const
constfocusBand:(color:d.v3f,uv:d.v2f)=>d.v3f
focusBand=(
color:d.v3f
color:
import d
d.
export v3f
Interface representing its WGSL vector type counterpart: vec3f or vec3.
A vector with 3 elements of type f32
v3f,
uv:d.v2f
uv:
import d
d.
export v2f
Interface representing its WGSL vector type counterpart: vec2f or vec2.
A vector with 2 elements of type f32
v2f)=>{
'use gpu';
const
constradius:number
radius=
import dof
dof.
constcircle:(uv:d.v2f)=>number
Returns the signed blur radius record last prepared at a point of the picture, from 0 to 1
over its region, as a fraction of the picture height. Call it from a pipeline piped with
blur.inject(), such as a final pass's grade showing the focus.
Creates the final pass of a frame. It reads the source, adds glow, tone maps, grades, applies
the vignette, encodes and dithers. It draws one triangle into the caller pass.
@allocates ― on resize: a bind group for a new source or glow
@owns ― 2 buffers, a sampler, a pipeline, 2 bind groups: the settings, the regions it reads, and a
group each for the source and the glow
@records ― record into a pass of its own on the view you give, usually the canvas's, or draw
into the caller's pass, after the bloom's record.
@param ― options.source the picture, rendered or a texture, read at every draw
@param ― options.region the part of the source that holds the picture, read at every draw
@param ― options.glow a bloom, or a glow of your own, added before tone mapping
@param ― options.exposure an adapted exposure, read on the GPU
@returns ― a final pass you draw every frame and destroy
create(
constroot:TgpuRoot
root,{
Options.format: GPUTextureFormat
Sets the output format, such as the canvas format.
Provides the picture at any size: a rendered picture, such as aa or blur, or a function
returning a texture, such as () => screen.textures.hdr. draw reads it on every call, so
the texture can be rebuilt.
Grades or tints the colour after tone mapping and before the vignette, encoding and dither.
It receives the colour and output UV coordinates from 0 to 1. Read other pixels with
finish.picture(uv) and glow with finish.glow(uv). Default none.
Prepare, at half resolution. Averages 2 × 2 pixels and stores the radius at the nearest of their depths in alpha.
Tiles, at a sixteenth of each side. Each texel holds the largest radius in its 16 × 16 pixels.
Gather, at half resolution. Scales rings of taps, 3 rings for 49 taps or 2 for 25, to the largest radius in the tiles that a circle of limit can reach. A tap behind the focus counts where the smaller of its circle and the centre’s reaches the centre, so what is sharp keeps its edge. A tap in front counts where its own circle reaches, and lays over the rest by how much of the disc such taps cover, so a blurred foreground spreads over a sharp background. Where the largest radius is under half a texel, it copies the centre.
Composite, at full resolution. Adds the source’s detail above the half-resolution picture back onto the blur where the radius is under a pixel, fading out by 3 pixels, and where no near field covers it.
Pass
Textures
Samplers
Uniforms
Prepare
source, depth
0
settings, regions, camera
Tiles
half-resolution picture
0
regions
Gather
half-resolution picture, tiles
0
settings, regions
Composite
source, half-resolution picture, blur, depth
1
settings, regions, camera
No pass binds storage. radius adds what it reads, such as the cursor’s uniform, to the prepare and the composite. After an aim, a one-thread compute pass before the prepare reads the depth under the aimed point, moves the focus in a storage buffer and copies it into the settings.
Depth of field reads one depth per pixel. A translucent drawing, which writes no depth, and anything the depth leaves out take the radius of what is behind them.
Out-of-focus highlights are always discs.
Under cameras.flat, dof.thinLens has no meaning. Give radius a function of your own.