interstellar / src /postprocessing /UnrealBloomPass.js
Pim Schreurs
Modernize the whole codebase; improve rendering
8194362
/**
* 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)