import { Mesh, BufferGeometry, BufferAttribute, LinearFilter, Scene, WebGLRenderTarget, WebGLRenderer, Camera, Vector2, HalfFloatType, } from 'three' import { ConvolutionMaterial } from './ConvolutionMaterial' export interface BlurPassProps { gl: WebGLRenderer resolution: number width?: number height?: number minDepthThreshold?: number maxDepthThreshold?: number depthScale?: number depthToBlurRatioBias?: number } export class BlurPass { readonly renderTargetA: WebGLRenderTarget readonly renderTargetB: WebGLRenderTarget readonly convolutionMaterial: ConvolutionMaterial readonly scene: Scene readonly camera: Camera readonly screen: Mesh renderToScreen: boolean = false constructor({ gl, resolution, width = 500, height = 500, minDepthThreshold = 0, maxDepthThreshold = 1, depthScale = 0, depthToBlurRatioBias = 0.25, }: BlurPassProps) { this.renderTargetA = new WebGLRenderTarget(resolution, resolution, { minFilter: LinearFilter, magFilter: LinearFilter, stencilBuffer: false, depthBuffer: false, type: HalfFloatType, }) this.renderTargetB = this.renderTargetA.clone() this.convolutionMaterial = new ConvolutionMaterial() this.convolutionMaterial.setTexelSize(1.0 / width, 1.0 / height) this.convolutionMaterial.setResolution(new Vector2(width, height)) this.scene = new Scene() // @ts-expect-error fixed in r154 this.camera = new Camera() this.convolutionMaterial.uniforms.minDepthThreshold.value = minDepthThreshold this.convolutionMaterial.uniforms.maxDepthThreshold.value = maxDepthThreshold this.convolutionMaterial.uniforms.depthScale.value = depthScale this.convolutionMaterial.uniforms.depthToBlurRatioBias.value = depthToBlurRatioBias this.convolutionMaterial.defines.USE_DEPTH = depthScale > 0 const vertices = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]) const uvs = new Float32Array([0, 0, 2, 0, 0, 2]) const geometry = new BufferGeometry() geometry.setAttribute('position', new BufferAttribute(vertices, 3)) geometry.setAttribute('uv', new BufferAttribute(uvs, 2)) this.screen = new Mesh(geometry, this.convolutionMaterial) this.screen.frustumCulled = false this.scene.add(this.screen) } render(renderer, inputBuffer, outputBuffer) { const scene = this.scene const camera = this.camera const renderTargetA = this.renderTargetA const renderTargetB = this.renderTargetB let material = this.convolutionMaterial let uniforms = material.uniforms uniforms.depthBuffer.value = inputBuffer.depthTexture const kernel = material.kernel let lastRT = inputBuffer let destRT let i, l // Apply the multi-pass blur. for (i = 0, l = kernel.length - 1; i < l; ++i) { // Alternate between targets. destRT = (i & 1) === 0 ? renderTargetA : renderTargetB uniforms.kernel.value = kernel[i] uniforms.inputBuffer.value = lastRT.texture renderer.setRenderTarget(destRT) renderer.render(scene, camera) lastRT = destRT } uniforms.kernel.value = kernel[i] uniforms.inputBuffer.value = lastRT.texture renderer.setRenderTarget(this.renderToScreen ? null : outputBuffer) renderer.render(scene, camera) } }