Vignette & Lens Distortion

Camera imperfections including dark edges (vignette) and radial lens warping.

What this code does

- VignetteShader: a classic effect that darkens the corners of the screen to draw focus to the center.
- Custom Lens Pass: implements a radial distortion shader to simulate barrel or pincushion lens warping.
- Strength Control: adjust the intensity of the lens distortion from negative (pincushion) to positive (barrel).
- Composition: multiple effect passes are layered via the EffectComposer to create a polished camera look.
- Scene Context: a simple room setup helps visualize how straight lines warp under lens distortion.

JavaScript Implementation

ES6+
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x222222)
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.set(2, 2, 5)

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)

const composer = new EffectComposer(renderer)
composer.addPass(new RenderPass(scene, camera))

const lensPass = new ShaderPass({
  uniforms: { 'tDiffuse': { value: null }, 'strength': { value: 0.1 } },
  vertexShader: 'varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }',
  fragmentShader: 'uniform sampler2D tDiffuse; uniform float strength; varying vec2 vUv; void main() { vec2 uv = vUv - 0.5; float r2 = uv.x*uv.x + uv.y*uv.y; uv *= 1.0 + r2 * strength; uv += 0.5; if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) gl_FragColor = vec4(0,0,0,1); else gl_FragColor = texture2D(tDiffuse, uv); }'
})
composer.addPass(lensPass)

const vignettePass = new ShaderPass(VignetteShader)
vignettePass.uniforms.darkness.value = 1.0
composer.addPass(vignettePass)

scene.add(new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), new THREE.MeshStandardMaterial({ color: 0x0077ff })))
scene.add(new THREE.AmbientLight(0xffffff, 0.5))
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(5, 10, 5)
scene.add(light)

function animate() {
  requestAnimationFrame(animate)
  composer.render()
}
animate()