Expand description
Full-screen passes a game writes in WGSL, run over the frame the engine drew.
A post effect runs inside the post chain, the passes that take a drawn
frame to the window. It is a type: its fields are the values its WGSL
reads, its
STAGE is where in the post chain it runs, and
its SHADER is the WGSL it runs. Name it in
Game::PostEffects through
post_effects!, and run it for one frame with
set_post_effect.
§What the engine declares
An effect’s WGSL is compiled into this, which it may read and must not declare again:
struct Pixel {
color: vec4<f32>, // what the chain holds at this pixel
uv: vec2<f32>, // 0..1 across the frame, zero at its top left
position: vec2<f32>, // physical pixels, the center of each at a half
size: vec2<f32>, // the frame, in physical pixels
}
fn color_at(uv: vec2<f32>) -> vec4<f32>;
fn depth_at(uv: vec2<f32>) -> f32;color is what the pass before this one wrote: at
Lit that is high-dynamic-range light, whose
channels run past 1.0; at ToneMapped and
OverUi it is the encoded value the target
holds, in 0..1. color_at(pixel.uv) reads the same texel as
pixel.color.
color_at and depth_at return the edge texel for a uv outside
0..1. depth_at returns the view depth in meters the forward pass
wrote —
the first sample of the pixel where the frame is drawn over more than
one — and the far clip of the frame’s camera where nothing was drawn,
which is 1000.0 until a game sets one with
Projection::clip.
§What an effect declares
One function, named and written as the engine reads it:
fn draw(pixel: Pixel) -> vec4<f32>;The pass writes what it returns, every channel, over what the target
held: nothing is blended. The effect after it in the same stage reads
all four channels back as pixel.color.
The bloom chain and the tone map read the color alone, so a Lit
effect’s alpha is dropped. A ToneMapped or OverUi effect’s alpha is
the alpha the target holds — the engine’s own passes write 1.0 there
— and a headless reading reads it back.
An effect’s values are bound as effect: a uniform of a WGSL struct
named after the Rust type, so a function of the effect’s own may take
that struct by value. An effect with no fields binds nothing.
fn tinted(values: Grain, color: vec3<f32>) -> vec3<f32> { … }An effect may declare whatever else it needs beside draw: its own
code, its own constant values, its own struct types. The names the
engine’s own shader already holds are Pixel, Frame, Fragment,
FORESHORTENED, frame, source, source_sampler, resolved,
scene, color_at, depth_at, fullscreen, effect_fragment,
resolve and effect; declaring one of them again stops startup, as
any other error in the WGSL does. The error names the effect and counts
its lines from the compiled shader, the engine’s own and the effect’s
together, not from the effect’s file.
A game may name any number of effects. Each is one pipeline built at startup and one full-screen pass in a frame that submits it; a frame that submits none runs none.
Enums§
- Effect
Stage - Where a post effect runs in the post chain — the passes that take a
drawn frame to the window: the forward pass, the
Liteffects, bloom, the tone map, theToneMappedeffects, the UI, then theOverUieffects. - NoPost
Effects - The set of a game that draws the frame as the chain leaves it.
Traits§
- Post
Effect - A pass of a game’s own over the whole frame, written in WGSL and stitched into the engine’s own at startup.
- Post
Effects - The post effects one game passes its frame through, named together as
Game::PostEffects.