Spaces:
Running
Running
| /** | |
| * The source code of this file is heavily based on | |
| * https://github.com/mrdoob/three.js/blob/dev/examples/js/postprocessing/UnrealBloomPass.js | |
| */ | |
| import { | |
| Vector2, | |
| Vector3, | |
| Color, | |
| ShaderMaterial, | |
| MeshBasicMaterial, | |
| LinearFilter, | |
| RGBAFormat, | |
| AdditiveBlending, | |
| UniformsUtils, | |
| WebGLRenderTarget | |
| } from 'three' | |
| import Pass from './Pass' | |
| import CopyShader from './CopyShader' | |
| import LuminosityHighPassShader from './LuminosityHighPassShader' | |
| import simpleVertexCode from '../shaders/simpleVertex.glsl' | |
| import seperableBlurCode from '../shaders/seperableBlur.glsl' | |
| import bloomCompositeCode from '../shaders/bloomComposite.glsl' | |
| export default class UnrealBloomPass extends Pass { | |
| constructor (resolution, strength, radius, threshold) { | |
| super() | |
| this.strength = strength | |
| this.radius = radius | |
| this.threshold = threshold | |
| this.resolution = new Vector2(resolution.x, resolution.y) | |
| // render targets | |
| const pars = { | |
| minFilter: LinearFilter, | |
| magFilter: LinearFilter, | |
| format: RGBAFormat | |
| } | |
| this.renderTargetsHorizontal = [] | |
| this.renderTargetsVertical = [] | |
| this.nMips = 5 | |
| let resx = Math.round(this.resolution.x / 2) | |
| let resy = Math.round(this.resolution.y / 2) | |
| this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars) | |
| this.renderTargetBright.texture.name = 'UnrealBloomPass.bright' | |
| this.renderTargetBright.texture.generateMipmaps = false | |
| for (let i = 0; i < this.nMips; i++) { | |
| let renderTarget = new WebGLRenderTarget(resx, resy, pars) | |
| renderTarget.texture.name = `UnrealBloomPass.h${i}` | |
| renderTarget.texture.generateMipmaps = false | |
| this.renderTargetsHorizontal.push(renderTarget) | |
| renderTarget = new WebGLRenderTarget(resx, resy, pars) | |
| renderTarget.texture.name = `UnrealBloomPass.v${i}` | |
| renderTarget.texture.generateMipmaps = false | |
| this.renderTargetsVertical.push(renderTarget) | |
| resx = Math.round(resx / 2) | |
| resy = Math.round(resy / 2) | |
| } | |
| this.highPassUniforms = UniformsUtils.clone(LuminosityHighPassShader.uniforms) | |
| this.highPassUniforms.luminosityThreshold.value = threshold | |
| this.highPassUniforms.smoothWidth.value = 0.01 | |
| this.materialHighPassFilter = new ShaderMaterial( | |
| { | |
| uniforms: this.highPassUniforms, | |
| vertexShader: LuminosityHighPassShader.vertexShader, | |
| fragmentShader: LuminosityHighPassShader.fragmentShader, | |
| defines: {} | |
| } | |
| ) | |
| // Gaussian Blur Materials | |
| this.separableBlurMaterials = [] | |
| const kernelSizeArray = [3, 5, 7, 9, 11] | |
| resx = Math.round(this.resolution.x / 2) | |
| resy = Math.round(this.resolution.y / 2) | |
| for (let i = 0; i < this.nMips; i++) { | |
| this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i])) | |
| this.separableBlurMaterials[i].uniforms.texSize.value = new Vector2(resx, resy) | |
| resx = Math.round(resx / 2) | |
| resy = Math.round(resy / 2) | |
| } | |
| // Composite material | |
| this.compositeMaterial = this.getCompositeMaterial(this.nMips) | |
| this.compositeMaterial.uniforms.blurTexture1.value = this.renderTargetsVertical[0].texture | |
| this.compositeMaterial.uniforms.blurTexture2.value = this.renderTargetsVertical[1].texture | |
| this.compositeMaterial.uniforms.blurTexture3.value = this.renderTargetsVertical[2].texture | |
| this.compositeMaterial.uniforms.blurTexture4.value = this.renderTargetsVertical[3].texture | |
| this.compositeMaterial.uniforms.blurTexture5.value = this.renderTargetsVertical[4].texture | |
| this.compositeMaterial.uniforms.bloomStrength.value = strength | |
| this.compositeMaterial.uniforms.bloomRadius.value = 0.1 | |
| this.compositeMaterial.needsUpdate = true | |
| const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2] | |
| this.compositeMaterial.uniforms.bloomFactors.value = bloomFactors | |
| this.bloomTintColors = [ | |
| new Vector3(1, 1, 1), | |
| new Vector3(1, 1, 1), | |
| new Vector3(1, 1, 1), | |
| new Vector3(1, 1, 1), | |
| new Vector3(1, 1, 1) | |
| ] | |
| this.compositeMaterial.uniforms.bloomTintColors.value = this.bloomTintColors | |
| this.copyUniforms = UniformsUtils.clone(CopyShader.uniforms) | |
| this.copyUniforms.opacity.value = 1.0 | |
| this.materialCopy = new ShaderMaterial( | |
| { | |
| uniforms: this.copyUniforms, | |
| vertexShader: CopyShader.vertexShader, | |
| fragmentShader: CopyShader.fragmentShader, | |
| blending: AdditiveBlending, | |
| depthTest: false, | |
| depthWrite: false, | |
| transparent: true | |
| } | |
| ) | |
| this.oldClearColor = new Color() | |
| this.oldClearAlpha = 1 | |
| this.basic = new MeshBasicMaterial() | |
| } | |
| dispose () { | |
| for (let i = 0; i < this.renderTargetsHorizontal.length; i++) { | |
| this.renderTargetsHorizontal[i].dispose() | |
| } | |
| for (let i = 0; i < this.renderTargetsVertical.length; i++) { | |
| this.renderTargetsVertical[i].dispose() | |
| } | |
| this.renderTargetBright.dispose() | |
| } | |
| setSize (width, height) { | |
| let resx = Math.round(width / 2) | |
| let resy = Math.round(height / 2) | |
| this.renderTargetBright.setSize(resx, resy) | |
| for (let i = 0; i < this.nMips; i++) { | |
| this.renderTargetsHorizontal[i].setSize(resx, resy) | |
| this.renderTargetsVertical[i].setSize(resx, resy) | |
| this.separableBlurMaterials[i].uniforms.texSize.value = new Vector2(resx, resy) | |
| resx = Math.round(resx / 2) | |
| resy = Math.round(resy / 2) | |
| } | |
| } | |
| render (renderer, writeBuffer, readBuffer) { | |
| this.oldClearColor.copy(renderer.getClearColor()) | |
| this.oldClearAlpha = renderer.getClearAlpha() | |
| const oldAutoClear = renderer.autoClear | |
| renderer.autoClear = false | |
| renderer.setClearColor(new Color(0, 0, 0), 0) | |
| // Render input to screen | |
| if (this.renderToScreen) { | |
| this.quad.material = this.basic | |
| this.basic.map = readBuffer.texture | |
| renderer.render(this.scene, this.camera, undefined, true) | |
| } | |
| // 1. Extract Bright Areas | |
| this.highPassUniforms.tDiffuse.value = readBuffer.texture | |
| this.highPassUniforms.luminosityThreshold.value = this.threshold | |
| this.quad.material = this.materialHighPassFilter | |
| renderer.render(this.scene, this.camera, this.renderTargetBright, true) | |
| // 2. Blur All the mips progressively | |
| let inputRenderTarget = this.renderTargetBright | |
| for (let i = 0; i < this.nMips; i++) { | |
| this.quad.material = this.separableBlurMaterials[i] | |
| this.separableBlurMaterials[i].uniforms.colorTexture.value = inputRenderTarget.texture | |
| this.separableBlurMaterials[i].uniforms.direction.value = UnrealBloomPass.BlurDirectionX | |
| renderer.render(this.scene, this.camera, this.renderTargetsHorizontal[i], true) | |
| this.separableBlurMaterials[i].uniforms.colorTexture.value = this.renderTargetsHorizontal[i].texture | |
| this.separableBlurMaterials[i].uniforms.direction.value = UnrealBloomPass.BlurDirectionY | |
| renderer.render(this.scene, this.camera, this.renderTargetsVertical[i], true) | |
| inputRenderTarget = this.renderTargetsVertical[i] | |
| } | |
| // Composite All the mips | |
| this.quad.material = this.compositeMaterial | |
| this.compositeMaterial.uniforms.bloomStrength.value = this.strength | |
| this.compositeMaterial.uniforms.bloomRadius.value = this.radius | |
| this.compositeMaterial.uniforms.bloomTintColors.value = this.bloomTintColors | |
| renderer.render(this.scene, this.camera, this.renderTargetsHorizontal[0], true) | |
| // Blend it additively over the input texture | |
| this.quad.material = this.materialCopy | |
| this.copyUniforms.tDiffuse.value = this.renderTargetsHorizontal[0].texture | |
| if (this.renderToScreen) { | |
| renderer.render(this.scene, this.camera, undefined, false) | |
| } | |
| else { | |
| renderer.render(this.scene, this.camera, readBuffer, false) | |
| } | |
| // Restore renderer settings | |
| renderer.setClearColor(this.oldClearColor, this.oldClearAlpha) | |
| renderer.autoClear = oldAutoClear | |
| } | |
| getSeperableBlurMaterial (kernelRadius) { | |
| return new ShaderMaterial( | |
| { | |
| defines: { | |
| KERNEL_RADIUS: kernelRadius, | |
| SIGMA: kernelRadius | |
| }, | |
| uniforms: { | |
| colorTexture: { value: null }, | |
| texSize: { value: new Vector2(0.5, 0.5) }, | |
| direction: { value: new Vector2(0.5, 0.5) } | |
| }, | |
| vertexShader: simpleVertexCode, | |
| fragmentShader: seperableBlurCode | |
| } | |
| ) | |
| } | |
| getCompositeMaterial (nMips) { | |
| return new ShaderMaterial( | |
| { | |
| defines: { | |
| NUM_MIPS: nMips | |
| }, | |
| uniforms: { | |
| blurTexture1: { value: null }, | |
| blurTexture2: { value: null }, | |
| blurTexture3: { value: null }, | |
| blurTexture4: { value: null }, | |
| blurTexture5: { value: null }, | |
| dirtTexture: { value: null }, | |
| bloomStrength: { value: 1.0 }, | |
| bloomFactors: { value: null }, | |
| bloomTintColors: { value: null }, | |
| bloomRadius: { value: 0.0 } | |
| }, | |
| vertexShader: simpleVertexCode, | |
| fragmentShader: bloomCompositeCode | |
| } | |
| ) | |
| } | |
| } | |
| UnrealBloomPass.BlurDirectionX = new Vector2(1, 0) | |
| UnrealBloomPass.BlurDirectionY = new Vector2(0, 1) | |