@pixi/3d
    Preparing search index...

    Class PostEffectAbstract

    Base class for fullscreen post effects, added to a view through View3D#passes

    After the scene draws, the view runs its 'beforeToneMapping' effects in array order, then the tone curve, then its 'afterToneMapping' effects, each reading the image the step before drew. Any enabled post effect makes the view draw its scene into a texture first, and a 'beforeToneMapping' one also makes that texture float, holding linear light; see View3D#toneMapping.

    A single-shader effect sets fragment, and uniforms if it has any, and exposes properties that write into those uniforms. A property write shows on the next frame.

    An effect runs at 'beforeToneMapping' unless it passes another stage to super. There it reads linear light, brightness above 1 included, which suits blur, glow, depth of field and occlusion. Pass 'afterToneMapping' for an effect that judges the picture as it shows, such as edge detection or a final colour grade, or one that shows data rather than light, such as depth or normals, so the tone curve leaves its colours alone. Spread the caller's options after the stage, so one instance can still pick its own:

    class MyOutlineEffect extends PostEffect {
    constructor(options: PostEffectOptions = {}) {
    super({ stage: 'afterToneMapping', ...options });
    }
    }

    A multi-pass effect overrides PostEffect#build and adds its own passes to the render graph. Its final image must go to io.output:

    public override build(graph: RenderGraph, io: EffectIO): void {
    const ao = graph.createColor({ resolution: 1, format: 'rgba8unorm' });
    graph.addPass(new FullscreenPass({
    renderer: io.renderer,
    reads: { depth: graph.sceneDepth },
    writes: ao,
    fragment: AO_FRAGMENT,
    }));
    graph.addPass(new FullscreenPass({
    renderer: io.renderer,
    reads: { color: io.input, color2: ao },
    writes: io.output,
    fragment: MERGE_FRAGMENT,
    }));
    }

    Toggling, reordering and retiring an effect work as for any pass; View3D#passes has the rules.

    class MyGreyscaleEffect extends PostEffect {
    protected readonly fragment: EffectFragment = {
    gpu: `
    fn effect(uv: vec2<f32>) -> vec4<f32> {
    let color = sampleColor(uv);
    let luma = dot(color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
    return vec4<f32>(vec3<f32>(luma), color.a);
    }
    `,
    gl: `
    vec4 effect(vec2 uv) {
    vec4 color = sampleColor(uv);
    float luma = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
    return vec4(vec3(luma), color.a);
    }
    `,
    };
    }

    view.passes = [new MyGreyscaleEffect()];

    Hierarchy (View Summary)

    Index
    enabled: boolean

    Whether this pass runs

    Flipping it rebuilds the view's render graph on the next render, which suits a settings toggle but not a change every frame; animate an effect's own properties instead. It can also recompile the view's materials; View3D#passes says when.

    const wireframe = new MeshPass({ name: 'wireframe' });
    view.passes = [wireframe];
    wireframe.enabled = false;
    inputMips: boolean = false

    Whether the effect's input has mips, for an effect that reads coarse levels with sampleColorLod, such as a blur

    Costs a mip generation every frame and about a third more memory for the input, so leave it false unless the effect samples levels above 0.

    requiredOutputs?: readonly SceneOutput[]

    Extra outputs the view's scene draw writes beside its colour while this pass is enabled

    Each costs the scene draw a texture and the shader work to fill it, and any output makes the view draw its scene into a texture first, as a post effect does. Reach an output's texture with RenderGraph#getSceneOutput, to read it or draw into it.

    const objectIdOutput: SceneOutput = { name: 'objectId', format: 'r32uint' };

    class MyPickingPass extends Pass3D {
    public readonly stage = 'afterTransparent';
    public readonly requiredOutputs = [objectIdOutput];
    public build(): void {}
    }

    The stage the effect runs at, set by the stage option

    • Advanced

      Adds this effect's passes to the graph

      The default adds one FullscreenPass that draws fragment, binding the scene depth only when the fragment calls sampleDepth, viewPos or depthTexel by name. On an antialiased canvas, reading the depth costs a copy of it each frame, shared by every effect that reads it. Override it for a multi-pass effect, as the class docs show; Pass3D#build says when handles become textures.

      Parameters

      • graph: RenderGraph

        Render graph being built; add passes and transients to it.

      • io: EffectIO

        This effect's input and output, plus the renderer and opaque copies

      Returns void

    • Releases GPU resources the pass made outside the graph; the base class has none

      The view never calls it, not on replacing view.passes nor on its own destroy, since a pass can be shared. Call it yourself once the pass is retired. Override it only for resources of your own: the render passes added in build go with the view's graph, and a plain UniformGroup needs no release.

      Returns void

      view.passes = [];
      wireframe.destroy();