Depth of Field Gallery
Post-processing depth of field effect with objects at varying distances.
What this code does
- BokehPass: simulates camera depth of field with realistic blur based on distance from focus plane.
- EffectComposer: post-processing pipeline that applies effects after scene rendering.
- Focus Distance: objects at this distance appear sharp, others blur based on distance difference.
- Aperture Control: smaller aperture values create stronger blur effects (shallow depth of field).
- Object Gallery: three rows of objects at different Z-depths demonstrate the effect.
- Interactive Controls: W/S (focus), A/D (aperture), Q/E (max blur), or use arrow keys.
- Console Output: check browser console for current values and control instructions.
JavaScript (plain)
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
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'
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x111122)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100)
camera.position.set(0, 2, 8)
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
// Create objects at different depths
const materials = [
new THREE.MeshStandardMaterial({ color: 0xff4444, metalness: 0.8, roughness: 0.2 }),
new THREE.MeshStandardMaterial({ color: 0x44ff44, metalness: 0.3, roughness: 0.7 }),
new THREE.MeshStandardMaterial({ color: 0x4444ff, metalness: 0.1, roughness: 0.9 })
]
const geometries = [
new THREE.SphereGeometry(0.6, 32, 16),
new THREE.BoxGeometry(1.2, 1.2, 1.2),
new THREE.ConeGeometry(0.6, 1.4, 8)
]
// Front row (close)
for (let i = 0; i < 3; i++) {
const mesh = new THREE.Mesh(geometries[i], materials[i])
mesh.position.set((i - 1) * 2.5, 0, 4)
scene.add(mesh)
}
// Back row (far)
for (let i = 0; i < 3; i++) {
const mesh = new THREE.Mesh(geometries[i], materials[i])
mesh.position.set((i - 1) * 2.5, 0, -4)
scene.add(mesh)
}
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040, 0.3)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 10, 5)
scene.add(directionalLight)
// Setup post-processing for depth of field
const composer = new EffectComposer(renderer)
const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)
const bokehPass = new BokehPass(scene, camera, {
focus: 8.0, // Focus distance (camera at z=8)
aperture: 0.0025, // Aperture size (smaller = more blur)
maxblur: 1.0, // Maximum blur amount
width: window.innerWidth,
height: window.innerHeight
})
composer.addPass(bokehPass)
// Animation loop
function animate() {
requestAnimationFrame(animate)
controls.update()
composer.render() // Render with post-processing
}
animate()
// Interactive controls
document.addEventListener('keydown', (event) => {
switch (event.key) {
case 'w':
bokehPass.uniforms.focus.value = Math.min(bokehPass.uniforms.focus.value + 1, 12)
break
case 's':
bokehPass.uniforms.focus.value = Math.max(bokehPass.uniforms.focus.value - 1, 1)
break
case 'a':
bokehPass.uniforms.aperture.value = Math.max(bokehPass.uniforms.aperture.value - 0.0005, 0.0005)
break
case 'd':
bokehPass.uniforms.aperture.value = Math.min(bokehPass.uniforms.aperture.value + 0.0005, 0.01)
break
}
})