UNPKG

@tresjs/post-processing

Version:
1,648 lines (1,589 loc) 157 kB
/** * name: @tresjs/post-processing * version: v2.4.0 * (c) 2025 * description: Post-processing library for TresJS * author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/) */ import { defineComponent as u, shallowRef as H, provide as He, computed as O, watch as g, onUnmounted as W, renderSlot as Ie, inject as J, watchEffect as w, nextTick as ce, toRaw as fe } from "vue"; import { EffectComposer as he, RenderPass as Je, NormalPass as Ze, DepthDownsamplingPass as _e, EffectPass as Xe, BloomEffect as pe, DepthOfFieldEffect as me, GlitchEffect as j, GlitchMode as $e, NoiseEffect as ve, OutlineEffect as ge, PixelationEffect as xe, VignetteEffect as be, Effect as Z, BlendFunction as _, ToneMappingEffect as Se, ChromaticAberrationEffect as ee, HueSaturationEffect as we, ScanlineEffect as te, ColorAverageEffect as oe, LensDistortionEffect as Ae, ShockWaveEffect as ye, TiltShiftEffect as Me, DotScreenEffect as Te, SepiaEffect as Ce, DepthPickingPass as et, GodRaysEffect as se, ColorDepthEffect as V, GridEffect as Ee, BrightnessContrastEffect as Pe, SMAAEffect as ae, TextureEffect as q, FXAAEffect as re, ASCIIEffect as ne, ASCIITexture as tt } from "postprocessing"; import { useTresContext as y, useLoop as I, normalizeColor as ie } from "@tresjs/core"; import { HalfFloatType as P, Uniform as C, Vector2 as f, Vector3 as B, Mesh as je, SphereGeometry as ot, MeshBasicMaterial as Ve, OrthographicCamera as st, BufferGeometry as at, Float32BufferAttribute as De, ShaderMaterial as T, UniformsUtils as L, WebGLRenderTarget as F, NoBlending as rt, Clock as nt, Color as K, MathUtils as M, DataTexture as it, RedFormat as lt, FloatType as ut, MeshNormalMaterial as ct, NearestFilter as N, DepthTexture as dt, Vector4 as ft, RawShaderMaterial as ht, ColorManagement as pt, SRGBTransfer as mt, LinearToneMapping as vt, ReinhardToneMapping as gt, CineonToneMapping as xt, ACESFilmicToneMapping as bt, AgXToneMapping as St, NeutralToneMapping as wt, CustomToneMapping as At, Texture as Le, LinearFilter as yt, AdditiveBlending as Mt } from "three"; import { useDevicePixelRatio as Ye } from "@vueuse/core"; class Fe { static isWebGL2Available() { try { const t = document.createElement("canvas"); return !!(window.WebGL2RenderingContext && t.getContext("webgl2")); } catch { return !1; } } static isColorSpaceAvailable(t) { try { const e = document.createElement("canvas"), o = window.WebGL2RenderingContext && e.getContext("webgl2"); return o.drawingBufferColorSpace = t, o.drawingBufferColorSpace === t; } catch { return !1; } } static getWebGL2ErrorMessage() { return this.getErrorMessage(2); } static getErrorMessage(t) { const e = { 1: "WebGL", 2: "WebGL 2" }, o = { 1: window.WebGLRenderingContext, 2: window.WebGL2RenderingContext }; let s = 'Your $0 does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">$1</a>'; const a = document.createElement("div"); return a.id = "webglmessage", a.style.fontFamily = "monospace", a.style.fontSize = "13px", a.style.fontWeight = "normal", a.style.textAlign = "center", a.style.background = "#fff", a.style.color = "#000", a.style.padding = "1.5em", a.style.width = "400px", a.style.margin = "5em auto 0", o[t] ? s = s.replace("$0", "graphics card") : s = s.replace("$0", "browser"), s = s.replace("$1", e[t]), a.innerHTML = s, a; } // @deprecated, r168 static isWebGLAvailable() { console.warn("isWebGLAvailable() has been deprecated and will be removed in r178. Use isWebGL2Available() instead."); try { const t = document.createElement("canvas"); return !!(window.WebGLRenderingContext && (t.getContext("webgl") || t.getContext("experimental-webgl"))); } catch { return !1; } } static getWebGLErrorMessage() { return console.warn("getWebGLErrorMessage() has been deprecated and will be removed in r178. Use getWebGL2ErrorMessage() instead."), this.getErrorMessage(1); } } const $ = Symbol("effectComposerPmndrs"), Yt = /* @__PURE__ */ u({ __name: "EffectComposerPmndrs", props: { enabled: { type: Boolean, default: !0 }, depthBuffer: { type: Boolean, default: void 0 }, disableNormalPass: { type: Boolean, default: !1 }, stencilBuffer: { type: Boolean, default: void 0 }, resolutionScale: {}, autoClear: { type: Boolean, default: !0 }, multisampling: { default: 0 }, frameBufferType: { default: P } }, emits: ["render"], setup(n, { expose: t, emit: e }) { const o = n, s = e, { scene: a, camera: r, renderer: i, sizes: l, render: d } = y(), p = H(null); let b = null, c = null; He($, p), t({ composer: p }); const S = () => { p.value && (c = new Ze(a.value, r.value), c.enabled = !1, p.value.addPass(c), o.resolutionScale !== void 0 && Fe.isWebGL2Available() && (b = new _e({ normalBuffer: c.texture, resolutionScale: o.resolutionScale }), b.enabled = !1, p.value.addPass(b))); }, m = O(() => { const x = new he(), G = { depthBuffer: o.depthBuffer !== void 0 ? o.depthBuffer : x.inputBuffer.depthBuffer, stencilBuffer: o.stencilBuffer !== void 0 ? o.stencilBuffer : x.inputBuffer.stencilBuffer, multisampling: Fe.isWebGL2Available() ? o.multisampling !== void 0 ? o.multisampling : x.multisampling : 0, frameBufferType: o.frameBufferType !== void 0 ? o.frameBufferType : P }; return x.dispose(), G; }), A = () => { var x; !i.value && !a.value && !r.value || ((x = p.value) == null || x.dispose(), p.value = new he(i.value, m.value), p.value.addPass(new Je(a.value, r.value)), o.disableNormalPass || S()); }; g([i, a, r, () => o.disableNormalPass], () => { !l.width.value || !l.height.value || A(); }), g(() => [l.width.value, l.height.value], ([x, G]) => { !x && !G || (p.value ? p.value.setSize(x, G) : A()); }, { immediate: !0 }); const { render: E } = I(); return E(() => { if (o.enabled && i.value && p.value && l.width.value && l.height.value && d.frames.value > 0) { const x = i.value.autoClear; i.value.autoClear = o.autoClear, o.stencilBuffer && !o.autoClear && i.value.clearStencil(), p.value.render(), s("render", p.value), i.value.autoClear = x; } d.frames.value = d.mode.value === "always" ? 1 : Math.max(0, d.frames.value - 1); }), W(() => { var x; (x = p.value) == null || x.dispose(); }), (x, G) => Ie(x.$slots, "default"); } }), h = (n, t, e) => { const o = J($), s = H(null), a = H(null), { scene: r, camera: i, invalidate: l } = y(); g(t, () => l()); const d = () => { var c, S, m; s.value && ((c = o == null ? void 0 : o.value) == null || c.removePass(s.value)), (S = a.value) == null || S.dispose(), (m = s.value) == null || m.dispose(); }, p = (c) => { !i.value || !(o != null && o.value) || !r.value || (a.value = n(), s.value = new Xe(i.value, a.value), o.value.addPass(s.value, c)); }; e && g( () => e.map((c) => t[c]), () => { var S; if (!(o != null && o.value)) return; const c = (S = o.value) == null ? void 0 : S.passes.findIndex((m) => m === s.value); ~c && (d(), p(c)); } ), w(() => { !i.value || !(a != null && a.value) || (a.value.mainCamera = i.value); }); const b = w(() => { !i.value || !(o != null && o.value) || !r.value || (ce(() => b()), !a.value && p()); }); return W(() => { d(); }), { pass: s, effect: a }; }, ke = /([^[.\]])+/g, Tt = (n, t) => { if (!t) return; const e = Array.isArray(t) ? t : t.match(ke); return e == null ? void 0 : e.reduce((o, s) => o && o[s], n); }, Oe = (n, t, e) => { const o = Array.isArray(t) ? t : t.match(ke); o && o.reduce((s, a, r) => (s[a] === void 0 && (s[a] = {}), r === o.length - 1 && (s[a] = e), s[a]), n); }, Ct = (n, t) => { const e = { ...n }; return t.forEach((o) => delete e[o]), e; }, X = (n, t, e, o, s = {}) => g(n, (a) => { var r; if (t.value) if (a === void 0) { const i = o(); Oe(t.value, e, Tt(i, e)), (r = i.dispose) == null || r.call(i); } else Oe(t.value, e, n()); }, s), v = (n, t, e) => n.map(([o, s]) => X( o, t, s, e )), de = (n, t, e) => Object.keys(n).map((o) => X( () => n[o], t, o, e )), kt = /* @__PURE__ */ u({ __name: "BloomPmndrs", props: { blendFunction: {}, intensity: {}, kernelSize: {}, luminanceThreshold: {}, luminanceSmoothing: {}, mipmapBlur: { type: Boolean, default: void 0 } }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new pe(e), e, ["mipmapBlur"]); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.intensity, "intensity"], [() => e.kernelSize, "kernelSize"], [() => e.luminanceSmoothing, "luminanceMaterial.smoothing"], [() => e.luminanceThreshold, "luminanceMaterial.threshold"] ], s, () => new pe() ), () => { }; } }), qt = /* @__PURE__ */ u({ __name: "DepthOfFieldPmndrs", props: { blendFunction: {}, worldFocusDistance: {}, worldFocusRange: {}, focusDistance: {}, focusRange: {}, bokehScale: {}, resolutionScale: {}, resolutionX: {}, resolutionY: {} }, setup(n, { expose: t }) { const e = n, { camera: o } = y(), { pass: s, effect: a } = h(() => new me(o.value, e), e); return t({ pass: s, effect: a }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.worldFocusDistance, "circleOfConfusionMaterial.worldFocusDistance"], [() => e.focusDistance, "circleOfConfusionMaterial.focusDistance"], [() => e.worldFocusRange, "circleOfConfusionMaterial.worldFocusRange"], [() => e.focusRange, "circleOfConfusionMaterial.focusRange"], [() => e.bokehScale, "bokehScale"], [() => e.resolutionScale, "blurPass.resolution.scale"], [() => e.resolutionX, "resolution.width"], [() => e.resolutionY, "resolution.height"] ], a, () => new me() ), () => { }; } }), Qt = /* @__PURE__ */ u({ __name: "GlitchPmndrs", props: { blendFunction: {}, delay: {}, duration: {}, strength: {}, mode: {}, active: { type: Boolean }, ratio: {}, columns: {}, chromaticAberrationOffset: {}, perturbationMap: {}, dtSize: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new j(e), e, ["dtSize"]); t({ pass: o, effect: s }); const { onBeforeRender: a } = I(); return a(({ invalidate: r }) => r()), w(() => { const r = () => { if (e.mode !== void 0) return e.active === !1 ? $e.DISABLED : e.mode; const i = new j(), l = i.mode; return i.dispose(), l; }; s.value && (s.value.mode = r()); }), X( () => e.blendFunction, s, "blendMode.blendFunction", () => new j() ), de( Ct(e, ["active", "blendFunction"]), s, () => new j() ), () => { }; } }), Kt = /* @__PURE__ */ u({ __name: "NoisePmndrs", props: { premultiply: { type: Boolean, default: void 0 }, blendFunction: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new ve(e), e); t({ pass: o, effect: s }); const { onBeforeRender: a } = I(); return a(({ invalidate: r }) => r()), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.premultiply, "premultiply"] ], s, () => new ve() ), () => { }; } }), Jt = /* @__PURE__ */ u({ __name: "OutlinePmndrs", props: { outlinedObjects: {}, blur: { type: Boolean, default: void 0 }, xRay: { type: Boolean, default: void 0 }, kernelSize: {}, pulseSpeed: {}, resolutionX: {}, resolutionY: {}, edgeStrength: {}, patternScale: {}, multisampling: {}, blendFunction: {}, patternTexture: {}, resolutionScale: {}, hiddenEdgeColor: {}, visibleEdgeColor: {} }, setup(n, { expose: t }) { const e = n, o = (d) => d !== void 0 ? ie(d).getHex() : void 0, { camera: s, scene: a } = y(), { pass: r, effect: i } = h( () => new ge( a.value, s.value, { blur: e.blur, xRay: e.xRay, kernelSize: e.kernelSize, pulseSpeed: e.pulseSpeed, resolutionX: e.resolutionX, resolutionY: e.resolutionY, patternScale: e.patternScale, edgeStrength: e.edgeStrength, blendFunction: e.blendFunction, multisampling: e.multisampling, patternTexture: e.patternTexture, resolutionScale: e.resolutionScale, hiddenEdgeColor: o(e.hiddenEdgeColor), visibleEdgeColor: o(e.visibleEdgeColor) // width and height are explicitly omitted, because they are deprecated in postprocessing's OutlineEffect } ), e ); t({ pass: r, effect: i }), g( [() => e.outlinedObjects, i], // watchEffect is intentionally not used here as it would result in an endless loop () => { var d; (d = i.value) == null || d.selection.set(e.outlinedObjects || []); }, { immediate: !0 } ); const l = O(() => ({ hiddenEdgeColor: e.hiddenEdgeColor ? ie(e.hiddenEdgeColor) : void 0, visibleEdgeColor: e.visibleEdgeColor ? ie(e.visibleEdgeColor) : void 0 })); return v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.blur, "blur"], [() => e.xRay, "xRay"], [() => e.pulseSpeed, "pulseSpeed"], [() => e.kernelSize, "kernelSize"], [() => e.edgeStrength, "edgeStrength"], [() => e.patternScale, "patternScale"], [() => e.multisampling, "multisampling"], [() => e.resolutionX, "resolution.width"], [() => e.resolutionY, "resolution.height"], [() => e.patternTexture, "patternTexture"], [() => e.resolutionScale, "resolution.scale"], [() => l.value.hiddenEdgeColor, "hiddenEdgeColor"], [() => l.value.visibleEdgeColor, "visibleEdgeColor"] ], i, () => new ge() ), () => { }; } }), Zt = /* @__PURE__ */ u({ __name: "PixelationPmndrs", props: { granularity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new xe(e.granularity), e); return t({ pass: o, effect: s }), de( e, s, () => new xe() ), () => { }; } }), _t = /* @__PURE__ */ u({ __name: "VignettePmndrs", props: { technique: {}, blendFunction: {}, offset: {}, darkness: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new be(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.offset, "offset"], [() => e.darkness, "darkness"], [() => e.technique, "technique"] ], s, () => new be() ), () => { }; } }); class Be extends Z { /** * Creates a new BarrelBlurEffect instance. * * @param {object} [options] - Configuration options for the effect. * @param {BlendFunction} [options.blendFunction] - Blend mode. * @param {number} [options.amount] - Intensity of the barrel distortion (0 to 1). * @param {Vector2} [options.offset] - Offset of the barrel distortion center (0 to 1 for both x and y). This allows you to change the position of the distortion effect. * */ constructor({ blendFunction: t = _.NORMAL, amount: e = 0.15, offset: o = new f(0.5, 0.5) } = {}) { super("BarrelBlurEffect", ` uniform float amount; uniform vec2 offset; #define NUM_ITER 16 #define RECIP_NUM_ITER 0.0625 #define GAMMA 1.0 vec3 spectrum_offset(float t) { float lo = step(t, 0.5); float hi = 1.0 - lo; float w = 1.0 - abs(2.0 * t - 1.0); return pow(vec3(lo, 1.0, hi) * vec3(1.0 - w, w, 1.0 - w), vec3(1.0 / GAMMA)); } vec2 barrelDistortion(vec2 p, float amt) { p = p - offset; float theta = atan(p.y, p.x); float radius = pow(length(p), 1.0 + 3.0 * amt); return vec2(cos(theta), sin(theta)) * radius + offset; } void mainUv(inout vec2 uv) { uv = barrelDistortion(uv, amount * 0.5); } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { vec3 sumcol = vec3(0.0); vec3 sumw = vec3(0.0); for (int i = 0; i < NUM_ITER; ++i) { float t = float(i) * RECIP_NUM_ITER; vec3 w = spectrum_offset(t); vec2 distortedUV = barrelDistortion(uv, amount * t); sumcol += w * texture(inputBuffer, distortedUV).rgb; sumw += w; } vec3 outcol = pow(sumcol / sumw, vec3(1.0 / GAMMA)); outcol = clamp(outcol, 0.0, 1.0); // Ensures normalized color values outputColor = vec4(outcol, inputColor.a); // Preserves original alpha } `, { blendFunction: t, uniforms: /* @__PURE__ */ new Map([ ["amount", new C(e)], // Uniform controlling the intensity of distortion ["offset", new C(o)] // Uniform controlling the offset of distortion ]) }); } /** * The amount. * * @type {number} */ get amount() { var t; return (t = this.uniforms.get("amount")) == null ? void 0 : t.value; } set amount(t) { this.uniforms.get("amount").value = t; } /** * The offset. * * @type {Vector2} */ get offset() { var t; return (t = this.uniforms.get("offset")) == null ? void 0 : t.value; } set offset(t) { this.uniforms.get("offset").value = t; } } const $t = /* @__PURE__ */ u({ __name: "BarrelBlurPmndrs", props: { blendFunction: {}, amount: {}, offset: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h( () => new Be({ ...e, offset: Array.isArray(e.offset) ? new f(...e.offset) : e.offset }), e ); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.amount, "amount"], [() => e.offset, "offset"] ], s, () => new Be() ), () => { }; } }), eo = /* @__PURE__ */ u({ __name: "ToneMappingPmndrs", props: { mode: {}, blendFunction: {}, resolution: {}, averageLuminance: {}, middleGrey: {}, minLuminance: {}, whitePoint: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Se(e), e); return t({ pass: o, effect: s }), v( [ [() => e.mode, "mode"], [() => e.blendFunction, "blendMode.blendFunction"], [() => e.resolution, "resolution"], [() => e.averageLuminance, "averageLuminance"], [() => e.middleGrey, "middleGrey"], [() => e.minLuminance, "adaptiveLuminanceMaterial.minLuminance"], [() => e.whitePoint, "whitePoint"] ], s, () => new Se() ), () => { }; } }), to = /* @__PURE__ */ u({ __name: "ChromaticAberrationPmndrs", props: { blendFunction: {}, offset: {}, radialModulation: { type: Boolean, default: void 0 }, modulationOffset: {} }, setup(n, { expose: t }) { const e = n, o = new ee(), { pass: s, effect: a } = h(() => new ee({ ...e, // Unfortunately, these defaults must be set this way as the type in postprocessing is not correct. // The arguments are optional in the actual constructor, but not in the type. radialModulation: e.radialModulation ?? o.radialModulation, modulationOffset: e.modulationOffset ?? o.modulationOffset }), e); return o.dispose(), t({ pass: s, effect: a }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.offset, "offset"], [() => e.radialModulation, "radialModulation"], [() => e.modulationOffset, "modulationOffset"] ], a, () => new ee() ), () => { }; } }), oo = /* @__PURE__ */ u({ __name: "HueSaturationPmndrs", props: { saturation: {}, hue: {}, blendFunction: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new we(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.hue, "hue"], [() => e.saturation, "saturation"] ], s, () => new we() ), () => { }; } }), so = /* @__PURE__ */ u({ __name: "ScanlinePmndrs", props: { blendFunction: {}, density: {}, scrollSpeed: {}, opacity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new te(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.density, "density"], [() => e.scrollSpeed, "scrollSpeed"] ], s, () => new te() ), g( [() => e.opacity], () => { var a, r; if (e.opacity !== void 0) (a = s.value) == null || a.blendMode.setOpacity(e.opacity); else { const i = new te(); (r = s.value) == null || r.blendMode.setOpacity(i.blendMode.getOpacity()), i.dispose(); } }, { immediate: !0 } ), () => { }; } }), Et = ` uniform float radius; uniform int sectorCount; const int MAX_SECTOR_COUNT = 8; float polynomialWeight(float x, float y, float eta, float lambda) { float polyValue = (x + eta) - lambda * (y * y); return max(0.0, polyValue * polyValue); } void getSectorVarianceAndAverageColor(mat2 anisotropyMat, float angle, float radius, out vec3 avgColor, out float variance) { vec3 weightedColorSum = vec3(0.0); vec3 weightedSquaredColorSum = vec3(0.0); float totalWeight = 0.0; float eta = 0.1; float lambda = 0.5; float angleStep = 0.196349; // Precompute angle step float halfAngleRange = 0.392699; // Precompute half angle range float cosAngle = cos(angle); float sinAngle = sin(angle); for (float r = 1.0; r <= radius; r += 1.0) { float rCosAngle = r * cosAngle; float rSinAngle = r * sinAngle; for (float a = -halfAngleRange; a <= halfAngleRange; a += angleStep) { float cosA = cos(a); float sinA = sin(a); vec2 sampleOffset = vec2(rCosAngle * cosA - rSinAngle * sinA, rCosAngle * sinA + rSinAngle * cosA) / resolution; sampleOffset *= anisotropyMat; vec3 color = texture2D(inputBuffer, vUv + sampleOffset).rgb; float weight = polynomialWeight(sampleOffset.x, sampleOffset.y, eta, lambda); weightedColorSum += color * weight; weightedSquaredColorSum += color * color * weight; totalWeight += weight; } } // Calculate average color and variance avgColor = weightedColorSum / totalWeight; vec3 varianceRes = (weightedSquaredColorSum / totalWeight) - (avgColor * avgColor); variance = dot(varianceRes, vec3(0.299, 0.587, 0.114)); // Convert to luminance } vec4 getDominantOrientation(vec4 structureTensor) { float Jxx = structureTensor.r; float Jyy = structureTensor.g; float Jxy = structureTensor.b; float trace = Jxx + Jyy; float det = Jxx * Jyy - Jxy * Jxy; float lambda1 = 0.5 * (trace + sqrt(trace * trace - 4.0 * det)); float lambda2 = 0.5 * (trace - sqrt(trace * trace - 4.0 * det)); float dominantOrientation = atan(2.0 * Jxy, Jxx - Jyy) / 2.0; return vec4(dominantOrientation, lambda1, lambda2, 0.0); } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { vec4 structureTensor = texture2D(inputBuffer, uv); vec3 sectorAvgColors[MAX_SECTOR_COUNT]; float sectorVariances[MAX_SECTOR_COUNT]; vec4 orientationAndAnisotropy = getDominantOrientation(structureTensor); vec2 orientation = orientationAndAnisotropy.xy; float anisotropy = (orientationAndAnisotropy.z - orientationAndAnisotropy.w) / (orientationAndAnisotropy.z + orientationAndAnisotropy.w + 1e-7); float alpha = 25.0; float scaleX = alpha / (anisotropy + alpha); float scaleY = (anisotropy + alpha) / alpha; mat2 anisotropyMat = mat2(orientation.x, -orientation.y, orientation.y, orientation.x) * mat2(scaleX, 0.0, 0.0, scaleY); for (int i = 0; i < sectorCount; i++) { float angle = float(i) * 6.28318 / float(sectorCount); // 2π / sectorCount getSectorVarianceAndAverageColor(anisotropyMat, angle, float(radius), sectorAvgColors[i], sectorVariances[i]); } float minVariance = sectorVariances[0]; vec3 finalColor = sectorAvgColors[0]; for (int i = 1; i < sectorCount; i++) { if (sectorVariances[i] < minVariance) { minVariance = sectorVariances[i]; finalColor = sectorAvgColors[i]; } } outputColor = vec4(finalColor, inputColor.a); } `; class Re extends Z { /** * Creates a new KuwaharaEffect instance. * * @param {object} [options] - Configuration options for the effect. * @param {BlendFunction} [options.blendFunction] - Blend mode. * @param {number} [options.radius] - Intensity of the effect. * @param {number} [options.sectorCount] - Number of sectors. * */ constructor({ blendFunction: t = _.NORMAL, radius: e = 1, sectorCount: o = 4 } = {}) { super("KuwaharaEffect", Et, { blendFunction: t, uniforms: /* @__PURE__ */ new Map([ ["radius", new C(e)], ["sectorCount", new C(o)] ]) }); } /** * The radius. * * @type {number} */ get radius() { var t; return (t = this.uniforms.get("radius")) == null ? void 0 : t.value; } set radius(t) { this.uniforms.get("radius").value = t; } /** * The sector count. * * @type {number} */ get sectorCount() { var t; return (t = this.uniforms.get("sectorCount")) == null ? void 0 : t.value; } set sectorCount(t) { this.uniforms.get("sectorCount").value = t; } } const ao = /* @__PURE__ */ u({ __name: "KuwaharaPmndrs", props: { blendFunction: {}, radius: {}, sectorCount: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h( () => new Re(e), e ); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.radius, "radius"], [() => e.sectorCount, "sectorCount"] ], s, () => new Re() ), () => { }; } }), ro = /* @__PURE__ */ u({ __name: "ColorAveragePmndrs", props: { blendFunction: {}, opacity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new oe(e.blendFunction), e); return t({ pass: o, effect: s }), X( () => e.blendFunction, s, "blendMode.blendFunction", () => new oe() ), g( [s, () => e.opacity], () => { var a, r; if (s.value) if (e.opacity !== void 0) (a = s.value) == null || a.blendMode.setOpacity(e.opacity); else { const i = new oe(); (r = s.value) == null || r.blendMode.setOpacity(i.blendMode.getOpacity()), i.dispose(); } } ), () => { }; } }), no = /* @__PURE__ */ u({ __name: "LensDistortionPmndrs", props: { distortion: {}, principalPoint: {}, focalLength: {}, skew: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h( () => new Ae({ ...e, distortion: e.distortion ? Array.isArray(e.distortion) ? new f(...e.distortion) : e.distortion : new f(), principalPoint: e.principalPoint ? Array.isArray(e.principalPoint) ? new f(...e.principalPoint) : e.principalPoint : new f(), focalLength: e.focalLength ? Array.isArray(e.focalLength) ? new f(...e.focalLength) : e.focalLength : new f() }), e ); return t({ pass: o, effect: s }), de( e, s, () => new Ae() ), () => { }; } }), io = /* @__PURE__ */ u({ __name: "ShockWavePmndrs", props: { position: {}, amplitude: {}, speed: {}, maxRadius: {}, waveSize: {} }, setup(n, { expose: t }) { const e = n, { camera: o } = y(), { pass: s, effect: a } = h( () => new ye(o.value, Array.isArray(e.position) ? new B(...e.position) : e.position, e), e ); return t({ pass: s, effect: a }), g( () => e.position, (r) => { a.value && (Array.isArray(r) ? a.value.position.set(...r) : r instanceof B && a.value.position.copy(r)); }, { immediate: !0 } ), v( [ [() => e.amplitude, "amplitude"], [() => e.waveSize, "waveSize"], [() => e.maxRadius, "maxRadius"], [() => e.speed, "speed"] ], a, () => new ye() ), () => { }; } }), lo = /* @__PURE__ */ u({ __name: "TiltShiftPmndrs", props: { blendFunction: {}, offset: {}, rotation: {}, focusArea: {}, feather: {}, kernelSize: {}, resolutionScale: {}, resolutionX: {}, resolutionY: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Me(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.offset, "offset"], [() => e.rotation, "rotation"], [() => e.focusArea, "focusArea"], [() => e.feather, "feather"], [() => e.kernelSize, "kernelSize"], [() => e.resolutionScale, "resolution.scale"], [() => e.resolutionX, "resolution.width"], [() => e.resolutionY, "resolution.height"] ], s, () => new Me() ), () => { }; } }), uo = /* @__PURE__ */ u({ __name: "DotScreenPmndrs", props: { angle: {}, scale: {}, blendFunction: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Te(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.angle, "angle"], [() => e.scale, "scale"] ], s, () => new Te() ), () => { }; } }), co = /* @__PURE__ */ u({ __name: "SepiaPmndrs", props: { blendFunction: {}, intensity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Ce(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.intensity, "intensity"] ], s, () => new Ce() ), () => { }; } }); class Ne extends Z { /** * Creates a new LinocutEffect instance. * * @param {LinocutPmndrsProps} [options] - Configuration options for the effect. * */ constructor({ blendFunction: t = _.NORMAL, scale: e = 0.85, noiseScale: o = 0, center: s = [0.5, 0.5], rotation: a = 0 } = {}) { const r = Array.isArray(s) ? new f().fromArray(s) : s; super("LinocutEffect", ` uniform float scale; uniform float noiseScale; uniform vec2 center; uniform float rotation; float luma(vec3 color) { return dot(color, vec3(0.299, 0.587, 0.114)); } float luma(vec4 color) { return dot(color.rgb, vec3(0.299, 0.587, 0.114)); } // Simple pseudo-random noise function float noise(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { // Calculate the center based on center vec2 fragCoord = uv * resolution.xy; // Apply rotation to the coordinates vec2 d = fragCoord - center * resolution.xy; mat2 rotMat = mat2(cos(rotation), -sin(rotation), sin(rotation), cos(rotation)); vec2 rotatedD = d * rotMat; // Calculate radial distance and angle float r = length(rotatedD) / (1000.0 / max(scale, 0.01)); // Normalization to avoid artifacts float a = atan(rotatedD.y, rotatedD.x) + scale * (0.5 - r) / 0.5; // Calculate transformed coordinates vec2 uvt = center * resolution.xy + r * vec2(cos(a), sin(a)); // Normalize UV coordinates vec2 uv2 = fragCoord / resolution.xy; // Generate sinusoidal line patterns float c = (0.75 + 0.25 * sin(uvt.x * 1000.0 * max(scale, 0.01))); // Prevent excessive distortions // Load the texture and convert to grayscale vec4 color = texture(inputBuffer, uv2); color.rgb = color.rgb * color.rgb; // Convert from sRGB to linear float l = luma(color); // Add noise based on noiseScale float n = noise(uv2 * 10.0); // Generate noise l += noiseScale * (n - 0.5); // Apply noise as a perturbation // Apply smoothing to achieve the linocut effect float f = smoothstep(0.5 * c, c, l); f = smoothstep(0.0, 0.5, f); // Convert the final value back to sRGB f = sqrt(f); // Output the final color in black and white outputColor = vec4(vec3(f), 1.0); } `, { blendFunction: t, uniforms: /* @__PURE__ */ new Map([ ["scale", new C(e)], ["noiseScale", new C(o)], ["center", new C(r)], ["rotation", new C(a)] ]) }); } get scale() { var t; return (t = this.uniforms.get("scale")) == null ? void 0 : t.value; } set scale(t) { this.uniforms.get("scale").value = t; } get noiseScale() { var t; return (t = this.uniforms.get("noiseScale")) == null ? void 0 : t.value; } set noiseScale(t) { this.uniforms.get("noiseScale").value = t; } get center() { var t; return (t = this.uniforms.get("center")) == null ? void 0 : t.value; } set center(t) { this.uniforms.get("center").value = Array.isArray(t) ? new f().fromArray(t) : t; } get rotation() { var t; return (t = this.uniforms.get("rotation")) == null ? void 0 : t.value; } set rotation(t) { this.uniforms.get("rotation").value = t; } } const fo = /* @__PURE__ */ u({ __name: "LinocutPmndrs", props: { blendFunction: {}, scale: {}, noiseScale: {}, center: {}, rotation: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h( () => new Ne({ ...e, center: e.center instanceof f ? [e.center.x, e.center.y] : e.center }), e ); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.scale, "scale"], [() => e.noiseScale, "noiseScale"], [() => e.center, "center"], [() => e.rotation, "rotation"] ], s, () => new Ne() ), () => { }; } }), ho = /* @__PURE__ */ u({ __name: "DepthPickingPassPmndrs", props: { depthPacking: {}, mode: {} }, setup(n, { expose: t }) { const e = n, o = J($), s = new et(e), a = w(() => { o != null && o.value && (ce(() => a()), o.value.addPass(s)); }); return W(() => { var r; !(o != null && o.value) || !s || ((r = o == null ? void 0 : o.value) == null || r.removePass(s), s.dispose()); }), t({ pass: s }), () => { }; } }), po = /* @__PURE__ */ u({ __name: "GodRaysPmndrs", props: { blendFunction: {}, lightSource: {}, opacity: {}, density: {}, decay: {}, kernelSize: {}, resolutionScale: {}, blur: { type: Boolean }, resolutionX: {}, resolutionY: {}, weight: {}, exposure: {}, samples: {}, clampMax: {} }, setup(n, { expose: t }) { const e = n, { camera: o } = y(), s = O( () => e.lightSource ?? new je( new ot(1e-5), new Ve({ visible: !1 }) ) ), { pass: a, effect: r } = h( () => new se(o.value, s.value, e), e ); return t({ pass: a, effect: r }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.density, "godRaysMaterial.density"], [() => e.decay, "godRaysMaterial.decay"], [() => e.weight, "godRaysMaterial.weight"], [() => e.exposure, "godRaysMaterial.exposure"], [() => e.samples, "godRaysMaterial.samples"], [() => e.clampMax, "godRaysMaterial.maxIntensity"], [() => e.resolutionScale, "resolution.scale"], [() => e.resolutionX, "resolution.width"], [() => e.resolutionY, "resolution.height"], [() => e.kernelSize, "blurPass.kernelSize"], [() => e.blur, "blurPass.enabled"] ], r, () => new se() ), g( [() => e.lightSource, r], () => { r.value && (r.value.lightSource = fe(s.value)); }, { immediate: !0 } ), g( [() => e.opacity], () => { var i, l; if (e.opacity !== void 0) (i = r.value) == null || i.blendMode.setOpacity(e.opacity); else { const d = new se( o.value, fe(s.value) ); (l = r.value) == null || l.blendMode.setOpacity(d.blendMode.getOpacity()), d.dispose(); } }, { immediate: !0 } ), () => { }; } }), mo = /* @__PURE__ */ u({ __name: "ColorDepthPmndrs", props: { blendFunction: {}, bits: {}, opacity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new V(e), e); return t({ pass: o, effect: s }), X( () => e.blendFunction, s, "blendMode.blendFunction", () => new V() ), g( [s, () => e.bits], () => { var a, r; if (s.value) if (e.bits !== void 0) (a = s.value) == null || a.setBitDepth(e.bits); else { const i = new V(); (r = s.value) == null || r.setBitDepth(i.getBitDepth()), i.dispose(); } } ), g( [s, () => e.opacity], () => { var a, r; if (s.value) if (e.opacity !== void 0) (a = s.value) == null || a.blendMode.setOpacity(e.opacity); else { const i = new V(); (r = s.value) == null || r.blendMode.setOpacity(i.blendMode.getOpacity()), i.dispose(); } } ), () => { }; } }), vo = /* @__PURE__ */ u({ __name: "GridPmndrs", props: { blendFunction: {}, scale: {}, lineWidth: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Ee(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.scale, "scale"], [() => e.lineWidth, "lineWidth"] ], s, () => new Ee() ), () => { }; } }); class ze extends Z { /** * Creates a new FishEyeEffect instance. * * @param {object} [options] - Configuration options for the effect. * @param {BlendFunction} [options.blendFunction] - Blend mode. * @param {Vector2} [options.lensS] - Lens scale. * @param {Vector2} [options.lensF] - Lens factor. * @param {number} [options.scale] - Scale. * */ constructor({ blendFunction: t = _.NORMAL, lensS: e = new f(1, 1), lensF: o = new f(0, 1), scale: s = 1 } = {}) { super("FishEyeEffect", ` uniform vec2 lensS; uniform vec2 lensF; uniform float scale; void mainUv(inout vec2 uv) { vec2 newUv = uv * 2.0 - 1.0; newUv.x = newUv.x + ((pow(newUv.y, 2.0) / scale) * newUv.x / scale) * -lensF.x; newUv.y = newUv.y + ((pow(newUv.x, 2.0) / scale) * newUv.y / scale) * -lensF.y; newUv = newUv * lensS; newUv = newUv / scale * 0.5 + 0.5; uv = newUv; } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { outputColor = vec4(inputColor.rgb, inputColor.a); // Preserves original alpha } `, { blendFunction: t, uniforms: /* @__PURE__ */ new Map([ ["lensS", new C(e)], ["lensF", new C(o)], ["scale", new C(s)] ]) }); } /** * The lensS. * * @type {Vector2} */ get lensS() { var t; return (t = this.uniforms.get("lensS")) == null ? void 0 : t.value; } set lensS(t) { this.uniforms.get("lensS").value = t; } /** * The lensF. * * @type {Vector2} */ get lensF() { var t; return (t = this.uniforms.get("lensF")) == null ? void 0 : t.value; } set lensF(t) { this.uniforms.get("lensF").value = t; } /** * The scale. * * @type {number} */ get scale() { var t; return (t = this.uniforms.get("scale")) == null ? void 0 : t.value; } set scale(t) { this.uniforms.get("scale").value = t; } } const go = /* @__PURE__ */ u({ __name: "FishEyePmndrs", props: { blendFunction: {}, lensS: {}, lensF: {}, scale: {} }, setup(n, { expose: t }) { const e = n, o = O( () => Array.isArray(e.lensS) ? new f(...e.lensS) : e.lensS ), s = O( () => Array.isArray(e.lensF) ? new f(...e.lensF) : e.lensF ), { pass: a, effect: r } = h( () => new ze({ ...e, lensS: o.value, lensF: s.value }), e ); return t({ pass: a, effect: r }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => o.value, "lensS"], [() => s.value, "lensF"], [() => e.scale, "scale"] ], r, () => new ze() ), () => { }; } }), xo = /* @__PURE__ */ u({ __name: "BrightnessContrastPmndrs", props: { blendFunction: {}, brightness: {}, contrast: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new Pe(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.brightness, "brightness"], [() => e.contrast, "contrast"] ], s, () => new Pe() ), () => { }; } }), le = { OFF: 0, EDGES: 1, WEIGHTS: 2 }, bo = /* @__PURE__ */ u({ __name: "SMAAPmndrs", props: { blendFunction: {}, opacity: {}, preset: {}, edgeDetectionMode: {}, predicationMode: {}, debug: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new ae(e), e), { camera: a } = y(), r = J($); t({ pass: o, effect: s }); const i = new ae(); v( [ [() => Number(e.blendFunction), "blendMode.blendFunction"], [() => Number(e.predicationMode), "edgeDetectionMaterial.predicationMode"], [() => Number(e.edgeDetectionMode), "edgeDetectionMaterial.edgeDetectionMode"] ], s, () => new ae() ), g( [s, () => e.opacity], () => { s.value && (e.opacity !== void 0 ? s.value.blendMode.setOpacity(e.opacity) : s.value.blendMode.setOpacity(i.blendMode.getOpacity())); }, { immediate: !0 } ), g( [s, () => e.preset], () => { s.value && s.value.applyPreset(Number(e.preset)); } ); let l = null, d = null; const p = (m) => { if (!s.value) return null; const A = m === "edges" ? s.value.edgesTexture : s.value.weightsTexture, E = new Xe(a.value, s.value, new q({ texture: A })); return E.renderToScreen = !1, E.enabled = !1, E.fullscreenMaterial.encodeOutput = !1, E; }, b = (m) => { m === "edges" && !l ? l = p("edges") : m === "weights" && !d && (d = p("weights")); }, c = (m, A) => { !m || !(r != null && r.value) || m.enabled !== A && (m.enabled = A, m.renderToScreen = A, A && !(r != null && r.value.passes.includes(m)) ? r == null || r.value.addPass(m) : !A && (r != null && r.value.passes.includes(m)) && (r == null || r.value.removePass(m), m.dispose())); }, S = (m) => { if (!o.value) return; const A = m === le.OFF, E = m === le.EDGES, x = m === le.WEIGHTS; o.value.enabled = A, o.value.renderToScreen = A, E && b("edges"), x && b("weights"), c(l, E), c(d, x), E || (l = null), x || (d = null); }; return g( () => e.debug, () => { !o.value || e.debug === void 0 || S(e.debug); }, { immediate: !0 } ), () => { }; } }), So = /* @__PURE__ */ u({ __name: "FXAAPmndrs", props: { blendFunction: {}, opacity: {}, samples: {}, minEdgeThreshold: {}, maxEdgeThreshold: {}, subpixelQuality: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new re(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.samples, "samples"], [() => e.minEdgeThreshold, "minEdgeThreshold"], [() => e.maxEdgeThreshold, "maxEdgeThreshold"], [() => e.subpixelQuality, "subpixelQuality"] ], s, () => new re() ), g( [s, () => e.opacity], () => { var a, r; if (s.value) if (e.opacity !== void 0) (a = s.value) == null || a.blendMode.setOpacity(e.opacity); else { const i = new re(); (r = s.value) == null || r.blendMode.setOpacity(i.blendMode.getOpacity()), i.dispose(); } }, { immediate: !0 } ), () => { }; } }), wo = /* @__PURE__ */ u({ __name: "TexturePmndrs", props: { blendFunction: {}, texture: {}, opacity: {} }, setup(n, { expose: t }) { const e = n, { pass: o, effect: s } = h(() => new q(e), e); return t({ pass: o, effect: s }), v( [ [() => e.blendFunction, "blendMode.blendFunction"] ], s, () => new q() ), g( [() => e.opacity], () => { var a, r; if (e.opacity !== void 0) (a = s.value) == null || a.blendMode.setOpacity(e.opacity); else { const i = new q(); (r = s.value) == null || r.blendMode.setOpacity(i.blendMode.getOpacity()), i.dispose(); } }, { immediate: !0 } ), () => { }; } }), Ao = /* @__PURE__ */ u({ __name: "ASCIIPmndrs", props: { blendFunction: {}, opacity: {}, cellSize: {}, inverted: { type: Boolean }, color: {}, useSceneColor: { type: Boolean }, asciiTexture: {} }, setup(n, { expose: t }) { const e = n, o = new ne(), { asciiTexture: s, ...a } = e, { pass: r, effect: i } = h(() => new ne(a), e); return t({ pass: r, effect: i }), W(() => { o.dispose(); }), v( [ [() => e.blendFunction, "blendMode.blendFunction"], [() => e.cellSize, "cellSize"], [() => e.inverted, "inverted"] ], i, () => new ne() ), g( [i, () => e.useSceneColor], () => { i.value && (e.useSceneColor ? i.value.color = null : i.value.color = e.color ?? o.color); }, { immediate: !0 } ), g( [i, () => e.opacity], () => { i.value && i.value.blendMode.setOpacity(e.opacity ?? o.blendMode.getOpacity()); }, { immediate: !0 } ), g( [i, () => e.color], () => { i.value && (e.useSceneColor || (i.value.color = e.color ?? null)); }, { immediate: !0 } ), g( [i, () => e.asciiTexture], () => { if (!i.value) return; const l = e.asciiTexture ? new tt(e.asciiTexture) : o.asciiTexture; i.value.asciiTexture = l; }, { immediate: !0 } ), g( [i, () => e.blendFunction], () => { i.value && (i.value.blendMode.blendFunction = e.blendFunction ? Number(e.blendFunction) : Number(o.blendMode.blendFunction)); }, { immediate: !0 } ), () => { }; } }), qe = { name: "CopyShader", uniforms: { tDiffuse: { value: null }, opacity: { value: 1 } }, vertexShader: ( /* glsl */ ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }` ), fragmentShader: ( /* glsl */ ` uniform float opacity; uniform sampler2D tDiffuse; varying vec2 vUv; void main() { vec4 texel = texture2D( tDiffuse, vUv ); gl_FragColor = opacity * texel; }` ) }; class D { constructor() { this.isPass = !0, this.enabled = !0, this.needsSwap = !0, this.clear = !1, this.renderToScreen = !1; } setSize() { } render() { console.error("THREE.Pass: .render() must be implemented in derived pass."); } dispose() { } } const Pt = new st(-1, 1, 1, -1, 0, 1); class Dt extends at { constructor() { super(), this.setAttribute("position", new De([-1, 3, 0, -1, -1, 0, 3, -1, 0], 3)), this.setAttribute("uv", new De([0, 2, 0, 0, 2, 0], 2)); } } const Lt = new Dt(); class R { constructor(t) { this._mesh = new je(Lt, t); } dispose() { this._mesh.geometry.dispose(); } render(t) { t.render(this._mesh, Pt); } get material() { return this._mesh.material; } set material(t) { this._mesh.material = t; } } class Ft extends D { constructor(t, e) { super(), this.textureID = e !== void 0 ? e : "tDiffuse", t instanceof T ? (this.uniforms = t.uniforms, this.material = t) : t && (this.uniforms = L.clone(t.uniforms), this.material = new T({ name: t.name !== void 0 ? t.name : "unspecified", defines: Object.assign({}, t.defines), uniforms: this.uniforms, vertexShader: t.vertexShader, fragmentShader: t.fragmentShader })), this.fsQuad = new R(this.material); } render(t, e, o) { this.uniforms[this.textureID] && (this.uniforms[this.textureID].value = o.texture), this.fsQuad.material = this.material, this.renderToScre