Three.js BokehPass Tutorial: Cinematic Depth of Field
Achieving a cinematic depth-of-field (DoF) effect in Three.js can
dramatically elevate the realism of your 3D scenes. This article
provides a straightforward guide on how to configure and fine-tune the
BokehPass post-processing effect in Three.js, covering the
necessary imports, effect composer integration, and key parameter
adjustments like focus distance, aperture, and maximum blur to get
professional photographic results.
To implement depth of field, Three.js utilizes its post-processing
library via EffectComposer. Instead of rendering the scene
directly to the screen, the scene is rendered to a buffer, and the
BokehPass shader is applied to simulate how a physical
camera lens blurs out-of-focus areas.
1. Import the Required Modules
First, import the core Three.js library along with the necessary
post-processing files. You will need EffectComposer,
RenderPass, and BokehPass.
import * as THREE from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { BokehPass } from 'three/examples/jsm/postprocessing/BokehPass.js';2. Set Up the Effect Composer
Set up your standard Three.js scene, camera, and WebGLRenderer. Once
those are initialized, instantiate the EffectComposer and
add a basic RenderPass to render your scene as the base
layer.
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const composer = new EffectComposer(renderer);
// Add the base render pass
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);3. Configure and Add the BokehPass
The BokehPass requires access to the scene and camera to
calculate depth information. You pass these, along with a configuration
object containing your lens parameters, into the constructor.
const bokehPass = new BokehPass(scene, camera, {
focus: 1.0, // Focus distance (distance from camera to in-focus point)
aperture: 0.025, // Camera aperture scale (larger values yield shallower depth of field)
maxblur: 0.01, // Maximum blur strength applied to out-of-focus elements
width: window.innerWidth,
height: window.innerHeight
});
composer.addPass(bokehPass);4. Key Parameters for Cinematic Realism
To get a realistic camera effect, you must balance three main settings:
focus: This represents the distance (in Three.js world units) from the camera to the focal plane. Any object at exactly this distance will be perfectly sharp. You can dynamically update this value to track a moving target object in your scene:const distance = camera.position.distanceTo(targetObject.position); bokehPass.uniforms['focus'].value = distance;aperture: Controls the depth of the focal plane. In real photography, a lower f-stop (larger aperture) creates a shallower depth of field. InBokehPass, increasing theaperturevalue narrows the in-focus band, making foreground and background blur much more intense.maxblur: Limits the maximum radius of the blur. If this value is too high, the blur will look blocky and pixelated; if it is too low, the cinematic effect will be barely noticeable. A value between0.01and0.02is generally recommended for a smooth, realistic blur.
5. Update the Render Loop
Finally, replace your standard
renderer.render(scene, camera) call inside your animation
loop with composer.render().
function animate() {
requestAnimationFrame(animate);
// Optional: Update focus point dynamically here
composer.render();
}
animate();6. Handling Window Resizing
To ensure the post-processing buffer scales correctly when the browser window is resized, update both the renderer and the composer dimensions:
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);
});