Volumetric Light Shafts
Fake god rays with additive cone; adjust density and decay.
What this code does
- Fake Volumetric Lighting: simulates god rays using a translucent cone mesh with additive blending.
- ConeGeometry: open cone (no bottom) creates light shaft geometry from point light source.
- Additive Blending: overlapping rays add together for bright concentration effects.
- Depth Write Disabled: prevents cone from blocking other transparent objects.
- Animated Occluder: rotating cube creates dynamic shadow patterns in light shafts.
- Interactive Controls: adjust ray density (opacity) and decay (scale falloff) in real-time.
JavaScript (plain)
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(70, width / height, 0.1, 200)
camera.position.set(0, 2, 6)
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
document.querySelector('#app').appendChild(renderer.domElement)
const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 0.5, 0)
controls.update()
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0x0f0f0f })
)
plane.rotation.x = -Math.PI / 2
plane.receiveShadow = true
scene.add(plane)
const occluder = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x222222 })
)
occluder.position.set(0, 0.5, 0)
scene.add(occluder)
const light = new THREE.PointLight(0xffddaa, 1.2, 30, 2)
light.position.set(-2, 3, -2)
scene.add(light)
const coneGeom = new THREE.ConeGeometry(0.6, 5, 32, 1, true)
const coneMat = new THREE.MeshBasicMaterial({
color: 0xffeedd,
transparent: true,
opacity: 0.2,
blending: THREE.AdditiveBlending,
depthWrite: false
})
const rays = new THREE.Mesh(coneGeom, coneMat)
rays.position.copy(light.position)
rays.rotation.x = -Math.PI / 2
scene.add(rays)
const params = { density: 0.2, decay: 0.95 }
function animate(timeMs) {
requestAnimationFrame(animate)
occluder.rotation.y += 0.01
rays.material.opacity = params.density
const d = THREE.MathUtils.lerp(1.0, 0.6, 1 - params.decay)
rays.scale.set(d, 1, d)
controls.update()
renderer.render(scene, camera)
}
animate()