postprocessing
Version:
A post processing library for three.js.
1,564 lines (1,540 loc) • 628 kB
JavaScript
/**
* postprocessing v6.37.2 build Fri Mar 28 2025
* https://github.com/pmndrs/postprocessing
* Copyright 2015-2025 Raoul van Rüschen
* @license Zlib
*/
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.js
var index_exports = {};
__export(index_exports, {
ASCIIEffect: () => ASCIIEffect,
ASCIITexture: () => ASCIITexture,
AdaptiveLuminanceMaterial: () => AdaptiveLuminanceMaterial,
AdaptiveLuminancePass: () => AdaptiveLuminancePass,
BlendFunction: () => BlendFunction,
BlendMode: () => BlendMode,
BloomEffect: () => BloomEffect,
BlurPass: () => KawaseBlurPass,
BokehEffect: () => BokehEffect,
BokehMaterial: () => BokehMaterial,
BoxBlurMaterial: () => BoxBlurMaterial,
BoxBlurPass: () => BoxBlurPass,
BrightnessContrastEffect: () => BrightnessContrastEffect,
ChromaticAberrationEffect: () => ChromaticAberrationEffect,
CircleOfConfusionMaterial: () => CircleOfConfusionMaterial,
ClearMaskPass: () => ClearMaskPass,
ClearPass: () => ClearPass,
ColorAverageEffect: () => ColorAverageEffect,
ColorChannel: () => ColorChannel,
ColorDepthEffect: () => ColorDepthEffect,
ColorEdgesMaterial: () => EdgeDetectionMaterial,
ConvolutionMaterial: () => KawaseBlurMaterial,
CopyMaterial: () => CopyMaterial,
CopyPass: () => CopyPass,
DepthComparisonMaterial: () => DepthComparisonMaterial,
DepthCopyMaterial: () => DepthCopyMaterial,
DepthCopyMode: () => DepthCopyMode,
DepthCopyPass: () => DepthCopyPass,
DepthDownsamplingMaterial: () => DepthDownsamplingMaterial,
DepthDownsamplingPass: () => DepthDownsamplingPass,
DepthEffect: () => DepthEffect,
DepthMaskMaterial: () => DepthMaskMaterial,
DepthOfFieldEffect: () => DepthOfFieldEffect,
DepthPass: () => DepthPass,
DepthPickingPass: () => DepthPickingPass,
DepthSavePass: () => DepthCopyPass,
DepthTestStrategy: () => DepthTestStrategy,
Disposable: () => Disposable,
DotScreenEffect: () => DotScreenEffect,
DownsamplingMaterial: () => DownsamplingMaterial,
EdgeDetectionMaterial: () => EdgeDetectionMaterial,
EdgeDetectionMode: () => EdgeDetectionMode,
Effect: () => Effect,
EffectAttribute: () => EffectAttribute,
EffectComposer: () => EffectComposer,
EffectMaterial: () => EffectMaterial,
EffectPass: () => EffectPass,
EffectShaderData: () => EffectShaderData,
EffectShaderSection: () => EffectShaderSection,
FXAAEffect: () => FXAAEffect,
GammaCorrectionEffect: () => GammaCorrectionEffect,
GaussKernel: () => GaussKernel,
GaussianBlurMaterial: () => GaussianBlurMaterial,
GaussianBlurPass: () => GaussianBlurPass,
GlitchEffect: () => GlitchEffect,
GlitchMode: () => GlitchMode,
GodRaysEffect: () => GodRaysEffect,
GodRaysMaterial: () => GodRaysMaterial,
GridEffect: () => GridEffect,
HueSaturationEffect: () => HueSaturationEffect,
ImmutableTimer: () => ImmutableTimer,
Initializable: () => Initializable,
KawaseBlurMaterial: () => KawaseBlurMaterial,
KawaseBlurPass: () => KawaseBlurPass,
KernelSize: () => KernelSize,
LUT1DEffect: () => LUT1DEffect,
LUT3DEffect: () => LUT3DEffect,
LUT3dlLoader: () => LUT3dlLoader,
LUTCubeLoader: () => LUTCubeLoader,
LUTEffect: () => LUT3DEffect,
LUTOperation: () => LUTOperation,
LambdaPass: () => LambdaPass,
LensDistortionEffect: () => LensDistortionEffect,
LookupTexture: () => LookupTexture,
LookupTexture3D: () => LookupTexture,
LuminanceMaterial: () => LuminanceMaterial,
LuminancePass: () => LuminancePass,
MaskFunction: () => MaskFunction,
MaskMaterial: () => MaskMaterial,
MaskPass: () => MaskPass,
MipmapBlurPass: () => MipmapBlurPass,
NoiseEffect: () => NoiseEffect,
NoiseTexture: () => NoiseTexture,
NormalPass: () => NormalPass,
OutlineEdgesMaterial: () => OutlineMaterial,
OutlineEffect: () => OutlineEffect,
OutlineMaterial: () => OutlineMaterial,
OverrideMaterialManager: () => OverrideMaterialManager,
Pass: () => Pass,
PixelationEffect: () => PixelationEffect,
PredicationMode: () => PredicationMode,
RawImageData: () => RawImageData,
RealisticBokehEffect: () => RealisticBokehEffect,
RenderPass: () => RenderPass,
Resizable: () => Resizable,
Resizer: () => Resolution,
Resolution: () => Resolution,
SMAAAreaImageData: () => SMAAAreaImageData,
SMAAEffect: () => SMAAEffect,
SMAAImageGenerator: () => SMAAImageGenerator,
SMAAImageLoader: () => SMAAImageLoader,
SMAAPreset: () => SMAAPreset,
SMAASearchImageData: () => SMAASearchImageData,
SMAAWeightsMaterial: () => SMAAWeightsMaterial,
SSAOEffect: () => SSAOEffect,
SSAOMaterial: () => SSAOMaterial,
SavePass: () => CopyPass,
ScanlineEffect: () => ScanlineEffect,
Section: () => EffectShaderSection,
Selection: () => Selection,
SelectiveBloomEffect: () => SelectiveBloomEffect,
SepiaEffect: () => SepiaEffect,
ShaderPass: () => ShaderPass,
ShockWaveEffect: () => ShockWaveEffect,
TetrahedralUpscaler: () => TetrahedralUpscaler,
TextureEffect: () => TextureEffect,
TiltShiftBlurMaterial: () => TiltShiftBlurMaterial,
TiltShiftBlurPass: () => TiltShiftBlurPass,
TiltShiftEffect: () => TiltShiftEffect,
Timer: () => Timer,
ToneMappingEffect: () => ToneMappingEffect,
ToneMappingMode: () => ToneMappingMode,
UpsamplingMaterial: () => UpsamplingMaterial,
VignetteEffect: () => VignetteEffect,
VignetteTechnique: () => VignetteTechnique,
WebGLExtension: () => WebGLExtension,
version: () => version
});
module.exports = __toCommonJS(index_exports);
// package.json
var version = "6.37.2";
// src/core/Disposable.js
var Disposable = class {
/**
* Frees internal resources.
*/
dispose() {
}
};
// src/core/EffectComposer.js
var import_three5 = require("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
var import_three = require("three");
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 import_three.BufferGeometry();
geometry.setAttribute("position", new import_three.BufferAttribute(vertices, 3));
geometry.setAttribute("uv", new import_three.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 import_three.Scene(), camera = new import_three.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 import_three.Mesh(_Pass.fullscreenGeometry, value);
screen.frustumCulled = false;
if (this.scene === null) {
this.scene = new import_three.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 = import_three.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 import_three.WebGLRenderTarget || property instanceof import_three.Material || property instanceof import_three.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
var import_three3 = require("three");
// src/materials/CopyMaterial.js
var import_three2 = require("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 import_three2.ShaderMaterial {
/**
* Constructs a new copy material.
*/
constructor() {
super({
name: "CopyMaterial",
uniforms: {
inputBuffer: new import_three2.Uniform(null),
opacity: new import_three2.Uniform(1)
},
blending: import_three2.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 import_three3.WebGLRenderTarget(1, 1, {
minFilter: import_three3.LinearFilter,
magFilter: import_three3.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 !== import_three3.UnsignedByteType) {
this.fullscreenMaterial.defines.FRAMEBUFFER_PRECISION_HIGH = "1";
} else if (renderer !== null && renderer.outputColorSpace === import_three3.SRGBColorSpace) {
this.renderTarget.texture.colorSpace = import_three3.SRGBColorSpace;
}
}
}
};
// src/passes/ClearPass.js
var import_three4 = require("three");
var color = /* @__PURE__ */ new import_three4.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 = class {
/**
* 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 import_three5.Vector2());
const alpha = renderer.getContext().getContextAttributes().alpha;
const frameBufferType = this.inputBuffer.texture.type;
if (frameBufferType === import_three5.UnsignedByteType && renderer.outputColorSpace === import_three5.SRGBColorSpace) {
this.inputBuffer.texture.colorSpace = import_three5.SRGBColorSpace;
this.outputBuffer.texture.colorSpace = import_three5.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 import_three5.DepthTexture();
this.inputBuffer.depthTexture = depthTexture;
this.inputBuffer.dispose();
if (this.inputBuffer.stencilBuffer) {
depthTexture.format = import_three5.DepthStencilFormat;
depthTexture.type = import_three5.UnsignedInt248Type;
} else {
depthTexture.type = import_three5.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 import_three5.Vector2() : renderer.getDrawingBufferSize(new import_three5.Vector2());
const options = {
minFilter: import_three5.LinearFilter,
magFilter: import_three5.LinearFilter,
stencilBuffer,
depthBuffer,
type
};
const renderTarget = new import_three5.WebGLRenderTarget(size.width, size.height, options);
if (multisampling > 0) {
renderTarget.ignoreDepthForMultisampleCopy = false;
renderTarget.samples = multisampling;
}
if (type === import_three5.UnsignedByteType && renderer !== null && renderer.outputColorSpace === import_three5.SRGBColorSpace) {
renderTarget.texture.colorSpace = import_three5.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 import_three5.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 import_three5.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 import_three5.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/core/EffectShaderData.js
var import_three6 = require("three");
// 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 = import_three6.LinearSRGBColorSpace;
}
};
// src/core/GaussKernel.js
function getCoefficients(n) {
let result;
if (n === 0) {
result = new Float64Array(0);
} else if (n === 1) {
result = new Float64Array([1]);
} else if (n > 1) {
let row0 = new Float64Array(n);
let row1 = new Float64Array(n);
for (let y = 1; y <= n; ++y) {
for (let x = 0; x < y; ++x) {
row1[x] = x === 0 || x === y - 1 ? 1 : row0[x - 1] + row0[x];
}
result = row1;
row1 = row0;
row0 = result;
}
}
return result;
}
var GaussKernel = class {
/**
* Constructs a new Gauss kernel.
*
* @param {Number} kernelSize - The kernel size. Should be an odd number in the range [3, 1020].
* @param {Number} [edgeBias=2] - Determines how many edge coefficients should be cut off for increased accuracy.
*/
constructor(kernelSize, edgeBias = 2) {
this.weights = null;
this.offsets = null;
this.linearWeights = null;
this.linearOffsets = null;
this.generate(kernelSize, edgeBias);
}
/**
* The number of steps for discrete sampling.
*
* @type {Number}
*/
get steps() {
return this.offsets === null ? 0 : this.offsets.length;
}
/**
* The number of steps for linear sampling.
*
* @type {Number}
*/
get linearSteps() {
return this.linearOffsets === null ? 0 : this.linearOffsets.length;
}
/**
* Generates the kernel.
*
* @private
* @param {Number} kernelSize - The kernel size.
* @param {Number} edgeBias - The amount of edge coefficients to ignore.
*/
generate(kernelSize, edgeBias) {
if (kernelSize < 3 || kernelSize > 1020) {
throw new Error("The kernel size must be in the range [3, 1020]");
}
const n = kernelSize + edgeBias * 2;
const coefficients = edgeBias > 0 ? getCoefficients(n).slice(edgeBias, -edgeBias) : getCoefficients(n);
const mid = Math.floor((coefficients.length - 1) / 2);
const sum = coefficients.reduce((a, b) => a + b, 0);
const weights = coefficients.slice(mid);
const offsets = [...Array(mid + 1).keys()];
const linearWeights = new Float64Array(Math.floor(offsets.length / 2));
const linearOffsets = new Float64Array(linearWeights.length);
linearWeights[0] = weights[0] / sum;
for (let i = 1, j = 1, l = offsets.length - 1; i < l; i += 2, ++j) {
const offset0 = offsets[i], offset1 = offsets[i + 1];
const weight0 = weights[i], weight1 = weights[i + 1];
const w = weight0 + weight1;
const o = (offset0 * weight0 + offset1 * weight1) / w;
linearWeights[j] = w / sum;
linearOffsets[j] = o;
}
for (let i = 0, l = weights.length, s = 1 / sum; i < l; ++i) {
weights[i] *= s;
}
const linearWeightSum = (linearWeights.reduce((a, b) => a + b, 0) - linearWeights[0] * 0.5) * 2;
if (linearWeightSum !== 0) {
for (let i = 0, l = linearWeights.length, s = 1 / linearWeightSum; i < l; ++i) {
linearWeights[i] *= s;
}
}
this.offsets = offsets;
this.weights = weights;
this.linearOffsets = linearOffsets;
this.linearWeights = linearWeights;
}
};
// src/core/ImmutableTimer.js
var ImmutableTimer = class {
/**
* The current delta time in seconds.
*
* @type {Number}
*/
getDelta() {
return NaN;
}
/**
* The elapsed time in seconds.
*
* @type {Number}
*/
getElapsed() {
return NaN;
}
};
// src/core/Initializable.js
var Initializable = class {
/**
* 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) {
}
};
// src/core/OverrideMaterialManager.js
var import_three7 = require("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 import_three7.DoubleSide:
materials = this.materialsFlatShadedDoubleSide;
break;
case import_three7.BackSide:
materials = this.materialsFlatShadedBackSide;
break;
default:
materials = this.materialsFlatShaded;
break;
}
} else {
switch (node.material.side) {
case import_three7.DoubleSide:
materials = this.materialsDoubleSide;
break;
case import_three7.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 import_three7.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 (