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()