postprocessing-fork
Version:
A post processing library for three.js.
1,732 lines (1,698 loc) • 1.97 MB
JavaScript
/**
* postprocessing-fork v6.37.4 build Mon Sep 15 2025
* https://github.com/pmndrs/postprocessing
* Copyright 2015-2025 Raoul van Rüschen
* @license Zlib
*/
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// package.json
var version = "6.37.4";
// src/core/index.js
import { PerspectiveCamera } from "three";
// src/core/Disposable.js
var Disposable = class {
/**
* Frees internal resources.
*/
dispose() {
}
};
// src/core/EffectComposer.js
import {
DepthStencilFormat,
DepthTexture,
LinearFilter as LinearFilter2,
SRGBColorSpace as SRGBColorSpace2,
UnsignedByteType as UnsignedByteType2,
UnsignedInt248Type,
UnsignedIntType,
Vector2 as Vector22,
WebGLRenderTarget as WebGLRenderTarget4,
NearestFilter as NearestFilter2,
RGBAFormat,
HalfFloatType
} from "three";
// 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;
}
};
// src/passes/Pass.js
import {
BasicDepthPacking,
BufferAttribute,
BufferGeometry,
Camera,
Material,
Mesh,
Scene,
Texture,
WebGLRenderTarget
} from "run-scene-core";
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.needsNormalTexture = 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.
*
* @deprecated Use getDepthTexture instead.
* @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) {
}
/**
* Returns the current normal texture.
*
* @return {Texture} The current normal texture, or null if there is none.
*/
getNormalTexture() {
return null;
}
/**
* Sets the normal texture.
*
* @param {Texture} normalTexture - A normal texture.
*/
setNormalTexture(normalTexture) {
}
/**
* 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.
* @param {DepthPass} [depthPass] - An optional shared depth pass for optimizing multiple effects.
*/
render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest, depthPass) {
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/passes/CopyPass.js
import { LinearFilter, SRGBColorSpace, UnsignedByteType, WebGLRenderTarget as WebGLRenderTarget2 } from "three";
// src/materials/CopyMaterial.js
import { NoBlending, ShaderMaterial, Uniform } from "three";
// 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 WebGLRenderTarget2(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;
}
}
}
};
// src/passes/DepthPass.js
import { Color as Color3, MeshDepthMaterial, NearestFilter, RGBADepthPacking, WebGLRenderTarget as WebGLRenderTarget3 } from "three";
// src/core/Resolution.js
import { EventDispatcher, Vector2 } from "three";
var AUTO_SIZE = -1;
var Resolution = class extends EventDispatcher {
/**
* Constructs a new resolution.
*
* TODO Remove resizable param.
* @param {Resizable} resizable - A resizable object.
* @param {Number} [width=Resolution.AUTO_SIZE] - The preferred width.
* @param {Number} [height=Resolution.AUTO_SIZE] - The preferred height.
* @param {Number} [scale=1.0] - A resolution scale.
*/
constructor(resizable, width = AUTO_SIZE, height = AUTO_SIZE, scale = 1) {
super();
this.resizable = resizable;
this.baseSize = new Vector2(1, 1);
this.preferredSize = new Vector2(width, height);
this.target = this.preferredSize;
this.s = scale;
this.effectiveSize = new Vector2();
this.addEventListener("change", () => this.updateEffectiveSize());
this.updateEffectiveSize();
}
/**
* Calculates the effective size.
*
* @private
*/
updateEffectiveSize() {
const base3 = this.baseSize;
const preferred = this.preferredSize;
const effective = this.effectiveSize;
const scale = this.scale;
if (preferred.width !== AUTO_SIZE) {
effective.width = preferred.width;
} else if (preferred.height !== AUTO_SIZE) {
effective.width = Math.round(preferred.height * (base3.width / Math.max(base3.height, 1)));
} else {
effective.width = Math.round(base3.width * scale);
}
if (preferred.height !== AUTO_SIZE) {
effective.height = preferred.height;
} else if (preferred.width !== AUTO_SIZE) {
effective.height = Math.round(preferred.width / Math.max(base3.width / Math.max(base3.height, 1), 1));
} else {
effective.height = Math.round(base3.height * scale);
}
}
/**
* The effective width.
*
* If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base width will be returned.
*
* @type {Number}
*/
get width() {
return this.effectiveSize.width;
}
set width(value) {
this.preferredWidth = value;
}
/**
* The effective height.
*
* If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base height will be returned.
*
* @type {Number}
*/
get height() {
return this.effectiveSize.height;
}
set height(value) {
this.preferredHeight = value;
}
/**
* Returns the effective width.
*
* If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base width will be returned.
*
* @deprecated Use width instead.
* @return {Number} The effective width.
*/
getWidth() {
return this.width;
}
/**
* Returns the effective height.
*
* If the preferred width and height are set to {@link Resizer.AUTO_SIZE}, the base height will be returned.
*
* @deprecated Use height instead.
* @return {Number} The effective height.
*/
getHeight() {
return this.height;
}
/**
* The resolution scale.
*
* @type {Number}
*/
get scale() {
return this.s;
}
set scale(value) {
if (this.s !== value) {
this.s = value;
this.preferredSize.setScalar(AUTO_SIZE);
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Returns the current resolution scale.
*
* @deprecated Use scale instead.
* @return {Number} The scale.
*/
getScale() {
return this.scale;
}
/**
* Sets the resolution scale.
*
* Also sets the preferred resolution to {@link Resizer.AUTO_SIZE}.
*
* @deprecated Use scale instead.
* @param {Number} value - The scale.
*/
setScale(value) {
this.scale = value;
}
/**
* The base width.
*
* @type {Number}
*/
get baseWidth() {
return this.baseSize.width;
}
set baseWidth(value) {
if (this.baseSize.width !== value) {
this.baseSize.width = value;
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Returns the base width.
*
* @deprecated Use baseWidth instead.
* @return {Number} The base width.
*/
getBaseWidth() {
return this.baseWidth;
}
/**
* Sets the base width.
*
* @deprecated Use baseWidth instead.
* @param {Number} value - The width.
*/
setBaseWidth(value) {
this.baseWidth = value;
}
/**
* The base height.
*
* @type {Number}
*/
get baseHeight() {
return this.baseSize.height;
}
set baseHeight(value) {
if (this.baseSize.height !== value) {
this.baseSize.height = value;
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Returns the base height.
*
* @deprecated Use baseHeight instead.
* @return {Number} The base height.
*/
getBaseHeight() {
return this.baseHeight;
}
/**
* Sets the base height.
*
* @deprecated Use baseHeight instead.
* @param {Number} value - The height.
*/
setBaseHeight(value) {
this.baseHeight = value;
}
/**
* Sets the base size.
*
* @param {Number} width - The width.
* @param {Number} height - The height.
*/
setBaseSize(width, height) {
if (this.baseSize.width !== width || this.baseSize.height !== height) {
this.baseSize.set(width, height);
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* The preferred width.
*
* @type {Number}
*/
get preferredWidth() {
return this.preferredSize.width;
}
set preferredWidth(value) {
if (this.preferredSize.width !== value) {
this.preferredSize.width = value;
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Returns the preferred width.
*
* @deprecated Use preferredWidth instead.
* @return {Number} The preferred width.
*/
getPreferredWidth() {
return this.preferredWidth;
}
/**
* Sets the preferred width.
*
* Use {@link Resizer.AUTO_SIZE} to automatically calculate the width based on the height and aspect ratio.
*
* @deprecated Use preferredWidth instead.
* @param {Number} value - The width.
*/
setPreferredWidth(value) {
this.preferredWidth = value;
}
/**
* The preferred height.
*
* @type {Number}
*/
get preferredHeight() {
return this.preferredSize.height;
}
set preferredHeight(value) {
if (this.preferredSize.height !== value) {
this.preferredSize.height = value;
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Returns the preferred height.
*
* @deprecated Use preferredHeight instead.
* @return {Number} The preferred height.
*/
getPreferredHeight() {
return this.preferredHeight;
}
/**
* Sets the preferred height.
*
* Use {@link Resizer.AUTO_SIZE} to automatically calculate the height based on the width and aspect ratio.
*
* @deprecated Use preferredHeight instead.
* @param {Number} value - The height.
*/
setPreferredHeight(value) {
this.preferredHeight = value;
}
/**
* Sets the preferred size.
*
* @param {Number} width - The width.
* @param {Number} height - The height.
*/
setPreferredSize(width, height) {
if (this.preferredSize.width !== width || this.preferredSize.height !== height) {
this.preferredSize.set(width, height);
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
}
/**
* Copies the given resolution.
*
* @param {Resolution} resolution - The resolution.
*/
copy(resolution) {
this.s = resolution.scale;
this.baseSize.set(resolution.baseWidth, resolution.baseHeight);
this.preferredSize.set(resolution.preferredWidth, resolution.preferredHeight);
this.dispatchEvent({ type: "change" });
this.resizable.setSize(this.baseSize.width, this.baseSize.height);
}
/**
* An auto sizing constant.
*
* Can be used to automatically calculate the width or height based on the original aspect ratio.
*
* @type {Number}
*/
static get AUTO_SIZE() {
return AUTO_SIZE;
}
};
// src/core/OverrideMaterialManager.js
import { BackSide, DoubleSide, FrontSide, ShaderMaterial as ShaderMaterial2 } from "three";
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 ShaderMaterial2)) {
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, renderOpts = {}) {
const shadowMapEnabled = renderer.shadowMap.enabled;
renderer.shadowMap.enabled = false;
let renderResult = null;
if (workaroundEnabled) {
const originalMaterials = this.originalMaterials;
this.meshCount = 0;
scene.traverse(this.replaceMaterial);
renderResult = renderer.render(scene, camera, renderOpts);
for (const entry of originalMaterials) {
entry[0].material = entry[1];
}
if (this.meshCount !== originalMaterials.size) {
originalMaterials.clear();
}
} else {
const overrideMaterial2 = scene.overrideMaterial;
scene.overrideMaterial = this.material;
renderResult = renderer.render(scene, camera, renderOpts);
scene.overrideMaterial = overrideMaterial2;
}
renderer.shadowMap.enabled = shadowMapEnabled;
return renderResult;
}
/**
* Deletes cloned override materials.
*
* @private
*/
disposeMaterials() {
if (this.material !== null) {
const materials = this.materials.concat(this.materialsBackSide).concat(this.materialsDoubleSide).concat(this.materialsFlatShaded).concat(this.materialsFlatShadedBackSide).concat(this.materialsFlatShadedDoubleSide);
for (const m2 of materials) {
m2.dispose();
}
}
}
/**
* Performs cleanup tasks.
*/
dispose() {
this.originalMaterials.clear();
this.disposeMaterials();
}
/**
* Indicates whether the override material workaround is enabled.
*
* @type {Boolean}
*/
static get workaroundEnabled() {
return workaroundEnabled;
}
/**
* Enables or disables the override material workaround globally.
*
* This only affects post processing passes and effects.
*
* @type {Boolean}
*/
static set workaroundEnabled(value) {
workaroundEnabled = value;
}
};
// src/passes/ClearPass.js
import { Color as Color2 } from "three";
var color = /* @__PURE__ */ new Color2();
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/utils/PerformanceLogger.js
var theEnabled = false;
var _PerformanceLogger = class _PerformanceLogger {
/**
* 获取单例实例
* @return {PerformanceLogger} 日志实例
*/
static getInstance() {
if (_PerformanceLogger.instance === null) {
_PerformanceLogger.instance = new _PerformanceLogger();
}
return _PerformanceLogger.instance;
}
/**
* 创建一个新的性能日志实例
* @param {Boolean} enabled - 是否启用日志
*/
constructor(enabled = theEnabled) {
this.enabled = enabled;
this.timers = /* @__PURE__ */ new Map();
this.collectTimes = false;
this.timeData = /* @__PURE__ */ new Map();
}
/**
* 启用日志
*/
enable() {
this.enabled = true;
}
/**
* 禁用日志
*/
disable() {
this.enabled = false;
}
/**
* 启用时间数据收集,用于性能分析
*/
enableTimeCollection() {
this.collectTimes = true;
}
/**
* 禁用时间数据收集
*/
disableTimeCollection() {
this.collectTimes = false;
}
/**
* 获取收集的时间数据
* @return {Object} 时间数据
*/
getTimeData() {
const result = {};
for (const [key, times] of this.timeData.entries()) {
if (times.length > 0) {
const sum = times.reduce((a, b) => a + b, 0);
result[key] = {
count: times.length,
total: sum,
average: sum / times.length,
min: Math.min(...times),
max: Math.max(...times)
};
}
}
return result;
}
/**
* 清除收集的时间数据
*/
clearTimeData() {
this.timeData.clear();
}
/**
* 开始计时
* @param {String} label - 计时标签
*/
time(label) {
if (!this.enabled) {
return;
}
console.time(label);
this.timers.set(label, performance.now());
}
/**
* 结束计时并打印结果
* @param {String} label - 计时标签
*/
timeEnd(label) {
if (!this.enabled) {
return;
}
console.timeEnd(label);
if (this.collectTimes && this.timers.has(label)) {
const startTime = this.timers.get(label);
const endTime = performance.now();
const duration = endTime - startTime;
if (!this.timeData.has(label)) {
this.timeData.set(label, []);
}
this.timeData.get(label).push(duration);
this.timers.delete(label);
}
}
/**
* 打印日志
* @param {...any} args - 日志参数
*/
log(...args) {
if (!this.enabled) {
return;
}
console.log(...args);
}
/**
* 打印警告
* @param {...any} args - 警告参数
*/
warn(...args) {
if (!this.enabled) {
return;
}
console.warn(...args);
}
/**
* 打印错误
* @param {...any} args - 错误参数
*/
error(...args) {
console.error(...args);
}
/**
* 打印性能摘要
*/
printSummary() {
if (!this.enabled || !this.collectTimes) {
return;
}
console.group("\u6027\u80FD\u65E5\u5FD7\u6458\u8981");
const data = this.getTimeData();
for (const [key, stats] of Object.entries(data)) {
console.log(
`${key}: ${stats.count}\u6B21\u8C03\u7528, \u5E73\u5747: ${stats.average.toFixed(2)}ms, \u603B\u8BA1: ${stats.total.toFixed(2)}ms, \u6700\u5C0F: ${stats.min.toFixed(2)}ms, \u6700\u5927: ${stats.max.toFixed(2)}ms`
);
}
console.groupEnd();
}
};
/**
* 单例实例
* @type {PerformanceLogger}
* @private
*/
__publicField(_PerformanceLogger, "instance", null);
var PerformanceLogger = _PerformanceLogger;
var logger = PerformanceLogger.getInstance();
var enableLogs = () => logger.enable();
var disableLogs = () => logger.disable();
var enableTimeCollection = () => logger.enableTimeCollection();
var disableTimeCollection = () => logger.disableTimeCollection();
var getTimeData = () => logger.getTimeData();
var clearTimeData = () => logger.clearTimeData();
var printSummary = () => logger.printSummary();
var timeLog = (label) => logger.time(label);
var timeEndLog = (label) => logger.timeEnd(label);
var log = (...args) => logger.log(...args);
var warn = (...args) => logger.warn(...args);
var error = (...args) => logger.error(...args);
// src/passes/RenderPass.js
var RenderPass = class extends Pass {
/**
* Constructs a new render pass.
*
* @param {Scene} scene - The scene to render.
* @param {Camera} camera - The camera to use to render the scene.
* @param {Material} [overrideMaterial=null] - An override material.
*/
constructor(scene, camera, overrideMaterial2 = null) {
super("RenderPass", scene, camera);
__publicField(this, "isRenderPass", true);
this.needsSwap = false;
this.clearPass = new ClearPass();
this.overrideMaterialManager = overrideMaterial2 === null ? null : new OverrideMaterialManager(overrideMaterial2);
this.ignoreBackground = false;
this.skipShadowMapUpdate = false;
this.selection = null;
}
set mainScene(value) {
this.scene = value;
}
set mainCamera(value) {
this.camera = value;
}
get renderToScreen() {
return super.renderToScreen;
}
set renderToScreen(value) {
super.renderToScreen = value;
this.clearPass.renderToScreen = value;
}
/**
* The current override material.
*
* @type {Material}
*/
get overrideMaterial() {
const manager = this.overrideMaterialManager;
return manager !== null ? manager.material : null;
}
set overrideMaterial(value) {
const manager = this.overrideMaterialManager;
if (value !== null) {
if (manager !== null) {
manager.setMaterial(value);
} else {
this.overrideMaterialManager = new OverrideMaterialManager(value);
}
} else if (manager !== null) {
manager.dispose();
this.overrideMaterialManager = null;
}
}
/**
* Returns the current override material.
*
* @deprecated Use overrideMaterial instead.
* @return {Material} The material.
*/
getOverrideMaterial() {
return this.overrideMaterial;
}
/**
* Sets the override material.
*
* @deprecated Use overrideMaterial instead.
* @return {Material} value - The material.
*/
setOverrideMaterial(value) {
this.overrideMaterial = value;
}
/**
* Indicates whether the target buffer should be cleared before rendering.
*
* @type {Boolean}
* @deprecated Use clearPass.enabled instead.
*/
get clear() {
return this.clearPass.enabled;
}
set clear(value) {
this.clearPass.enabled = value;
}
/**
* Returns the selection. Default is `null` (no restriction).
*
* @deprecated Use selection instead.
* @return {Selection} The selection.
*/
getSelection() {
return this.selection;
}
/**
* Sets the selection. Set to `null` to disable.
*
* @deprecated Use selection instead.
* @param {Selection} value - The selection.
*/
setSelection(value) {
this.selection = value;
}
/**
* Indicates whether the scene background is disabled.
*
* @deprecated Use ignoreBackground instead.
* @return {Boolean} Whether the scene background is disabled.
*/
isBackgroundDisabled() {
return this.ignoreBackground;
}
/**
* Enables or disables the scene background.
*
* @deprecated Use ignoreBackground instead.
* @param {Boolean} value - Whether the scene background should be disabled.
*/
setBackgroundDisabled(value) {
this.ignoreBackground = value;
}
/**
* Indicates whether the shadow map auto update is disabled.
*
* @deprecated Use skipShadowMapUpdate instead.
* @return {Boolean} Whether the shadow map update is disabled.
*/
isShadowMapDisabled() {
return this.skipShadowMapUpdate;
}
/**
* Enables or disables the shadow map auto update.
*
* @deprecated Use skipShadowMapUpdate instead.
* @param {Boolean} value - Whether the shadow map auto update should be disabled.
*/
setShadowMapDisabled(value) {
this.skipShadowMapUpdate = value;
}
/**
* Returns the clear pass.
*
* @deprecated Use clearPass.enabled instead.
* @return {ClearPass} The clear pass.
*/
getClearPass() {
return this.clearPass;
}
/**
* Renders the scene.
*
* @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, depthPass, renderOpts = {}, effectPassOpts) {
timeLog("RenderPass.render");
const scene = this.scene;
const camera = this.camera;
const selection = this.selection;
const mask = camera.layers.mask;
const background = scene.background;
const shadowMapAutoUpdate = renderer.shadowMap.autoUpdate;
const renderTarget = this.renderToScreen ? null : inputBuffer;
let renderResult = null;
if (selection !== null) {
camera.layers.set(selection.getLayer());
}
if (this.skipShadowMapUpdate) {
renderer.shadowMap.autoUpdate = false;
}
if (this.ignoreBackground || this.clearPass.overrideClearColor !== null) {
scene.background = null;
}
if (this.clearPass.enabled) {
this.clearPass.render(renderer, inputBuffer);
}
renderer.setRenderTarget(renderTarget);
if (this.overrideMaterialManager !== null) {
renderResult = this.overrideMaterialManager.render(renderer, scene, camera, renderOpts);
} else {
renderResult = renderer.render(scene, camera, renderOpts);
}
camera.layers.mask = mask;
scene.background = background;
renderer.shadowMap.autoUpdate = shadowMapAutoUpdate;
timeEndLog("RenderPass.render");
return renderResult;
}
};
// src/passes/DepthPass.js
var DepthPass = class extends Pass {
/**
* Constructs a new depth pass.
*
* @param {Scene} scene - The scene to render.
* @param {Camera} camera - The camera to use to render the scene.
* @param {Object} [options] - The options.
* @param {WebGLRenderTarget} [options.renderTarget] - A custom render target.
* @param {Number} [options.resolutionScale=1.0] - The resolution scale.
* @param {Number} [options.resolutionX=Resolution.AUTO_SIZE] - The horizontal resolution.
* @param {Number} [options.resolutionY=Resolution.AUTO_SIZE] - The vertical resolution.
* @param {Number} [options.width=Resolution.AUTO_SIZE] - Deprecated. Use resolutionX instead.
* @param {Number} [options.height=Resolution.AUTO_SIZE] - Deprecated. Use resolutionY instead.
*/
constructor(scene, camera, {
renderTarget,
resolutionScale = 1,
width = Resolution.AUTO_SIZE,
height = Resolution.AUTO_SIZE,
resolutionX = width,
resolutionY = height
} = {}) {
super("DepthPass");
this.needsSwap = false;
this.isDepthPass = true;
this.depthPacking = RGBADepthPacking;
this.renderPass = new RenderPass(scene, camera, new MeshDepthMaterial({
depthPacking: RGBADepthPacking
}));
const renderPass = this.renderPass;
renderPass.skipShadowMapUpdate = true;
renderPass.ignoreBackground = true;
const clearPass = renderPass.clearPass;
clearPass.overrideClearColor = new Color3(16777215);
clearPass.overrideClearAlpha = 1;
this.renderTarget = renderTarget;
if (this.renderTarget === void 0) {
this.renderTarget = new WebGLRenderTarget3(1, 1, {
minFilter: NearestFilter,
magFilter: NearestFilter
});
this.renderTarget.texture.name = "DepthPass.Target";
}
const resolution = this.resolution = new Resolution(this, resolutionX, resolutionY, resolutionScale);
resolution.addEventListener("change", (e) => this.setSize(resolution.baseWidth, resolution.baseHeight));
}
set mainScene(value) {
this.renderPass.mainScene = value;
}
set mainCamera(value) {
this.renderPass.mainCamera = value;
}
/**
* The depth texture.
*
* @type {Texture}
*/
get texture() {
return this.renderTarget.texture;
}
/**
* Returns the depth texture.
*
* @deprecated Use texture instead.
* @return {Texture} The texture.
*/
getTexture() {
return this.renderTarget.texture;
}
/**
* Returns the resolution settings.
*
* @deprecated Use resolution instead.
* @return {Resolution} The resolution.
*/
getResolution() {
return this.resolution;
}
/**
* Returns the current resolution scale.
*
* @return {Number} The resolution scale.
* @deprecated Use resolution instead.
*/
getResolutionScale() {
return this.resolution.scale;
}
/**
* Sets the resolution scale.
*
* @param {Number} scale - The new resolution scale.
* @deprecated Use resolution instead.
*/
setResolutionScale(scale) {
this.resolution.scale = scale;
}
/**
* Renders the scene depth.
*
* @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, depthPass, renderOpts = {}) {
timeLog("DepthPass.render");
const renderTarget = this.renderToScreen ? null : this.renderTarget;
let childClassName = "\u65E0\u5B50\u5BF9\u8C61";
if (this.renderPass.scene && this.renderPass.scene.children && this.renderPass.scene.children.length > 0) {
childClassName = this.renderPass.scene.children[0].constructor.name;
}
log(`DepthPass \u6E32\u67D3\u573A\u666F, \u7B2C\u4E00\u4E2A\u5B50\u5BF9\u8C61\u7C7B\u578B: ${childClassName}`);
this.renderPass.render(renderer, renderTarget, outputBuffer, deltaTime, stencilTest, depthPass, renderOpts);
timeEndLog("DepthPass.render");
}
/**
* Updates the size of this pass.
*
* @param {Number} width - The