(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "./Pass.js", "../shaders/CopyShader.js", "../shaders/LuminosityHighPassShader.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("./Pass.js"), require("../shaders/CopyShader.js"), require("../shaders/LuminosityHighPassShader.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.Pass, global.CopyShader, global.LuminosityHighPassShader); global.UnrealBloomPass = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, _Pass2, _CopyShader, _LuminosityHighPassShader) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.UnrealBloomPass = void 0; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } /** * 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/ */ var UnrealBloomPass = /*#__PURE__*/function (_Pass) { _inherits(UnrealBloomPass, _Pass); var _super = _createSuper(UnrealBloomPass); function UnrealBloomPass(resolution, strength, radius, threshold) { var _this; _classCallCheck(this, UnrealBloomPass); _this = _super.call(this); _this.strength = strength !== undefined ? strength : 1; _this.radius = radius; _this.threshold = threshold; _this.resolution = resolution !== undefined ? new _three.Vector2(resolution.x, resolution.y) : new _three.Vector2(256, 256); // create color only once here, reuse it later inside the render function _this.clearColor = new _three.Color(0, 0, 0); // render targets var pars = { minFilter: _three.LinearFilter, magFilter: _three.LinearFilter, format: _three.RGBAFormat }; _this.renderTargetsHorizontal = []; _this.renderTargetsVertical = []; _this.nMips = 5; var resx = Math.round(_this.resolution.x / 2); var resy = Math.round(_this.resolution.y / 2); _this.renderTargetBright = new _three.WebGLRenderTarget(resx, resy, pars); _this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'; _this.renderTargetBright.texture.generateMipmaps = false; for (var i = 0; i < _this.nMips; i++) { var renderTargetHorizonal = new _three.WebGLRenderTarget(resx, resy, pars); renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i; renderTargetHorizonal.texture.generateMipmaps = false; _this.renderTargetsHorizontal.push(renderTargetHorizonal); var renderTargetVertical = new _three.WebGLRenderTarget(resx, resy, pars); renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i; renderTargetVertical.texture.generateMipmaps = false; _this.renderTargetsVertical.push(renderTargetVertical); resx = Math.round(resx / 2); resy = Math.round(resy / 2); } // luminosity high pass material if (_LuminosityHighPassShader.LuminosityHighPassShader === undefined) console.error('THREE.UnrealBloomPass relies on LuminosityHighPassShader'); var highPassShader = _LuminosityHighPassShader.LuminosityHighPassShader; _this.highPassUniforms = _three.UniformsUtils.clone(highPassShader.uniforms); _this.highPassUniforms['luminosityThreshold'].value = threshold; _this.highPassUniforms['smoothWidth'].value = 0.01; _this.materialHighPassFilter = new _three.ShaderMaterial({ uniforms: _this.highPassUniforms, vertexShader: highPassShader.vertexShader, fragmentShader: highPassShader.fragmentShader, defines: {} }); // Gaussian Blur Materials _this.separableBlurMaterials = []; var kernelSizeArray = [3, 5, 7, 9, 11]; resx = Math.round(_this.resolution.x / 2); resy = Math.round(_this.resolution.y / 2); for (var _i = 0; _i < _this.nMips; _i++) { _this.separableBlurMaterials.push(_this.getSeperableBlurMaterial(kernelSizeArray[_i])); _this.separableBlurMaterials[_i].uniforms['texSize'].value = new _three.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; var bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2]; _this.compositeMaterial.uniforms['bloomFactors'].value = bloomFactors; _this.bloomTintColors = [new _three.Vector3(1, 1, 1), new _three.Vector3(1, 1, 1), new _three.Vector3(1, 1, 1), new _three.Vector3(1, 1, 1), new _three.Vector3(1, 1, 1)]; _this.compositeMaterial.uniforms['bloomTintColors'].value = _this.bloomTintColors; // copy material if (_CopyShader.CopyShader === undefined) { console.error('THREE.UnrealBloomPass relies on CopyShader'); } var copyShader = _CopyShader.CopyShader; _this.copyUniforms = _three.UniformsUtils.clone(copyShader.uniforms); _this.copyUniforms['opacity'].value = 1.0; _this.materialCopy = new _three.ShaderMaterial({ uniforms: _this.copyUniforms, vertexShader: copyShader.vertexShader, fragmentShader: copyShader.fragmentShader, blending: _three.AdditiveBlending, depthTest: false, depthWrite: false, transparent: true }); _this.enabled = true; _this.needsSwap = false; _this._oldClearColor = new _three.Color(); _this.oldClearAlpha = 1; _this.basic = new _three.MeshBasicMaterial(); _this.fsQuad = new _Pass2.FullScreenQuad(null); return _this; } _createClass(UnrealBloomPass, [{ key: "dispose", value: function dispose() { for (var i = 0; i < this.renderTargetsHorizontal.length; i++) { this.renderTargetsHorizontal[i].dispose(); } for (var _i2 = 0; _i2 < this.renderTargetsVertical.length; _i2++) { this.renderTargetsVertical[_i2].dispose(); } this.renderTargetBright.dispose(); } }, { key: "setSize", value: function setSize(width, height) { var resx = Math.round(width / 2); var resy = Math.round(height / 2); this.renderTargetBright.setSize(resx, resy); for (var 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 _three.Vector2(resx, resy); resx = Math.round(resx / 2); resy = Math.round(resy / 2); } } }, { key: "render", value: function render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { renderer.getClearColor(this._oldClearColor); this.oldClearAlpha = renderer.getClearAlpha(); var oldAutoClear = renderer.autoClear; renderer.autoClear = false; renderer.setClearColor(this.clearColor, 0); if (maskActive) renderer.state.buffers.stencil.setTest(false); // Render input to screen if (this.renderToScreen) { this.fsQuad.material = this.basic; this.basic.map = readBuffer.texture; renderer.setRenderTarget(null); renderer.clear(); this.fsQuad.render(renderer); } // 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. Blur All the mips progressively var inputRenderTarget = this.renderTargetBright; for (var i = 0; i < this.nMips; i++) { this.fsQuad.material = this.separableBlurMaterials[i]; this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture; this.separableBlurMaterials[i].uniforms['direction'].value = UnrealBloomPass.BlurDirectionX; renderer.setRenderTarget(this.renderTargetsHorizontal[i]); renderer.clear(); this.fsQuad.render(renderer); this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture; this.separableBlurMaterials[i].uniforms['direction'].value = UnrealBloomPass.BlurDirectionY; renderer.setRenderTarget(this.renderTargetsVertical[i]); renderer.clear(); this.fsQuad.render(renderer); inputRenderTarget = this.renderTargetsVertical[i]; } // Composite All the mips this.fsQuad.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.setRenderTarget(this.renderTargetsHorizontal[0]); renderer.clear(); this.fsQuad.render(renderer); // Blend it additively over the input texture this.fsQuad.material = this.materialCopy; this.copyUniforms['tDiffuse'].value = this.renderTargetsHorizontal[0].texture; if (maskActive) renderer.state.buffers.stencil.setTest(true); if (this.renderToScreen) { renderer.setRenderTarget(null); this.fsQuad.render(renderer); } else { renderer.setRenderTarget(readBuffer); this.fsQuad.render(renderer); } // Restore renderer settings renderer.setClearColor(this._oldClearColor, this.oldClearAlpha); renderer.autoClear = oldAutoClear; } }, { key: "getSeperableBlurMaterial", value: function getSeperableBlurMaterial(kernelRadius) { return new _three.ShaderMaterial({ defines: { 'KERNEL_RADIUS': kernelRadius, 'SIGMA': kernelRadius }, uniforms: { 'colorTexture': { value: null }, 'texSize': { value: new _three.Vector2(0.5, 0.5) }, 'direction': { value: new _three.Vector2(0.5, 0.5) } }, vertexShader: "varying vec2 vUv;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t\t\t}", fragmentShader: "#include \n\t\t\t\tvarying vec2 vUv;\n\t\t\t\tuniform sampler2D colorTexture;\n\t\t\t\tuniform vec2 texSize;\n\t\t\t\tuniform vec2 direction;\n\n\t\t\t\tfloat gaussianPdf(in float x, in float sigma) {\n\t\t\t\t\treturn 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\n\t\t\t\t}\n\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 invSize = 1.0 / texSize;\n\t\t\t\t\tfloat fSigma = float(SIGMA);\n\t\t\t\t\tfloat weightSum = gaussianPdf(0.0, fSigma);\n\t\t\t\t\tvec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\n\t\t\t\t\tfor( int i = 1; i < KERNEL_RADIUS; i ++ ) {\n\t\t\t\t\t\tfloat x = float(i);\n\t\t\t\t\t\tfloat w = gaussianPdf(x, fSigma);\n\t\t\t\t\t\tvec2 uvOffset = direction * invSize * x;\n\t\t\t\t\t\tvec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\n\t\t\t\t\t\tvec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\n\t\t\t\t\t\tdiffuseSum += (sample1 + sample2) * w;\n\t\t\t\t\t\tweightSum += 2.0 * w;\n\t\t\t\t\t}\n\t\t\t\t\tgl_FragColor = vec4(diffuseSum/weightSum, 1.0);\n\t\t\t\t}" }); } }, { key: "getCompositeMaterial", value: function getCompositeMaterial(nMips) { return new _three.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: "varying vec2 vUv;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t\t\t}", fragmentShader: "varying vec2 vUv;\n\t\t\t\tuniform sampler2D blurTexture1;\n\t\t\t\tuniform sampler2D blurTexture2;\n\t\t\t\tuniform sampler2D blurTexture3;\n\t\t\t\tuniform sampler2D blurTexture4;\n\t\t\t\tuniform sampler2D blurTexture5;\n\t\t\t\tuniform sampler2D dirtTexture;\n\t\t\t\tuniform float bloomStrength;\n\t\t\t\tuniform float bloomRadius;\n\t\t\t\tuniform float bloomFactors[NUM_MIPS];\n\t\t\t\tuniform vec3 bloomTintColors[NUM_MIPS];\n\n\t\t\t\tfloat lerpBloomFactor(const in float factor) {\n\t\t\t\t\tfloat mirrorFactor = 1.2 - factor;\n\t\t\t\t\treturn mix(factor, mirrorFactor, bloomRadius);\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\t\t\t\t\tgl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) +\n\t\t\t\t\t\tlerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) +\n\t\t\t\t\t\tlerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) +\n\t\t\t\t\t\tlerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) +\n\t\t\t\t\t\tlerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\n\t\t\t\t}" }); } }]); return UnrealBloomPass; }(_Pass2.Pass); _exports.UnrealBloomPass = UnrealBloomPass; UnrealBloomPass.BlurDirectionX = new _three.Vector2(1.0, 0.0); UnrealBloomPass.BlurDirectionY = new _three.Vector2(0.0, 1.0); });