123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- import {
- AdditiveBlending,
- Color,
- HalfFloatType,
- MeshBasicMaterial,
- ShaderMaterial,
- UniformsUtils,
- Vector2,
- Vector3,
- WebGLRenderTarget,
- NoBlending,
- NormalBlending
- } from 'three';
- import { Pass, FullScreenQuad } from './Pass.js';
- import { CopyShader } from '../shaders/CopyShader.js';
- import { LuminosityHighPassShader } from '../shaders/LuminosityHighPassShader.js';
- /**
- * UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a
- * mip map chain of bloom textures and blurs them with different radii. Because
- * of the weighted combination of mips, and because larger blurs are done on
- * higher mips, this effect provides good quality and performance.
- *
- * Reference:
- * - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
- */
- class BloomPass extends Pass {
- constructor( width, height ) {
- super();
- this.strength = 1
- this.interation = 6;
- this.radius = 1;
- this.threshold = 1;
- this.width = ( width !== undefined ) ? width : 256
- this.height = ( height !== undefined ) ? height : 256
- this.clearColor = new Color( 0, 0, 0 );
- // render targets
- this.renderTargetsDownSample = [];
- this.renderTargetsUpSample = [];
-
- let resx = Math.round( this.width / 2 );
- let resy = Math.round( this.height / 2 );
- this.renderTargetBright = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
- this.renderTargetBright.texture.name = 'UnrealBloomPass.bright';
- this.renderTargetBright.texture.generateMipmaps = false;
- for(let i = 0; i < this.interation + 1; i++) {
- const rendererTargetDownSample = new WebGLRenderTarget(resx, resy, { type: HalfFloatType});
- rendererTargetDownSample.texture.name = 'DownSample_' + i;
- rendererTargetDownSample.texture.generateMipmaps = false;
- this.renderTargetsDownSample.push(rendererTargetDownSample);
- if(i == this.interation) break;
- const rendererTargetUpSample = new WebGLRenderTarget(resx, resy, { type: HalfFloatType});
- rendererTargetUpSample.texture.name = 'UpSample_' + i;
- rendererTargetUpSample.texture.generateMipmaps = false;
- this.renderTargetsUpSample.push(rendererTargetUpSample);
-
- resx = Math.round( resx / 2 );
- resy = Math.round( resy / 2 );
- }
- // console.log(this.renderTargetsDownSample, this.renderTargetsUpSample)
- // this.renderTargetDown = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
- // this.renderTargetDown.texture.generateMipmaps = false;
- // luminosity high pass material
- const highPassShader = LuminosityHighPassShader;
- this.highPassUniforms = UniformsUtils.clone( highPassShader.uniforms );
- this.highPassUniforms[ 'luminosityThreshold' ].value = this.threshold;
- this.highPassUniforms[ 'smoothWidth' ].value = 0.01;
- this.materialHighPassFilter = new ShaderMaterial( {
- uniforms: this.highPassUniforms,
- vertexShader: highPassShader.vertexShader,
- fragmentShader: highPassShader.fragmentShader
- } );
- // blur material
- this.downSampleMaterial = new ShaderMaterial({
- uniforms: UniformsUtils.clone( DownSampleShader.uniforms ),
- vertexShader: DownSampleShader.vertexShader,
- fragmentShader: DownSampleShader.fragmentShader,
- blending: NoBlending
- })
- this.upSampleMaterial = new ShaderMaterial({
- uniforms: UniformsUtils.clone( UpSampleShader.uniforms ),
- vertexShader: UpSampleShader.vertexShader,
- fragmentShader: UpSampleShader.fragmentShader,
- blending: NoBlending
- })
- // blend material
- const copyShader = BlendShader;
- this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
- this.copyMaterial = new ShaderMaterial( {
- uniforms: this.copyUniforms,
- vertexShader: copyShader.vertexShader,
- fragmentShader: copyShader.fragmentShader,
- blending: AdditiveBlending,
- // depthTest: false,
- // depthWrite: false,
- // transparent: true
- } );
- this.enabled = true;
- this.needsSwap = false;
- this._oldClearColor = new Color();
- this.oldClearAlpha = 1;
- this.fsQuad = new FullScreenQuad( null );
- }
- render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
- renderer.getClearColor( this._oldClearColor );
- this.oldClearAlpha = renderer.getClearAlpha();
- const oldAutoClear = renderer.autoClear;
- renderer.autoClear = false;
- renderer.setClearColor( this.clearColor, 0 );
- // 1. Extract Bright Areas
- this.highPassUniforms[ 'tDiffuse' ].value = readBuffer.texture;
- this.highPassUniforms[ 'luminosityThreshold' ].value = this.threshold;
- this.fsQuad.material = this.materialHighPassFilter;
- renderer.setRenderTarget( this.renderTargetBright );
- renderer.clear();
- this.fsQuad.render( renderer );
- // 2. DownSample
- this.downSampleMaterial.uniforms[ 'offset' ].value = this.radius;
- for(let i = 0; i < this.interation + 1; i++) {
- let preTarget = this.renderTargetsDownSample[i-1];
- if(i == 0) {
- preTarget = this.renderTargetBright;
- }
- this.downSampleMaterial.uniforms[ 'tDiffuse' ].value = preTarget.texture
- this.downSampleMaterial.uniforms[ 'resolution' ].value.set(preTarget.width, preTarget.height);
- this.renderPass(renderer, this.downSampleMaterial, this.renderTargetsDownSample[i]);
- }
- // 3. UpSample
- this.upSampleMaterial.uniforms[ 'offset' ].value = this.radius;
- for(let i = this.interation - 1; i >= 0; i--) {
- let preTarget = this.renderTargetsUpSample[i + 1];
- let addTarget = this.renderTargetsDownSample[i]
- if(i == this.interation - 1) {
- preTarget = this.renderTargetsDownSample[i + 1];
- }
- this.upSampleMaterial.uniforms[ 'tDiffuse' ].value = preTarget.texture
- this.upSampleMaterial.uniforms[ 'tAddDiffuse' ].value = addTarget.texture
- this.upSampleMaterial.uniforms[ 'resolution' ].value.set(preTarget.width, preTarget.height);
- this.renderPass(renderer, this.upSampleMaterial, this.renderTargetsUpSample[i]);
- }
-
- this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.renderTargetsUpSample[0].texture;
- this.copyMaterial.uniforms[ 'strength' ].value = this.strength;
- this.renderPass( renderer, this.copyMaterial, readBuffer );
- // Restore renderer settings
- renderer.setClearColor( this._oldClearColor, this.oldClearAlpha );
- renderer.autoClear = oldAutoClear;
- }
- renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
- // save original state
- renderer.getClearColor( this._oldClearColor );
- this.oldClearAlpha = renderer.getClearAlpha();
- const oldAutoClear = renderer.autoClear;
- renderer.setRenderTarget( renderTarget );
- // setup pass state
- renderer.autoClear = false;
- if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
- renderer.setClearColor( clearColor );
- renderer.setClearAlpha( clearAlpha || 0 );
- renderer.clear();
- }
- this.fsQuad.material = passMaterial;
- this.fsQuad.render( renderer );
- // restore original state
- renderer.autoClear = oldAutoClear;
- renderer.setClearColor( this._oldClearColor );
- renderer.setClearAlpha( this.oldClearAlpha );
- }
- dispose() {
- for ( let i = 0; i < this.renderTargetsDownSample.length; i ++ ) {
- this.renderTargetsDownSample[ i ].dispose();
- }
- for ( let i = 0; i < this.renderTargetsUpSample.length; i ++ ) {
- this.renderTargetsUpSample[ i ].dispose();
- }
- this.renderTargetBright.dispose();
- this.materialHighPassFilter.dispose();
- this.downSampleMaterial.dispose();
- this.upSampleMaterial.dispose();
- this.copyMaterial.dispose();
- this.fsQuad.dispose();
- }
- setSize( width, height ) {
- this.width = width;
- this.height = height;
- let resx = Math.round( width / 2 );
- let resy = Math.round( height / 2 );
- this.renderTargetBright.setSize( resx, resy ); //threshold
- for ( let i = 0; i < this.nMips; i ++ ) {
- this.renderTargetsDownSample[ i ].setSize( resx, resy );
- this.renderTargetsUpSample[ i ].setSize( resx, resy );
-
- resx = Math.round( resx / 2 );
- resy = Math.round( resy / 2 );
- }
- }
- }
- const DownSampleShader = {
- name: 'DownSampleShader',
- uniforms: {
- 'tDiffuse': { value: null },
- 'resolution': { value: new Vector2() },
- 'offset' : { value: 1.0 },
- },
- vertexShader: /* glsl */`
- varying vec2 vUv;
- void main() {
- vec4 modelViewPosition = modelViewMatrix * vec4( position, 1.0 );
- gl_Position = projectionMatrix * modelViewPosition;
- vUv = uv;
- }`,
- fragmentShader: /* glsl */`
- precision highp sampler2D;
- uniform sampler2D tDiffuse;
- uniform vec2 resolution;
- varying vec2 vUv;
- float GaussWeight2D(float x, float y, float sigma)
- {
- float PI = 3.14159265358;
- float E = 2.71828182846;
- float sigma_2 = pow(sigma, 2.0);
- float a = -(x*x + y*y) / (2.0 * sigma_2);
- return pow(E, a) / (2.0 * PI * sigma_2);
- }
- vec4 GaussNxN(sampler2D tex, vec2 uv, int n, vec2 stride, float sigma)
- {
- vec4 color = vec4(0.0);
- int r = n / 2;
- float weight = 0.0;
- for(int i=-r; i<=r; i++)
- {
- for(int j=-r; j<=r; j++)
- {
- float w = GaussWeight2D(float(i), float(j), sigma);
- vec2 coord = uv + vec2(i, j) * stride;
- color += texture2D(tex, coord) * w;
- weight += w;
- }
- }
- color /= weight;
- return color;
- }
- void main() {
- vec4 sum = vec4(0.0);
- vec2 stride = 1.0 / resolution;
- sum = GaussNxN(tDiffuse, vUv, 5, stride, 1.0);
- gl_FragColor = sum;
- }`
- };
- const UpSampleShader = {
- name: 'UpSampleShader',
- uniforms: {
- 'tDiffuse': { value: null },
- 'tAddDiffuse': { value: null },
- 'resolution': { value: new Vector2() },
- 'offset' : { value: 1.0 },
- },
- vertexShader: /* glsl */`
- varying vec2 vUv;
-
- void main() {
- vec4 modelViewPosition = modelViewMatrix * vec4( position, 1.0 );
- gl_Position = projectionMatrix * modelViewPosition;
- vUv = uv;
- }`,
- fragmentShader: /* glsl */`
- precision highp sampler2D;
- uniform sampler2D tDiffuse;
- uniform sampler2D tAddDiffuse;
- uniform vec2 resolution;
- uniform float offset;
- varying vec2 vUv;
- float GaussWeight2D(float x, float y, float sigma)
- {
- float PI = 3.14159265358;
- float E = 2.71828182846;
- float sigma_2 = pow(sigma, 2.0);
- float a = -(x*x + y*y) / (2.0 * sigma_2);
- return pow(E, a) / (2.0 * PI * sigma_2);
- }
- vec4 GaussNxN(sampler2D tex, vec2 uv, int n, vec2 stride, float sigma)
- {
- vec4 color = vec4(0.0);
- int r = n / 2;
- float weight = 0.0;
- for(int i=-r; i<=r; i++)
- {
- for(int j=-r; j<=r; j++)
- {
- float w = GaussWeight2D(float(i), float(j), sigma);
- vec2 coord = uv + vec2(i, j) * stride;
- color += texture2D(tex, coord) * w;
- weight += w;
- }
- }
- color /= weight;
- return color;
- }
- void main() {
- vec2 stride = 1.0 / resolution;
- vec2 curStride = 1.0 / resolution * 2.0;
- vec4 sum = GaussNxN(tDiffuse, vUv, 5, stride, offset);
- vec4 add = GaussNxN(tAddDiffuse, vUv, 5, stride, offset);
- gl_FragColor = sum + add;
- }`
- };
- const BlendShader = {
- name: 'BlendShader',
- uniforms: {
- 'tDiffuse': { value: null },
- 'strength': { value: 0.2 }
- },
- vertexShader: /* glsl */`
- varying vec2 vUv;
-
- void main() {
- vUv = uv;
- vec4 modelViewPosition = modelViewMatrix * vec4( position, 1.0 );
- gl_Position = projectionMatrix * modelViewPosition;
- }`,
- fragmentShader: /* glsl */`
- precision highp sampler2D;
- uniform sampler2D tDiffuse;
- uniform float strength;
- varying vec2 vUv;
- void main() {
- vec4 bloom = texture2D(tDiffuse, vUv);
- gl_FragColor = bloom * strength;
- // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
- }`
- };
- export { BloomPass };
|