postprocessing

repository·main·Indexed 25 days ago

https://github.com/pmndrs/postprocessing

A high-performance post-processing library for three.js that provides a managed pipeline of passes and effects to apply fullscreen image effects. It includes tools like EffectComposer and RenderPipeline, supports HDR workflows via HalfFloatType buffers, and allows for the creation of custom effects by extending the Effect class with GLSL fragment and vertex shaders.

Tokens
9.2K
Snippets
13
Records
74
Agent score
83%

What's inside postprocessing

  1. Configure Tone Mapping in postprocessing

    main

    To use tone mapping correctly with this library:

    1. Set the renderer's toneMapping setting to NoToneMapping (the default).
    2. Enable high precision frame buffers (e.g., HalfFloatType).
    3. Add a ToneMappingEffect at the end of your effect pipeline.
  2. Configure WebGLRenderer for optimal postprocessing

    main

    For an optimal postprocessing workflow, configure your three.js WebGLRenderer with the following attributes: powerPreference: "high-performance", antialias: false, stencil: false, and depth: false.

    import { WebGLRenderer } from "three";
    
    const renderer = new WebGLRenderer({
    	powerPreference: "high-performance",
    	antialias: false,
    	stencil: false,
    	depth: false
    });
  3. Basic usage of EffectComposer and passes

    main

    To use post-processing, use an EffectComposer to manage and run passes.

    1. Configure your WebGLRenderer with optimal settings (disable antialias, stencil, and depth if you are using post-processing passes).
    2. Use RenderPass as the first pass to clear buffers and render the scene.
    3. Use EffectPass to apply fullscreen image effects (like BloomEffect).
    4. Call composer.render() in your animation loop instead of renderer.render().
    import { BloomEffect, EffectComposer, EffectPass, RenderPass } from "postprocessing";
    
    const composer = new EffectComposer(renderer);
    composer.addPass(new RenderPass(scene, camera));
    composer.addPass(new EffectPass(camera, new BloomEffect()));
    
    requestAnimationFrame(function render() {
    	requestAnimationFrame(render);
    	composer.render();
    });
  4. Create a custom effect by extending the Effect class

    main

    To create a custom effect, extend the Effect class from postprocessing. You must provide a fragment shader that implements either mainImage or mainUv. You can also optionally provide a vertex shader implementing mainSupport.

    Effects are headless fullscreen passes and should be rendered using an EffectPass. They do not have direct access to an output buffer and are not intended to render to the screen independently.

    import { Uniform, Vector3 } from "three";
    import { Effect } from "postprocessing";
    
    // Use a bundler plugin like esbuild-plugin-glsl to import shaders as text.
    import fragmentShader from "./shader.frag";
    
    export class CustomEffect extends Effect {
    
    	constructor() {
    
    		super("CustomEffect", fragmentShader, {
    			uniforms: new Map([
    				["weights", new Uniform(new Vector3())]
    			])
    		});
    
    	}
    
    }
  5. Use RenderPipeline to group passes

    main

    A RenderPipeline is used to group multiple passes. A common setup includes a ClearPass, a GeometryPass, and one or more EffectPass instances (which render fullscreen Effects).

    To use the pipeline:

    1. Initialize RenderPipeline with your renderer.
    2. Add passes using .addPass().
    3. Update the pipeline size using .setSize(width, height) during resize events.
    4. Execute the pipeline in your animation loop using .render(timestamp).
    import {
    	BloomEffect,
    	ClearPass,
    	EffectPass,
    	GeometryPass,
    	RenderPipeline
    } from "postprocessing";
    
    // ... setup renderer, scene, camera ...
    
    const pipeline = new RenderPipeline(renderer);
    pipeline.addPass(new ClearPass());
    pipeline.addPass(new GeometryPass(scene, camera, { samples: 4 }));
    pipeline.addPass(new EffectPass(new BloomEffect()));
    
    function onResize(): void {
    	const width = container.clientWidth, height = container.clientHeight;
    	camera.aspect = width / height;
    	camera.updateProjectionMatrix();
    	pipeline.setSize(width, height);
    }
    
    window.addEventListener("resize", onResize);
    onResize();
    
    requestAnimationFrame(function render(timestamp: number): void {
    	requestAnimationFrame(render);
    	pipeline.render(timestamp);
    });
  6. Optimize WebGLRenderer for post-processing

    main

    For an optimal post-processing workflow, initialize your WebGLRenderer with the following attributes to avoid redundant work or conflicts with post-processing passes:

    import { WebGLRenderer } from "three";
    
    const renderer = new WebGLRenderer({
    	powerPreference: "high-performance",
    	antialias: false,
    	stencil: false,
    	depth: false
    });