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:

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);
});