Skip to main content

Module post_effect

Module post_effect 

Source
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§

EffectStage
Where a post effect runs in the post chain — the passes that take a drawn frame to the window: the forward pass, the Lit effects, bloom, the tone map, the ToneMapped effects, the UI, then the OverUi effects.

Traits§

PostEffect
A pass of a game’s own over the whole frame, written in WGSL and stitched into the engine’s own at startup.
PostEffects
The post effects one game passes its frame through, named together as Game::PostEffects.