Capítulo 838 de 859

Chapter 838: Post Processing (Manual)

Core Idea

How EffectComposer chains post-processing Passes by ping-ponging between two render targets, and how to write a custom ShaderPass when the built-in passes (bloom, film grain, etc.) aren't enough.

Key Concepts

  • EffectComposer: creates two internal render targets and applies each added Pass in order, feeding one target's output as the next pass's input and swapping which target is "current" as it goes.
  • Per-pass flags: enabled (whether to run this pass at all), needsSwap (whether to swap the two render targets after this pass), clear (whether to clear before rendering this pass), and renderToScreen (render to the canvas instead of a render target — usually left alone, since the last pass in the chain does this automatically).
  • Built-in passes: RenderPass (renders the actual scene, normally first), BloomPass (blurs a smaller copy of the input and adds it back for a glow effect), FilmPass (adds noise/scanlines), OutputPass (sRGB conversion and tone mapping, normally last).
  • composer.render(deltaTime): replaces renderer.render() in the animation loop; deltaTime (seconds since last frame) is passed through to any time-based effects like FilmPass.
  • ShaderPass: wraps a custom GLSL shader (with a standard, rarely-changed vertex shader) as a pass; the fragment shader reads the previous pass's result from the tDiffuse sampler and writes a modified color.

Code Examples

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new ShaderPass({
  uniforms: { tDiffuse: { value: null }, color: { value: new THREE.Color(1, 1, 1) } },
  fragmentShader: `
    uniform sampler2D tDiffuse;
    uniform vec3 color;
    varying vec2 vUv;
    void main() {
      vec4 previousPassColor = texture2D(tDiffuse, vUv);
      gl_FragColor = vec4(previousPassColor.rgb * color, previousPassColor.a);
    }
  `,
}));
  • What it demonstrates: a minimal custom ShaderPass that multiplies the previous pass's output by a color, using the standard tDiffuse input.

Key Takeaways

  1. EffectComposer manages two render targets and feeds each pass's output into the next, swapping targets as it goes.
  2. RenderPass normally starts the chain and OutputPass (color-space + tone mapping) normally ends it.
  3. Replace renderer.render() with composer.render(deltaTime) and keep the composer's size synced to the canvas on resize.
  4. A custom effect is a ShaderPass reading tDiffuse (the previous pass's texture) in its fragment shader — the vertex shader is boilerplate you rarely need to change.

Connects To

  • WebGLRenderer: the composer still renders through a WebGLRenderer under the hood.