UNPKG

@google/model-viewer-effects

Version:

Easily add and combine post-processing effects with <model-viewer>!

1,349 lines (1,332 loc) 454 kB
import { MeshNormalMaterial, Color, WebGLRenderTarget, NearestFilter, SRGBColorSpace, BasicDepthPacking, UnsignedByteType, Scene, Camera, Mesh, BackSide, DoubleSide, ShaderMaterial, FrontSide, EventDispatcher, Vector2, DepthTexture, DepthStencilFormat, UnsignedInt248Type, UnsignedIntType, LinearFilter, NoBlending, Uniform, REVISION, PerspectiveCamera, LinearSRGBColorSpace, NoColorSpace, Material, Texture, BufferGeometry, BufferAttribute, Vector4, LinearMipmapLinearFilter, Vector3, RGBAFormat, RepeatWrapping, DataTexture, LuminanceFormat, RedFormat, RGFormat, MeshDepthMaterial, RGBADepthPacking, LoadingManager, FloatType, Matrix4, EqualDepth, NotEqualDepth, LessDepth, GreaterDepth, GreaterEqualDepth, LessEqualDepth, AlwaysDepth, NeverDepth, HalfFloatType, NeutralToneMapping, NoToneMapping } from 'three'; /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const t$1=globalThis,e$2=t$1.ShadowRoot&&(void 0===t$1.ShadyCSS||t$1.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$1=Symbol(),o$3=new WeakMap;let n$3 = class n{constructor(t,e,o){if(this._$cssResult$=true,o!==s$1)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$2&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o$3.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o$3.set(s,t));}return t}toString(){return this.cssText}};const r$4=t=>new n$3("string"==typeof t?t:t+"",void 0,s$1),S$1=(s,o)=>{if(e$2)s.adoptedStyleSheets=o.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet));else for(const e of o){const o=document.createElement("style"),n=t$1.litNonce;void 0!==n&&o.setAttribute("nonce",n),o.textContent=e.cssText,s.appendChild(o);}},c$2=e$2?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$4(e)})(t):t; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */const{is:i$2,defineProperty:e$1,getOwnPropertyDescriptor:r$3,getOwnPropertyNames:h$1,getOwnPropertySymbols:o$2,getPrototypeOf:n$2}=Object,a$1=globalThis,c$1=a$1.trustedTypes,l$1=c$1?c$1.emptyScript:"",p$1=a$1.reactiveElementPolyfillSupport,d$1=(t,s)=>t,u$1={toAttribute(t,s){switch(s){case Boolean:t=t?l$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t);}catch(t){i=null;}}return i}},f$1=(t,s)=>!i$2(t,s),y={attribute:true,type:String,converter:u$1,reflect:false,hasChanged:f$1};Symbol.metadata??=Symbol("metadata"),a$1.litPropertyMetadata??=new WeakMap;class b extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=y){if(s.state&&(s.attribute=false),this._$Ei(),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),r=this.getPropertyDescriptor(t,i,s);void 0!==r&&e$1(this.prototype,t,r);}}static getPropertyDescriptor(t,s,i){const{get:e,set:h}=r$3(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t;}};return {get(){return e?.call(this)},set(s){const r=e?.call(this);h.call(this,s),this.requestUpdate(t,r,i);},configurable:true,enumerable:true}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}static _$Ei(){if(this.hasOwnProperty(d$1("elementProperties")))return;const t=n$2(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties);}static finalize(){if(this.hasOwnProperty(d$1("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(d$1("properties"))){const t=this.properties,s=[...h$1(t),...o$2(t)];for(const i of s)this.createProperty(i,t[i]);}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i);}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(c$2(s));}else void 0!==s&&i.push(c$2(s));return i}static _$Eu(t,s){const i=s.attribute;return false===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach((t=>t(this)));}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.();}removeController(t){this._$EO?.delete(t);}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t);}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return S$1(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(true),this._$EO?.forEach((t=>t.hostConnected?.()));}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach((t=>t.hostDisconnected?.()));}attributeChangedCallback(t,s,i){this._$AK(t,i);}_$EC(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&true===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:u$1).toAttribute(s,i.type);this._$Em=t,null==r?this.removeAttribute(e):this.setAttribute(e,r),this._$Em=null;}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u$1;this._$Em=e,this[e]=r.fromAttribute(s,t.type),this._$Em=null;}}requestUpdate(t,s,i){if(void 0!==t){if(i??=this.constructor.getPropertyOptions(t),!(i.hasChanged??f$1)(this[t],s))return;this.P(t,s,i);} false===this.isUpdatePending&&(this._$ES=this._$ET());}P(t,s,i){this._$AL.has(t)||this._$AL.set(t,s),true===i.reflect&&this._$Em!==t&&(this._$Ej??=new Set).add(t);}async _$ET(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0;}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t) true!==i.wrapped||this._$AL.has(s)||void 0===this[s]||this.P(s,this[s],i);}let t=false;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach((t=>t.hostUpdate?.())),this.update(s)):this._$EU();}catch(s){throw t=false,this._$EU(),s}t&&this._$AE(s);}willUpdate(t){}_$AE(t){this._$EO?.forEach((t=>t.hostUpdated?.())),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(t)),this.updated(t);}_$EU(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return true}update(t){this._$Ej&&=this._$Ej.forEach((t=>this._$EC(t,this[t]))),this._$EU();}updated(t){}firstUpdated(t){}}b.elementStyles=[],b.shadowRootOptions={mode:"open"},b[d$1("elementProperties")]=new Map,b[d$1("finalized")]=new Map,p$1?.({ReactiveElement:b}),(a$1.reactiveElementVersions??=[]).push("2.0.4"); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */const o$1={attribute:true,type:String,converter:u$1,reflect:false,hasChanged:f$1},r$2=(t=o$1,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),s.set(r.name,t),"accessor"===n){const{name:o}=r;return {set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t);},init(e){return void 0!==e&&this.P(o,void 0,t),e}}}if("setter"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t);}}throw Error("Unsupported decorator location: "+n)};function n$1(t){return (e,o)=>"object"==typeof o?r$2(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,r?{...t,wrapped:true}:t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)} /** * postprocessing v6.37.2 build Fri Mar 28 2025 * https://github.com/pmndrs/postprocessing * Copyright 2015-2025 Raoul van Rüschen * @license Zlib */ // src/core/Timer.js var MILLISECONDS_TO_SECONDS = 1 / 1e3; var SECONDS_TO_MILLISECONDS = 1e3; var Timer = class { /** * Constructs a new timer. */ constructor() { this.startTime = performance.now(); this.previousTime = 0; this.currentTime = 0; this._delta = 0; this._elapsed = 0; this._fixedDelta = 1e3 / 60; this.timescale = 1; this.useFixedDelta = false; this._autoReset = false; } /** * Enables or disables auto reset based on page visibility. * * If enabled, the timer will be reset when the page becomes visible. This effectively pauses the timer when the page * is hidden. Has no effect if the API is not supported. * * @type {Boolean} * @see https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API */ get autoReset() { return this._autoReset; } set autoReset(value) { if (typeof document !== "undefined" && document.hidden !== void 0) { if (value) { document.addEventListener("visibilitychange", this); } else { document.removeEventListener("visibilitychange", this); } this._autoReset = value; } } get delta() { return this._delta * MILLISECONDS_TO_SECONDS; } get fixedDelta() { return this._fixedDelta * MILLISECONDS_TO_SECONDS; } set fixedDelta(value) { this._fixedDelta = value * SECONDS_TO_MILLISECONDS; } get elapsed() { return this._elapsed * MILLISECONDS_TO_SECONDS; } /** * Updates this timer. * * @param {Boolean} [timestamp] - The current time in milliseconds. */ update(timestamp) { if (this.useFixedDelta) { this._delta = this.fixedDelta; } else { this.previousTime = this.currentTime; this.currentTime = (timestamp !== void 0 ? timestamp : performance.now()) - this.startTime; this._delta = this.currentTime - this.previousTime; } this._delta *= this.timescale; this._elapsed += this._delta; } /** * Resets this timer. */ reset() { this._delta = 0; this._elapsed = 0; this.currentTime = performance.now() - this.startTime; } getDelta() { return this.delta; } getElapsed() { return this.elapsed; } handleEvent(e) { if (!document.hidden) { this.currentTime = performance.now() - this.startTime; } } dispose() { this.autoReset = false; } }; var fullscreenGeometry = /* @__PURE__ */ (() => { const vertices = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]); const uvs = new Float32Array([0, 0, 2, 0, 0, 2]); const geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(vertices, 3)); geometry.setAttribute("uv", new BufferAttribute(uvs, 2)); return geometry; })(); var Pass = class _Pass { /** * A shared fullscreen triangle. * * The screen size is 2x2 units (NDC). A triangle needs to be 4x4 units to fill the screen. * @see https://michaldrobot.com/2014/04/01/gcn-execution-patterns-in-full-screen-passes/ * @type {BufferGeometry} * @internal */ static get fullscreenGeometry() { return fullscreenGeometry; } /** * Constructs a new pass. * * @param {String} [name] - The name of this pass. Does not have to be unique. * @param {Scene} [scene] - The scene to render. The default scene contains a single mesh that fills the screen. * @param {Camera} [camera] - A camera. Fullscreen effect passes don't require a camera. */ constructor(name = "Pass", scene = new Scene(), camera = new Camera()) { this.name = name; this.renderer = null; this.scene = scene; this.camera = camera; this.screen = null; this.rtt = true; this.needsSwap = true; this.needsDepthTexture = false; this.enabled = true; } /** * Sets the render to screen flag. * * If this flag is changed, the fullscreen material will be updated as well. * * @type {Boolean} */ get renderToScreen() { return !this.rtt; } set renderToScreen(value) { if (this.rtt === value) { const material = this.fullscreenMaterial; if (material !== null) { material.needsUpdate = true; } this.rtt = !value; } } /** * Sets the main scene. * * @type {Scene} */ set mainScene(value) { } /** * Sets the main camera. * * @type {Camera} */ set mainCamera(value) { } /** * Sets the renderer * * @deprecated * @param {WebGLRenderer} renderer - The renderer. */ setRenderer(renderer) { this.renderer = renderer; } /** * Indicates whether this pass is enabled. * * @deprecated Use enabled instead. * @return {Boolean} Whether this pass is enabled. */ isEnabled() { return this.enabled; } /** * Enables or disables this pass. * * @deprecated Use enabled instead. * @param {Boolean} value - Whether the pass should be enabled. */ setEnabled(value) { this.enabled = value; } /** * The fullscreen material. * * @type {Material} */ get fullscreenMaterial() { return this.screen !== null ? this.screen.material : null; } set fullscreenMaterial(value) { let screen = this.screen; if (screen !== null) { screen.material = value; } else { screen = new Mesh(_Pass.fullscreenGeometry, value); screen.frustumCulled = false; if (this.scene === null) { this.scene = new Scene(); } this.scene.add(screen); this.screen = screen; } } /** * Returns the current fullscreen material. * * @deprecated Use fullscreenMaterial instead. * @return {Material} The current fullscreen material, or null if there is none. */ getFullscreenMaterial() { return this.fullscreenMaterial; } /** * Sets the fullscreen material. * * @deprecated Use fullscreenMaterial instead. * @protected * @param {Material} value - A fullscreen material. */ setFullscreenMaterial(value) { this.fullscreenMaterial = value; } /** * Returns the current depth texture. * * @return {Texture} The current depth texture, or null if there is none. */ getDepthTexture() { return null; } /** * Sets the depth texture. * * This method will be called automatically by the {@link EffectComposer}. * You may override this method if your pass relies on the depth information of a preceding {@link RenderPass}. * * @param {Texture} depthTexture - A depth texture. * @param {DepthPackingStrategy} [depthPacking=BasicDepthPacking] - The depth packing. */ setDepthTexture(depthTexture, depthPacking = BasicDepthPacking) { } /** * Renders this pass. * * This is an abstract method that must be overridden. * * @abstract * @throws {Error} An error is thrown if the method is not overridden. * @param {WebGLRenderer} renderer - The renderer. * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass. * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen. * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds. * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active. */ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) { throw new Error("Render method not implemented!"); } /** * Sets the size. * * You may override this method if you want to be informed about the size of the backbuffer/canvas. * This method is called before {@link initialize} and every time the size of the {@link EffectComposer} changes. * * @param {Number} width - The width. * @param {Number} height - The height. */ setSize(width, height) { } /** * Performs initialization tasks. * * This method is called when this pass is added to an {@link EffectComposer}. * * @param {WebGLRenderer} renderer - The renderer. * @param {Boolean} alpha - Whether the renderer uses the alpha channel or not. * @param {Number} frameBufferType - The type of the main frame buffers. */ initialize(renderer, alpha, frameBufferType) { } /** * Performs a shallow search for disposable properties and deletes them. * * The {@link EffectComposer} calls this method when it is being destroyed. You can use it independently to free * memory when you're certain that you don't need this pass anymore. */ dispose() { for (const key of Object.keys(this)) { const property = this[key]; const isDisposable = property instanceof WebGLRenderTarget || property instanceof Material || property instanceof Texture || property instanceof _Pass; if (isDisposable) { this[key].dispose(); } } if (this.fullscreenMaterial !== null) { this.fullscreenMaterial.dispose(); } } }; // src/passes/ClearMaskPass.js var ClearMaskPass = class extends Pass { /** * Constructs a new clear mask pass. */ constructor() { super("ClearMaskPass", null, null); this.needsSwap = false; } /** * Disables the global stencil test. * * @param {WebGLRenderer} renderer - The renderer. * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass. * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen. * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds. * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active. */ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) { const stencil = renderer.state.buffers.stencil; stencil.setLocked(false); stencil.setTest(false); } }; // src/materials/glsl/copy.frag var copy_default = `#include <common> #include <dithering_pars_fragment> #ifdef FRAMEBUFFER_PRECISION_HIGH uniform mediump sampler2D inputBuffer; #else uniform lowp sampler2D inputBuffer; #endif uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuffer,vUv);gl_FragColor=opacity*texel; #include <colorspace_fragment> #include <dithering_fragment> }`; // src/materials/glsl/common.vert var common_default = `varying vec2 vUv;void main(){vUv=position.xy*0.5+0.5;gl_Position=vec4(position.xy,1.0,1.0);}`; // src/materials/CopyMaterial.js var CopyMaterial = class extends ShaderMaterial { /** * Constructs a new copy material. */ constructor() { super({ name: "CopyMaterial", uniforms: { inputBuffer: new Uniform(null), opacity: new Uniform(1) }, blending: NoBlending, toneMapped: false, depthWrite: false, depthTest: false, fragmentShader: copy_default, vertexShader: common_default }); } /** * The input buffer. * * @type {Texture} */ set inputBuffer(value) { this.uniforms.inputBuffer.value = value; } /** * Sets the input buffer. * * @deprecated Use inputBuffer instead. * @param {Number} value - The buffer. */ setInputBuffer(value) { this.uniforms.inputBuffer.value = value; } /** * Returns the opacity. * * @deprecated Use opacity instead. * @return {Number} The opacity. */ getOpacity(value) { return this.uniforms.opacity.value; } /** * Sets the opacity. * * @deprecated Use opacity instead. * @param {Number} value - The opacity. */ setOpacity(value) { this.uniforms.opacity.value = value; } }; // src/passes/CopyPass.js var CopyPass = class extends Pass { /** * Constructs a new save pass. * * @param {WebGLRenderTarget} [renderTarget] - A render target. * @param {Boolean} [autoResize=true] - Whether the render target size should be updated automatically. */ constructor(renderTarget, autoResize = true) { super("CopyPass"); this.fullscreenMaterial = new CopyMaterial(); this.needsSwap = false; this.renderTarget = renderTarget; if (renderTarget === void 0) { this.renderTarget = new WebGLRenderTarget(1, 1, { minFilter: LinearFilter, magFilter: LinearFilter, stencilBuffer: false, depthBuffer: false }); this.renderTarget.texture.name = "CopyPass.Target"; } this.autoResize = autoResize; } /** * Enables or disables auto resizing of the render target. * * @deprecated Use autoResize instead. * @type {Boolean} */ get resize() { return this.autoResize; } set resize(value) { this.autoResize = value; } /** * The output texture. * * @type {Texture} */ get texture() { return this.renderTarget.texture; } /** * Returns the output texture. * * @deprecated Use texture instead. * @return {Texture} The texture. */ getTexture() { return this.renderTarget.texture; } /** * Enables or disables auto resizing of the render target. * * @deprecated Use autoResize instead. * @param {Boolean} value - Whether the render target size should be updated automatically. */ setAutoResizeEnabled(value) { this.autoResize = value; } /** * Saves the input buffer. * * @param {WebGLRenderer} renderer - The renderer. * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass. * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen. * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds. * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active. */ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) { this.fullscreenMaterial.inputBuffer = inputBuffer.texture; renderer.setRenderTarget(this.renderToScreen ? null : this.renderTarget); renderer.render(this.scene, this.camera); } /** * Updates the size of this pass. * * @param {Number} width - The width. * @param {Number} height - The height. */ setSize(width, height) { if (this.autoResize) { this.renderTarget.setSize(width, height); } } /** * Performs initialization tasks. * * @param {WebGLRenderer} renderer - A renderer. * @param {Boolean} alpha - Whether the renderer uses the alpha channel. * @param {Number} frameBufferType - The type of the main frame buffers. */ initialize(renderer, alpha, frameBufferType) { if (frameBufferType !== void 0) { this.renderTarget.texture.type = frameBufferType; if (frameBufferType !== UnsignedByteType) { this.fullscreenMaterial.defines.FRAMEBUFFER_PRECISION_HIGH = "1"; } else if (renderer !== null && renderer.outputColorSpace === SRGBColorSpace) { this.renderTarget.texture.colorSpace = SRGBColorSpace; } } } }; var color = /* @__PURE__ */ new Color(); var ClearPass = class extends Pass { /** * Constructs a new clear pass. * * @param {Boolean} [color=true] - Determines whether the color buffer should be cleared. * @param {Boolean} [depth=true] - Determines whether the depth buffer should be cleared. * @param {Boolean} [stencil=false] - Determines whether the stencil buffer should be cleared. */ constructor(color2 = true, depth = true, stencil = false) { super("ClearPass", null, null); this.needsSwap = false; this.color = color2; this.depth = depth; this.stencil = stencil; this.overrideClearColor = null; this.overrideClearAlpha = -1; } /** * Sets the clear flags. * * @param {Boolean} color - Whether the color buffer should be cleared. * @param {Boolean} depth - Whether the depth buffer should be cleared. * @param {Boolean} stencil - Whether the stencil buffer should be cleared. */ setClearFlags(color2, depth, stencil) { this.color = color2; this.depth = depth; this.stencil = stencil; } /** * Returns the override clear color. Default is null. * * @deprecated Use overrideClearColor instead. * @return {Color} The clear color. */ getOverrideClearColor() { return this.overrideClearColor; } /** * Sets the override clear color. * * @deprecated Use overrideClearColor instead. * @param {Color} value - The clear color. */ setOverrideClearColor(value) { this.overrideClearColor = value; } /** * Returns the override clear alpha. Default is -1. * * @deprecated Use overrideClearAlpha instead. * @return {Number} The clear alpha. */ getOverrideClearAlpha() { return this.overrideClearAlpha; } /** * Sets the override clear alpha. * * @deprecated Use overrideClearAlpha instead. * @param {Number} value - The clear alpha. */ setOverrideClearAlpha(value) { this.overrideClearAlpha = value; } /** * Clears the input buffer or the screen. * * @param {WebGLRenderer} renderer - The renderer. * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass. * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen. * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds. * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active. */ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) { const overrideClearColor = this.overrideClearColor; const overrideClearAlpha = this.overrideClearAlpha; const clearAlpha = renderer.getClearAlpha(); const hasOverrideClearColor = overrideClearColor !== null; const hasOverrideClearAlpha = overrideClearAlpha >= 0; if (hasOverrideClearColor) { renderer.getClearColor(color); renderer.setClearColor(overrideClearColor, hasOverrideClearAlpha ? overrideClearAlpha : clearAlpha); } else if (hasOverrideClearAlpha) { renderer.setClearAlpha(overrideClearAlpha); } renderer.setRenderTarget(this.renderToScreen ? null : inputBuffer); renderer.clear(this.color, this.depth, this.stencil); if (hasOverrideClearColor) { renderer.setClearColor(color, clearAlpha); } else if (hasOverrideClearAlpha) { renderer.setClearAlpha(clearAlpha); } } }; // src/passes/MaskPass.js var MaskPass = class extends Pass { /** * Constructs a new mask pass. * * @param {Scene} scene - The scene to render. * @param {Camera} camera - The camera to use. */ constructor(scene, camera) { super("MaskPass", scene, camera); this.needsSwap = false; this.clearPass = new ClearPass(false, false, true); this.inverse = false; } set mainScene(value) { this.scene = value; } set mainCamera(value) { this.camera = value; } /** * Indicates whether the mask should be inverted. * * @type {Boolean} */ get inverted() { return this.inverse; } set inverted(value) { this.inverse = value; } /** * Indicates whether this pass should clear the stencil buffer. * * @type {Boolean} * @deprecated Use clearPass.enabled instead. */ get clear() { return this.clearPass.enabled; } set clear(value) { this.clearPass.enabled = value; } /** * Returns the internal clear pass. * * @deprecated Use clearPass.enabled instead. * @return {ClearPass} The clear pass. */ getClearPass() { return this.clearPass; } /** * Indicates whether the mask is inverted. * * @deprecated Use inverted instead. * @return {Boolean} Whether the mask is inverted. */ isInverted() { return this.inverted; } /** * Enables or disable mask inversion. * * @deprecated Use inverted instead. * @param {Boolean} value - Whether the mask should be inverted. */ setInverted(value) { this.inverted = value; } /** * Renders the effect. * * @param {WebGLRenderer} renderer - The renderer. * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass. * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen. * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds. * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active. */ render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) { const context = renderer.getContext(); const buffers = renderer.state.buffers; const scene = this.scene; const camera = this.camera; const clearPass = this.clearPass; const writeValue = this.inverted ? 0 : 1; const clearValue = 1 - writeValue; buffers.color.setMask(false); buffers.depth.setMask(false); buffers.color.setLocked(true); buffers.depth.setLocked(true); buffers.stencil.setTest(true); buffers.stencil.setOp(context.REPLACE, context.REPLACE, context.REPLACE); buffers.stencil.setFunc(context.ALWAYS, writeValue, 4294967295); buffers.stencil.setClear(clearValue); buffers.stencil.setLocked(true); if (this.clearPass.enabled) { if (this.renderToScreen) { clearPass.render(renderer, null); } else { clearPass.render(renderer, inputBuffer); clearPass.render(renderer, outputBuffer); } } if (this.renderToScreen) { renderer.setRenderTarget(null); renderer.render(scene, camera); } else { renderer.setRenderTarget(inputBuffer); renderer.render(scene, camera); renderer.setRenderTarget(outputBuffer); renderer.render(scene, camera); } buffers.color.setLocked(false); buffers.depth.setLocked(false); buffers.stencil.setLocked(false); buffers.stencil.setFunc(context.EQUAL, 1, 4294967295); buffers.stencil.setOp(context.KEEP, context.KEEP, context.KEEP); buffers.stencil.setLocked(true); } }; // src/core/EffectComposer.js var EffectComposer$1 = class EffectComposer { /** * Constructs a new effect composer. * * @param {WebGLRenderer} renderer - The renderer that should be used. * @param {Object} [options] - The options. * @param {Boolean} [options.depthBuffer=true] - Whether the main render targets should have a depth buffer. * @param {Boolean} [options.stencilBuffer=false] - Whether the main render targets should have a stencil buffer. * @param {Boolean} [options.alpha] - Deprecated. Buffers are always RGBA since three r137. * @param {Number} [options.multisampling=0] - The number of samples used for multisample antialiasing. Requires WebGL 2. * @param {Number} [options.frameBufferType] - The type of the internal frame buffers. It's recommended to use HalfFloatType if possible. */ constructor(renderer = null, { depthBuffer = true, stencilBuffer = false, multisampling = 0, frameBufferType } = {}) { this.renderer = null; this.inputBuffer = this.createBuffer(depthBuffer, stencilBuffer, frameBufferType, multisampling); this.outputBuffer = this.inputBuffer.clone(); this.copyPass = new CopyPass(); this.depthTexture = null; this.passes = []; this.timer = new Timer(); this.autoRenderToScreen = true; this.setRenderer(renderer); } /** * The current amount of samples used for multisample anti-aliasing. * * @type {Number} */ get multisampling() { return this.inputBuffer.samples || 0; } /** * Sets the amount of MSAA samples. * * Requires WebGL 2. Set to zero to disable multisampling. * * @type {Number} */ set multisampling(value) { const buffer = this.inputBuffer; const multisampling = this.multisampling; if (multisampling > 0 && value > 0) { this.inputBuffer.samples = value; this.outputBuffer.samples = value; this.inputBuffer.dispose(); this.outputBuffer.dispose(); } else if (multisampling !== value) { this.inputBuffer.dispose(); this.outputBuffer.dispose(); this.inputBuffer = this.createBuffer( buffer.depthBuffer, buffer.stencilBuffer, buffer.texture.type, value ); this.inputBuffer.depthTexture = this.depthTexture; this.outputBuffer = this.inputBuffer.clone(); } } /** * Returns the internal timer. * * @return {Timer} The timer. */ getTimer() { return this.timer; } /** * Returns the renderer. * * @return {WebGLRenderer} The renderer. */ getRenderer() { return this.renderer; } /** * Sets the renderer. * * @param {WebGLRenderer} renderer - The renderer. */ setRenderer(renderer) { this.renderer = renderer; if (renderer !== null) { const size = renderer.getSize(new Vector2()); const alpha = renderer.getContext().getContextAttributes().alpha; const frameBufferType = this.inputBuffer.texture.type; if (frameBufferType === UnsignedByteType && renderer.outputColorSpace === SRGBColorSpace) { this.inputBuffer.texture.colorSpace = SRGBColorSpace; this.outputBuffer.texture.colorSpace = SRGBColorSpace; this.inputBuffer.dispose(); this.outputBuffer.dispose(); } renderer.autoClear = false; this.setSize(size.width, size.height); for (const pass of this.passes) { pass.initialize(renderer, alpha, frameBufferType); } } } /** * Replaces the current renderer with the given one. * * The auto clear mechanism of the provided renderer will be disabled. If the new render size differs from the * previous one, all passes will be updated. * * By default, the DOM element of the current renderer will automatically be removed from its parent node and the DOM * element of the new renderer will take its place. * * @deprecated Use setRenderer instead. * @param {WebGLRenderer} renderer - The new renderer. * @param {Boolean} updateDOM - Indicates whether the old canvas should be replaced by the new one in the DOM. * @return {WebGLRenderer} The old renderer. */ replaceRenderer(renderer, updateDOM = true) { const oldRenderer = this.renderer; const parent = oldRenderer.domElement.parentNode; this.setRenderer(renderer); if (updateDOM && parent !== null) { parent.removeChild(oldRenderer.domElement); parent.appendChild(renderer.domElement); } return oldRenderer; } /** * Creates a depth texture attachment that will be provided to all passes. * * Note: When a shader reads from a depth texture and writes to a render target that uses the same depth texture * attachment, the depth information will be lost. This happens even if `depthWrite` is disabled. * * @private * @return {DepthTexture} The depth texture. */ createDepthTexture() { const depthTexture = this.depthTexture = new DepthTexture(); this.inputBuffer.depthTexture = depthTexture; this.inputBuffer.dispose(); if (this.inputBuffer.stencilBuffer) { depthTexture.format = DepthStencilFormat; depthTexture.type = UnsignedInt248Type; } else { depthTexture.type = UnsignedIntType; } return depthTexture; } /** * Deletes the current depth texture. * * @private */ deleteDepthTexture() { if (this.depthTexture !== null) { this.depthTexture.dispose(); this.depthTexture = null; this.inputBuffer.depthTexture = null; this.inputBuffer.dispose(); for (const pass of this.passes) { pass.setDepthTexture(null); } } } /** * Creates a new render target. * * @deprecated Create buffers manually via WebGLRenderTarget instead. * @param {Boolean} depthBuffer - Whether the render target should have a depth buffer. * @param {Boolean} stencilBuffer - Whether the render target should have a stencil buffer. * @param {Number} type - The frame buffer type. * @param {Number} multisampling - The number of samples to use for antialiasing. * @return {WebGLRenderTarget} A new render target that equals the renderer's canvas. */ createBuffer(depthBuffer, stencilBuffer, type, multisampling) { const renderer = this.renderer; const size = renderer === null ? new Vector2() : renderer.getDrawingBufferSize(new Vector2()); const options = { minFilter: LinearFilter, magFilter: LinearFilter, stencilBuffer, depthBuffer, type }; const renderTarget = new WebGLRenderTarget(size.width, size.height, options); if (multisampling > 0) { renderTarget.ignoreDepthForMultisampleCopy = false; renderTarget.samples = multisampling; } if (type === UnsignedByteType && renderer !== null && renderer.outputColorSpace === SRGBColorSpace) { renderTarget.texture.colorSpace = SRGBColorSpace; } renderTarget.texture.name = "EffectComposer.Buffer"; renderTarget.texture.generateMipmaps = false; return renderTarget; } /** * Can be used to change the main scene for all registered passes and effects. * * @param {Scene} scene - The scene. */ setMainScene(scene) { for (const pass of this.passes) { pass.mainScene = scene; } } /** * Can be used to change the main camera for all registered passes and effects. * * @param {Camera} camera - The camera. */ setMainCamera(camera) { for (const pass of this.passes) { pass.mainCamera = camera; } } /** * Adds a pass, optionally at a specific index. * * @param {Pass} pass - A new pass. * @param {Number} [index] - An index at which the pass should be inserted. */ addPass(pass, index) { const passes = this.passes; const renderer = this.renderer; const drawingBufferSize = renderer.getDrawingBufferSize(new Vector2()); const alpha = renderer.getContext().getContextAttributes().alpha; const frameBufferType = this.inputBuffer.texture.type; pass.setRenderer(renderer); pass.setSize(drawingBufferSize.width, drawingBufferSize.height); pass.initialize(renderer, alpha, frameBufferType); if (this.autoRenderToScreen) { if (passes.length > 0) { passes[passes.length - 1].renderToScreen = false; } if (pass.renderToScreen) { this.autoRenderToScreen = false; } } if (index !== void 0) { passes.splice(index, 0, pass); } else { passes.push(pass); } if (this.autoRenderToScreen) { passes[passes.length - 1].renderToScreen = true; } if (pass.needsDepthTexture || this.depthTexture !== null) { if (this.depthTexture === null) { const depthTexture = this.createDepthTexture(); for (pass of passes) { pass.setDepthTexture(depthTexture); } } else { pass.setDepthTexture(this.depthTexture); } } } /** * Removes a pass. * * @param {Pass} pass - The pass. */ removePass(pass) { const passes = this.passes; const index = passes.indexOf(pass); const exists = index !== -1; const removed = exists && passes.splice(index, 1).length > 0; if (removed) { if (this.depthTexture !== null) { const reducer = (a, b) => a || b.needsDepthTexture; const depthTextureRequired = passes.reduce(reducer, false); if (!depthTextureRequired) { if (pass.getDepthTexture() === this.depthTexture) { pass.setDepthTexture(null); } this.deleteDepthTexture(); } } if (this.autoRenderToScreen) { if (index === passes.length) { pass.renderToScreen = false; if (passes.length > 0) { passes[passes.length - 1].renderToScreen = true; } } } } } /** * Removes all passes. */ removeAllPasses() { const passes = this.passes; this.deleteDepthTexture(); if (passes.length > 0) { if (this.autoRenderToScreen) { passes[passes.length - 1].renderToScreen = false; } this.passes = []; } } /** * Renders all enabled passes in the order in which they were added. * * @param {Number} [deltaTime] - The time since the last frame in seconds. */ render(deltaTime) { const renderer = this.renderer; const copyPass = this.copyPass; let inputBuffer = this.inputBuffer; let outputBuffer = this.outputBuffer; let stencilTest = false; let context, stencil, buffer; if (deltaTime === void 0) { this.timer.update(); deltaTime = this.timer.getDelta(); } for (const pass of this.passes) { if (pass.enabled) { pass.render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest); if (pass.needsSwap) { if (stencilTest) { copyPass.renderToScreen = pass.renderToScreen; context = renderer.getContext(); stencil = renderer.state.buffers.stencil; stencil.setFunc(context.NOTEQUAL, 1, 4294967295); copyPass.render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest); stencil.setFunc(context.EQUAL, 1, 4294967295); } buffer = inputBuffer; inputBuffer = outputBuffer; outputBuffer = buffer; } if (pass instanceof MaskPass) { stencilTest = true; } else if (pass instanceof ClearMaskPass) { stencilTest = false; } } } } /** * Sets the size of the buffers, passes and the renderer. * * @param {Number} width - The width. * @param {Number} height - The height. * @param {Boolean} [updateStyle] - Determines whether the style of the canvas should be updated. */ setSize(width, height, updateStyle) { const renderer = this.renderer; const currentSize = renderer.getSize(new Vector2()); if (width === void 0 || height === void 0) { width = currentSize.width; height = currentSize.height; } if (currentSize.width !== width || currentSize.height !== height) { renderer.setSize(width, height, updateStyle); } const drawingBufferSize = renderer.getDrawingBufferSize(new Vector2()); this.inputBuffer.setSize(drawingBufferSize.width, drawingBufferSize.height); this.outputBuffer.setSize(drawingBufferSize.width, drawingBufferSize.height); for (const pass of this.passes) { pass.setSize(drawingBufferSize.width, drawingBufferSize.height); } } /** * Resets this composer by deleting all passes and creating new buffers. */ reset() { this.dispose(); this.autoRenderToScreen = true; } /** * Disposes this composer and all passes. */ dispose() { for (const pass of this.passes) { pass.dispose(); } this.passes = []; if (this.inputBuffer !== null) { this.inputBuffer.dispose(); } if (this.outputBuffer !== null) { this.outputBuffer.dispose(); } this.deleteDepthTexture(); this.copyPass.dispose(); this.timer.dispose(); Pass.fullscreenGeometry.dispose(); } }; // src/enums/EffectAttribute.js var EffectAttribute = { NONE: 0, DEPTH: 1, CONVOLUTION: 2 }; // src/enums/EffectShaderSection.js var EffectShaderSection = { FRAGMENT_HEAD: "FRAGMENT_HEAD", FRAGMENT_MAIN_UV: "FRAGMENT_MAIN_UV", FRAGMENT_MAIN_IMAGE: "FRAGMENT_MAIN_IMAGE", VERTEX_HEAD: "VERTEX_HEAD", VERTEX_MAIN_SUPPORT: "VERTEX_MAIN_SUPPORT" }; // src/core/EffectShaderData.js var EffectShaderData = class { /** * Constructs new shader data. */ constructor() { this.shaderParts = /* @__PURE__ */ new Map([ [EffectShaderSection.FRAGMENT_HEAD, null], [EffectShaderSection.FRAGMENT_MAIN_UV, null], [EffectShaderSection.FRAGMENT_MAIN_IMAGE, null], [EffectShaderSection.VERTEX_HEAD, null], [EffectShaderSection.VERTEX_MAIN_SUPPORT, null] ]); this.defines = /* @__PURE__ */ new Map(); this.uniforms = /* @__PURE__ */ new Map(); this.blendModes = /* @__PURE__ */ new Map(); this.extensions = /* @__PURE__ */ new Set(); this.attributes = EffectAttribute.NONE; this.varyings = /* @__PURE__ */ new Set(); this.uvTransformation = false; this.readDepth = false; this.colorSpace = LinearSRGBColorSpace; } }; var workaroundEnabled = false; var OverrideMaterialManager = class { /** * Constructs a new override material manager. * * @param {Material} [material=null] - An override material. */ constructor(material = null) { this.originalMaterials = /* @__PURE__ */ new Map(); this.material = null; this.materials = null; this.materialsBackSide = null; this.materialsDoubleSide = null; this.materialsFlatShaded = null; this.materialsFlatShadedBackSide = null; this.materialsFlatShadedDoubleSide = null; this.setMaterial(material); this.meshCount = 0; this.replaceMaterial = (node) => { if (node.isMesh) { let materials; if (node.material.flatShading) { switch (node.material.side) { case DoubleSide: materials = this.materialsFlatShadedDoubleSide; break; case BackSide: materials = this.materialsFlatShadedBackSide; break; default: materials = this.materialsFlatShaded; break; } } else { switch (node.material.side) { case DoubleSide: materials = this.materialsDoubleSide; break; case BackSide: materials = this.materialsBackSide; break; default: materials = this.materials; break; } } this.originalMaterials.set(node, node.material); if (node.isSkinnedMesh) { node.material = materials[2]; } else if (node.isInstancedMesh) { node.material = materials[1]; } else { node.material = materials[0]; } ++this.meshCount; } }; } /** * Clones the given material. * * @private * @param {Material} material - The material. * @return {Material} The cloned material. */ cloneMaterial(material) { if (!(material instanceof ShaderMaterial)) { return material.clone(); } const uniforms = material.uniforms; const textureUniforms = /* @__PURE__ */ new Map(); for (const key in uniforms) { const value = uniforms[key].value; if (value.isRenderTargetTexture) { uniforms[key].value = null; textureUniforms.set(key, value); } } const clone = material.clone(); for (const entry of textureUniforms) { uniforms[entry[0]].value = entry[1]; clone.uniforms[entry[0]].value = entry[1]; } return clone; } /** * Sets the override material. * * @param {Material} material - The material. */ setMaterial(material) { this.disposeMaterials(); this.material = material; if (material !== null) { const materials = this.materials = [ this.cloneMaterial(material), this.cloneMaterial(material), this.cloneMaterial(material) ]; for (const m2 of materials) { m2.uniforms = Object.assign({}, material.uniforms); m2.side = FrontSide; } materials[2].skinning = true; this.materialsBackSide = materials.map((m2) => { const c2 = this.cloneMaterial(m2); c2.uniforms = Object.assign({}, material.uniforms); c2.side = BackSide; return c2; }); this.materialsDoubleSide = materials.map((m2) => { const c2 = this.cloneMaterial(m2); c2.uniforms = Object.assign({}, material.uniforms); c2.side = DoubleSide; return c2; }); this.materialsFlatShaded = materials.map((m2) => { const c2 = this.cloneMaterial(m2); c2.uniforms = Object.assign({}, material.uniforms); c2.flatShading = true; return c2; }); this.materialsFlatShadedBackSide = materials.map((m2) => { const c2 = this.cloneMaterial(m2); c2.uniforms = Object.assign({}, material.uniforms); c2.flatShading = true; c2.side = BackSide; return c2; }); this.materialsFlatShadedDoubleSide = materials.map((m2) => { const c2 = this.cloneMaterial(m2); c2.uniforms = Object.assign({}, material.uniforms); c2.flatShading = true; c2.side = DoubleSide; return c2; }); } } /** * Renders the scene with the override material. * * @private * @param {WebGLRenderer} renderer - The renderer. * @param {Scene} scene - A scene. * @param {Camera} camera - A camera. */ render(renderer, scene, camera) { const shadowMapEnabled = renderer.shadowMap.enabled; renderer.shadowMap.enabled = false; if (workaroundEnabled) { const originalMaterials = this.originalMaterials; this.meshCount = 0; scene.traverse(this.replaceMaterial); renderer.render(scene, camera);