import * as THREE from 'three' export class MotionBlurEffect { private scene: THREE.Scene private camera: THREE.PerspectiveCamera private renderer: THREE.WebGLRenderer private renderTarget1: THREE.WebGLRenderTarget private renderTarget2: THREE.WebGLRenderTarget private blurMaterial: THREE.ShaderMaterial private quad: THREE.Mesh private intensity: number = 0.5 private accumulation: number = 0.8 constructor( scene: THREE.Scene, camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer ) { this.scene = scene this.camera = camera this.renderer = renderer const size = renderer.getSize(new THREE.Vector2()) // Create render targets for accumulation this.renderTarget1 = new THREE.WebGLRenderTarget(size.x, size.y, { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, type: THREE.FloatType }) this.renderTarget2 = new THREE.WebGLRenderTarget(size.x, size.y, { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, type: THREE.FloatType }) // Motion blur shader material this.blurMaterial = new THREE.ShaderMaterial({ uniforms: { tCurrent: { value: null }, tPrevious: { value: null }, intensity: { value: this.intensity }, accumulation: { value: this.accumulation } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform sampler2D tCurrent; uniform sampler2D tPrevious; uniform float intensity; uniform float accumulation; varying vec2 vUv; void main() { vec4 current = texture2D(tCurrent, vUv); vec4 previous = texture2D(tPrevious, vUv); // Blend current frame with accumulated previous frames vec4 result = mix(current, previous, accumulation * intensity); gl_FragColor = result; } ` }) // Create fullscreen quad const geometry = new THREE.PlaneGeometry(2, 2) this.quad = new THREE.Mesh(geometry, this.blurMaterial) } public render(deltaTime: number): THREE.WebGLRenderTarget { // Render current frame to renderTarget1 this.renderer.setRenderTarget(this.renderTarget1) this.renderer.render(this.scene, this.camera) // Apply motion blur by blending with previous frame this.blurMaterial.uniforms.tCurrent.value = this.renderTarget1.texture this.blurMaterial.uniforms.tPrevious.value = this.renderTarget2.texture // Render blurred result to renderTarget2 this.renderer.setRenderTarget(this.renderTarget2) this.renderer.render(this.quad, new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)) // Swap render targets for next frame const temp = this.renderTarget1 this.renderTarget1 = this.renderTarget2 this.renderTarget2 = temp return this.renderTarget1 } public setIntensity(intensity: number): void { this.intensity = intensity this.blurMaterial.uniforms.intensity.value = intensity } public setAccumulation(accumulation: number): void { this.accumulation = accumulation this.blurMaterial.uniforms.accumulation.value = accumulation } public resize(width: number, height: number): void { this.renderTarget1.setSize(width, height) this.renderTarget2.setSize(width, height) } public dispose(): void { this.renderTarget1.dispose() this.renderTarget2.dispose() this.blurMaterial.dispose() this.quad.geometry.dispose() } }