Three.js Post-Processing and EffectComposer Guide
This article explains the fundamentals of post-processing in
Three.js, a technique used to apply visual effects to a 3D scene after
it has been rendered. You will learn what post-processing is and receive
a step-by-step guide on how to construct and configure the
EffectComposer to implement effects like bloom, glitch, and
color correction in your web projects.
What is Post-Processing?
In standard 3D rendering, the geometry, materials, and lights of a scene are calculated and drawn directly to the screen. Post-processing changes this workflow. Instead of rendering the scene directly to the canvas, the scene is rendered to an off-screen buffer (a render target) as a 2D image.
Once the scene is saved as a 2D texture, you can apply one or more shader filters to it before displaying the final image to the user. This is similar to applying filters in Photoshop or Instagram. Common post-processing effects include:
- Bloom: Makes bright areas of the scene glow.
- Depth of Field: Blurs objects that are out of focus, simulating a physical camera lens.
- Glitch: Creates digital artifacting and screen distortion.
- Color Correction: Adjusts saturation, brightness, contrast, and color grading.
How to Construct the EffectComposer
In Three.js, post-processing is managed by the
EffectComposer class. The composer coordinates a chain of
“passes” that are executed in sequence.
To use EffectComposer, you must import it along with the
necessary passes from the Three.js addons directory, as they are not
included in the core library.
Step 1: Import the Required Modules
First, import the core Three.js library, the
EffectComposer, the foundational RenderPass,
and any specific effect passes you want to apply (for example,
GlitchPass).
import * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';Step 2: Initialize the Renderer, Scene, and Camera
Set up your standard Three.js environment.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);Step 3: Construct the EffectComposer
Instantiate the EffectComposer by passing your
WebGLRenderer as the argument. The composer will handle the
rendering output.
const composer = new EffectComposer(renderer);Step 4: Add the Passes
An EffectComposer needs at least one pass to work.
- RenderPass: This is almost always the first pass. It renders your 3D scene normally from the perspective of your camera and inputs the resulting image into the composer’s memory buffer.
- Effect Passes: Add any subsequent effect passes. The last pass in the chain will automatically render to the screen unless specified otherwise.
// 1. Create and add the default render pass
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// 2. Create and add a post-processing effect pass
const glitchPass = new GlitchPass();
composer.addPass(glitchPass);Step 5: Update the Animation Loop
Normally, you would render a scene using
renderer.render(scene, camera). When using post-processing,
you must replace this call with composer.render() inside
your animation loop. This tells Three.js to execute the chain of passes
you configured.
function animate() {
requestAnimationFrame(animate);
// Cube rotation or other updates go here
// Render the scene through the effect composer
composer.render();
}
animate();Step 6: Handle Window Resizing
If the browser window resizes, both the renderer and the composer must be updated to maintain the correct resolution.
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
composer.setSize(width, height);
});