(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "./Pass.js", "../shaders/CopyShader.js", "../shaders/LuminosityShader.js", "../shaders/ToneMapShader.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("./Pass.js"), require("../shaders/CopyShader.js"), require("../shaders/LuminosityShader.js"), require("../shaders/ToneMapShader.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.Pass, global.CopyShader, global.LuminosityShader, global.ToneMapShader); global.AdaptiveToneMappingPass = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, _Pass2, _CopyShader, _LuminosityShader, _ToneMapShader) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.AdaptiveToneMappingPass = 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); } /** * Generate a texture that represents the luminosity of the current scene, adapted over time * to simulate the optic nerve responding to the amount of light it is receiving. * Based on a GDC2007 presentation by Wolfgang Engel titled "Post-Processing Pipeline" * * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf */ var AdaptiveToneMappingPass = /*#__PURE__*/function (_Pass) { _inherits(AdaptiveToneMappingPass, _Pass); var _super = _createSuper(AdaptiveToneMappingPass); function AdaptiveToneMappingPass(adaptive, resolution) { var _this; _classCallCheck(this, AdaptiveToneMappingPass); _this = _super.call(this); _this.resolution = resolution !== undefined ? resolution : 256; _this.needsInit = true; _this.adaptive = adaptive !== undefined ? !!adaptive : true; _this.luminanceRT = null; _this.previousLuminanceRT = null; _this.currentLuminanceRT = null; if (_CopyShader.CopyShader === undefined) console.error('THREE.AdaptiveToneMappingPass relies on CopyShader'); var copyShader = _CopyShader.CopyShader; _this.copyUniforms = _three.UniformsUtils.clone(copyShader.uniforms); _this.materialCopy = new _three.ShaderMaterial({ uniforms: _this.copyUniforms, vertexShader: copyShader.vertexShader, fragmentShader: copyShader.fragmentShader, blending: _three.NoBlending, depthTest: false }); if (_LuminosityShader.LuminosityShader === undefined) console.error('THREE.AdaptiveToneMappingPass relies on LuminosityShader'); _this.materialLuminance = new _three.ShaderMaterial({ uniforms: _three.UniformsUtils.clone(_LuminosityShader.LuminosityShader.uniforms), vertexShader: _LuminosityShader.LuminosityShader.vertexShader, fragmentShader: _LuminosityShader.LuminosityShader.fragmentShader, blending: _three.NoBlending }); _this.adaptLuminanceShader = { defines: { 'MIP_LEVEL_1X1': (Math.log(_this.resolution) / Math.log(2.0)).toFixed(1) }, uniforms: { 'lastLum': { value: null }, 'currentLum': { value: null }, 'minLuminance': { value: 0.01 }, 'delta': { value: 0.016 }, 'tau': { value: 1.0 } }, vertexShader: "varying vec2 vUv;\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\n\t\t\t\t}", fragmentShader: "varying vec2 vUv;\n\n\t\t\t\tuniform sampler2D lastLum;\n\t\t\t\tuniform sampler2D currentLum;\n\t\t\t\tuniform float minLuminance;\n\t\t\t\tuniform float delta;\n\t\t\t\tuniform float tau;\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec4 lastLum = texture2D( lastLum, vUv, MIP_LEVEL_1X1 );\n\t\t\t\t\tvec4 currentLum = texture2D( currentLum, vUv, MIP_LEVEL_1X1 );\n\n\t\t\t\t\tfloat fLastLum = max( minLuminance, lastLum.r );\n\t\t\t\t\tfloat fCurrentLum = max( minLuminance, currentLum.r );\n\n\t\t\t\t\t//The adaption seems to work better in extreme lighting differences\n\t\t\t\t\t//if the input luminance is squared.\n\t\t\t\t\tfCurrentLum *= fCurrentLum;\n\n\t\t\t\t\t// Adapt the luminance using Pattanaik's technique\n\t\t\t\t\tfloat fAdaptedLum = fLastLum + (fCurrentLum - fLastLum) * (1.0 - exp(-delta * tau));\n\t\t\t\t\t// \"fAdaptedLum = sqrt(fAdaptedLum);\n\t\t\t\t\tgl_FragColor.r = fAdaptedLum;\n\t\t\t\t}" }; _this.materialAdaptiveLum = new _three.ShaderMaterial({ uniforms: _three.UniformsUtils.clone(_this.adaptLuminanceShader.uniforms), vertexShader: _this.adaptLuminanceShader.vertexShader, fragmentShader: _this.adaptLuminanceShader.fragmentShader, defines: Object.assign({}, _this.adaptLuminanceShader.defines), blending: _three.NoBlending }); if (_ToneMapShader.ToneMapShader === undefined) console.error('THREE.AdaptiveToneMappingPass relies on ToneMapShader'); _this.materialToneMap = new _three.ShaderMaterial({ uniforms: _three.UniformsUtils.clone(_ToneMapShader.ToneMapShader.uniforms), vertexShader: _ToneMapShader.ToneMapShader.vertexShader, fragmentShader: _ToneMapShader.ToneMapShader.fragmentShader, blending: _three.NoBlending }); _this.fsQuad = new _Pass2.FullScreenQuad(null); return _this; } _createClass(AdaptiveToneMappingPass, [{ key: "render", value: function render(renderer, writeBuffer, readBuffer, deltaTime /*, maskActive*/ ) { if (this.needsInit) { this.reset(renderer); this.luminanceRT.texture.type = readBuffer.texture.type; this.previousLuminanceRT.texture.type = readBuffer.texture.type; this.currentLuminanceRT.texture.type = readBuffer.texture.type; this.needsInit = false; } if (this.adaptive) { //Render the luminance of the current scene into a render target with mipmapping enabled this.fsQuad.material = this.materialLuminance; this.materialLuminance.uniforms.tDiffuse.value = readBuffer.texture; renderer.setRenderTarget(this.currentLuminanceRT); this.fsQuad.render(renderer); //Use the new luminance values, the previous luminance and the frame delta to //adapt the luminance over time. this.fsQuad.material = this.materialAdaptiveLum; this.materialAdaptiveLum.uniforms.delta.value = deltaTime; this.materialAdaptiveLum.uniforms.lastLum.value = this.previousLuminanceRT.texture; this.materialAdaptiveLum.uniforms.currentLum.value = this.currentLuminanceRT.texture; renderer.setRenderTarget(this.luminanceRT); this.fsQuad.render(renderer); //Copy the new adapted luminance value so that it can be used by the next frame. this.fsQuad.material = this.materialCopy; this.copyUniforms.tDiffuse.value = this.luminanceRT.texture; renderer.setRenderTarget(this.previousLuminanceRT); this.fsQuad.render(renderer); } this.fsQuad.material = this.materialToneMap; this.materialToneMap.uniforms.tDiffuse.value = readBuffer.texture; if (this.renderToScreen) { renderer.setRenderTarget(null); this.fsQuad.render(renderer); } else { renderer.setRenderTarget(writeBuffer); if (this.clear) renderer.clear(); this.fsQuad.render(renderer); } } }, { key: "reset", value: function reset() { // render targets if (this.luminanceRT) { this.luminanceRT.dispose(); } if (this.currentLuminanceRT) { this.currentLuminanceRT.dispose(); } if (this.previousLuminanceRT) { this.previousLuminanceRT.dispose(); } var pars = { minFilter: _three.LinearFilter, magFilter: _three.LinearFilter, format: _three.RGBAFormat }; // was RGB format. changed to RGBA format. see discussion in #8415 / #8450 this.luminanceRT = new _three.WebGLRenderTarget(this.resolution, this.resolution, pars); this.luminanceRT.texture.name = 'AdaptiveToneMappingPass.l'; this.luminanceRT.texture.generateMipmaps = false; this.previousLuminanceRT = new _three.WebGLRenderTarget(this.resolution, this.resolution, pars); this.previousLuminanceRT.texture.name = 'AdaptiveToneMappingPass.pl'; this.previousLuminanceRT.texture.generateMipmaps = false; // We only need mipmapping for the current luminosity because we want a down-sampled version to sample in our adaptive shader pars.minFilter = _three.LinearMipmapLinearFilter; pars.generateMipmaps = true; this.currentLuminanceRT = new _three.WebGLRenderTarget(this.resolution, this.resolution, pars); this.currentLuminanceRT.texture.name = 'AdaptiveToneMappingPass.cl'; if (this.adaptive) { this.materialToneMap.defines['ADAPTED_LUMINANCE'] = ''; this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT.texture; } //Put something in the adaptive luminance texture so that the scene can render initially this.fsQuad.material = new _three.MeshBasicMaterial({ color: 0x777777 }); this.materialLuminance.needsUpdate = true; this.materialAdaptiveLum.needsUpdate = true; this.materialToneMap.needsUpdate = true; // renderer.render( this.scene, this.camera, this.luminanceRT ); // renderer.render( this.scene, this.camera, this.previousLuminanceRT ); // renderer.render( this.scene, this.camera, this.currentLuminanceRT ); } }, { key: "setAdaptive", value: function setAdaptive(adaptive) { if (adaptive) { this.adaptive = true; this.materialToneMap.defines['ADAPTED_LUMINANCE'] = ''; this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT.texture; } else { this.adaptive = false; delete this.materialToneMap.defines['ADAPTED_LUMINANCE']; this.materialToneMap.uniforms.luminanceMap.value = null; } this.materialToneMap.needsUpdate = true; } }, { key: "setAdaptionRate", value: function setAdaptionRate(rate) { if (rate) { this.materialAdaptiveLum.uniforms.tau.value = Math.abs(rate); } } }, { key: "setMinLuminance", value: function setMinLuminance(minLum) { if (minLum) { this.materialToneMap.uniforms.minLuminance.value = minLum; this.materialAdaptiveLum.uniforms.minLuminance.value = minLum; } } }, { key: "setMaxLuminance", value: function setMaxLuminance(maxLum) { if (maxLum) { this.materialToneMap.uniforms.maxLuminance.value = maxLum; } } }, { key: "setAverageLuminance", value: function setAverageLuminance(avgLum) { if (avgLum) { this.materialToneMap.uniforms.averageLuminance.value = avgLum; } } }, { key: "setMiddleGrey", value: function setMiddleGrey(middleGrey) { if (middleGrey) { this.materialToneMap.uniforms.middleGrey.value = middleGrey; } } }, { key: "dispose", value: function dispose() { if (this.luminanceRT) { this.luminanceRT.dispose(); } if (this.previousLuminanceRT) { this.previousLuminanceRT.dispose(); } if (this.currentLuminanceRT) { this.currentLuminanceRT.dispose(); } if (this.materialLuminance) { this.materialLuminance.dispose(); } if (this.materialAdaptiveLum) { this.materialAdaptiveLum.dispose(); } if (this.materialCopy) { this.materialCopy.dispose(); } if (this.materialToneMap) { this.materialToneMap.dispose(); } } }]); return AdaptiveToneMappingPass; }(_Pass2.Pass); _exports.AdaptiveToneMappingPass = AdaptiveToneMappingPass; });