Motion Blur
Velocity-based blur trails created by blending current and previous frames with custom shader.
What this code does
- Custom Motion Blur Shader: blends current frame with previous frame based on velocity factors.
- Frame Buffer Technique: uses two render targets to store current and previous frame data.
- Velocity-Based Blur: adaptive blur strength based on luminance changes between frames.
- Multi-Object Animation: cubes with rotation and spheres with orbital motion create varied blur trails.
- Real-time Controls: adjust blur strength and velocity factor for different motion blur effects.
- Shader Pass Integration: custom shader integrated into Three.js EffectComposer pipeline.
JavaScript (plain)
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x000000)
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.set(0, 0, 8)
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
document.querySelector('#app').appendChild(renderer.domElement)
// Create objects with visible trails
const objects = []
const trailLength = 15
function createTrailObject(index, motionType) {
const hue = index / 7
const baseColor = new THREE.Color().setHSL(hue, 1.0, 0.8)
const mainObject = {
type: motionType,
speed: 15 + index * 5,
position: new THREE.Vector3(),
trail: [],
mesh: null
}
// Create main sphere
const geometry = new THREE.SphereGeometry(0.4, 16, 16)
const material = new THREE.MeshBasicMaterial({ color: baseColor.clone() })
mainObject.mesh = new THREE.Mesh(geometry, material)
scene.add(mainObject.mesh)
// Create trail spheres
for (let i = 0; i < trailLength; i++) {
const trailGeometry = new THREE.SphereGeometry(0.4 * (1 - i / trailLength), 8, 8)
const alpha = 1.0 - (i / trailLength)
const trailColor = baseColor.clone()
trailColor.multiplyScalar(alpha)
const trailMaterial = new THREE.MeshBasicMaterial({
color: trailColor,
transparent: true,
opacity: alpha * 0.8
})
const trailMesh = new THREE.Mesh(trailGeometry, trailMaterial)
trailMesh.visible = false
scene.add(trailMesh)
mainObject.trail.push({
mesh: trailMesh,
position: new THREE.Vector3()
})
}
return mainObject
}
// Create horizontal and vertical streaking objects
for (let i = 0; i < 4; i++) {
objects.push(createTrailObject(i, 'horizontal'))
}
for (let i = 0; i < 3; i++) {
objects.push(createTrailObject(i + 4, 'vertical'))
}
// Add lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 1.0)
scene.add(ambientLight)
function animate() {
requestAnimationFrame(animate)
const time = Date.now() * 0.001
objects.forEach((obj, objIndex) => {
// Store previous position
const prevPosition = obj.position.clone()
// Update position based on motion type
if (obj.type === 'horizontal') {
obj.position.x = Math.sin(time * obj.speed * 0.1) * 12
obj.position.y = (objIndex - 1.5) * 1.5
obj.position.z = Math.sin(objIndex + time) * 2
} else if (obj.type === 'vertical') {
obj.position.y = Math.sin(time * obj.speed * 0.1) * 8
obj.position.x = (objIndex - 5.5) * 2
obj.position.z = Math.cos(objIndex + time) * 1.5
}
// Update main mesh position
obj.mesh.position.copy(obj.position)
// Update trail positions (shift all positions back)
for (let i = obj.trail.length - 1; i > 0; i--) {
obj.trail[i].position.copy(obj.trail[i - 1].position)
obj.trail[i].mesh.position.copy(obj.trail[i].position)
obj.trail[i].mesh.visible = true
}
// Set first trail position to previous main position
if (obj.trail.length > 0) {
obj.trail[0].position.copy(prevPosition)
obj.trail[0].mesh.position.copy(prevPosition)
obj.trail[0].mesh.visible = true
}
})
renderer.render(scene, camera)
}
animate()