@threepipe/webgi-plugins
Version:
WebGi - Realistic Rendering Plugins for ThreePipe.
1 lines • 287 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../src/plugins/buffer/shaders/VelocityBufferPlugin.unpack.glsl","../src/plugins/buffer/shaders/VelocityBufferPlugin.mat.vert.glsl","../src/plugins/buffer/shaders/VelocityBufferPlugin.mat.frag.glsl","../src/plugins/postprocessing/shaders/temporalaa.glsl","../src/plugins/postprocessing/TemporalAAPlugin.ts","../src/plugins/buffer/VelocityBufferPlugin.ts","../src/plugins/postprocessing/shaders/hdrBloom.glsl","../src/plugins/postprocessing/BloomPlugin.ts","../src/plugins/postprocessing/shaders/dofCombine.glsl","../src/plugins/postprocessing/shaders/dofPoissonBox.glsl","../src/plugins/postprocessing/shaders/poissonDiskSamples.glsl","../src/plugins/postprocessing/shaders/dofComputeCoC.glsl","../src/plugins/postprocessing/shaders/dofExpandCoC.glsl","../src/plugins/postprocessing/DepthOfFieldPlugin.ts","../src/plugins/postprocessing/shaders/ssrt.glsl","../src/plugins/postprocessing/SSContactShadowsPlugin.ts","../src/plugins/postprocessing/shaders/samplePointHelpers.glsl","../src/plugins/postprocessing/shaders/ssreflection.glsl","../src/plugins/postprocessing/shaders/ssrPatch.glsl","../src/plugins/postprocessing/shaders/ssreflectionMain.glsl","../src/plugins/postprocessing/SSReflectionPlugin.ts","../src/plugins/postprocessing/shaders/outline.glsl","../src/plugins/postprocessing/shaders/outlineDepthVert.glsl","../src/plugins/postprocessing/shaders/outlineDepthFrag.glsl","../src/plugins/postprocessing/OutlinePlugin.ts","../src/plugins/postprocessing/shaders/giPatch.glsl","../src/plugins/postprocessing/shaders/ssrtao.glsl","../src/passes/shaders/ssaoBilateral.glsl","../src/passes/BilateralFilterPass.ts","../src/plugins/postprocessing/SSGIPlugin.ts","../src/plugins/extras/shaders/anisotropyBsdf.glsl","../src/plugins/extras/shaders/anisotropyTBN.glsl","../src/plugins/extras/AnisotropyPlugin.ts","../src/plugins/index.ts"],"sourcesContent":["#if defined(HAS_VELOCITY_BUFFER)\nuniform sampler2D tVelocity;\nvec2 getVelocity(const in vec2 uv) {\n vec2 screenSpaceVelocity = texture2D(tVelocity, uv).xy * 2.0 - 1.0;\n return sign(screenSpaceVelocity) * pow(abs(screenSpaceVelocity), vec2(4.));\n}\n#endif\n\n","#ifdef USE_ALPHAMAP\n#define USE_UV\n#endif\n#include <uv_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\n\n//varying vec3 vViewPosition;\n\nvarying vec3 vWorldPosition;\nvarying vec3 vWorldPositionPrevious;\n\nuniform mat4 modelMatrixPrevious;\n\nvoid main() {\n\n #include <uv_vertex>\n #include <skinbase_vertex>\n\n #include <begin_vertex>\n #include <morphtarget_vertex>\n #include <skinning_vertex>\n #include <displacementmap_vertex>\n\n // project_vertex\n\n vec4 mvPosition = vec4( transformed, 1.0 );\n\n #ifdef USE_INSTANCING\n\n mvPosition = instanceMatrix * mvPosition;\n\n #endif\n\n vWorldPosition = (modelMatrix * mvPosition).xyz;\n vWorldPositionPrevious = (modelMatrixPrevious * mvPosition).xyz;\n\n mvPosition = modelViewMatrix * mvPosition;\n\n gl_Position = projectionMatrix * mvPosition;\n\n #include <logdepthbuf_vertex>\n #include <clipping_planes_vertex>\n\n // vViewPosition = - mvPosition.xyz;\n\n}\n","varying vec3 vWorldPosition;\nvarying vec3 vWorldPositionPrevious;\nuniform mat4 currentProjectionViewMatrix;\nuniform mat4 lastProjectionViewMatrix;\n\nvec2 computeScreenSpaceVelocity2() {\n vec4 currentPositionClip = currentProjectionViewMatrix * vec4(vWorldPosition, 1.0);\n vec4 prevPositionClip = lastProjectionViewMatrix * vec4(vWorldPositionPrevious, 1.0);\n\n vec2 currentPositionNDC = currentPositionClip.xy / currentPositionClip.w;\n vec2 prevPositionNDC = prevPositionClip.xy / prevPositionClip.w;\n\n if(prevPositionNDC.x >= 1.0 || prevPositionNDC.x <= -1.0 || prevPositionNDC.x >= 1.0 || prevPositionNDC.y <= -1.0) {\n return vec2(0.0);\n }\n return 0.5 * (currentPositionNDC - prevPositionNDC);\n}\n\nvoid main() {\n vec2 velocity = clamp(computeScreenSpaceVelocity2(), -1.0, 1.0);\n velocity = sign(velocity) * pow(abs(velocity), vec2(1./4.));\n velocity = velocity * 0.5 + 0.5;\n gl_FragColor = vec4(velocity.x, velocity.y, 1., 1.);\n\n // float speed = length(computeScreenSpaceVelocity2());\n // gl_FragColor = vec4(speed, speed, speed, 1.);\n}\n","#include <common>\n#include <gbuffer_unpack>\n#ifdef HAS_VELOCITY_BUFFER\n#pragma <velocity_unpack>\n#endif\n#include <cameraHelpers>\n\nvarying vec2 vUv;\nuniform vec2 previousRTSize;\nuniform mat4 lastProjectionViewMatrix;\nuniform mat4 currentProjectionViewMatrix;\nuniform mat4 inverseViewMatrix;\nuniform vec2 jitterSample;\nuniform vec2 feedBack;\nuniform bool firstFrame;\n\n//float getViewZ( const in float depth ) {\n//\t#if PERSPECTIVE_CAMERA == 1\n//\treturn perspectiveDepthToViewZ( depth, cameraNearFar.x, cameraNearFar.y );\n//\t#else\n//\treturn orthoDepthToViewZ( depth, cameraNearFar.x, cameraNearFar.y );\n//\t#endif\n//}\n\n#ifndef HAS_VELOCITY_BUFFER\n#define HAS_VELOCITY_BUFFER 0\n#endif\n\nvec3 find_closest_fragment_3x3(const in vec2 uv) {\n const vec3 offset = vec3(-1.0, 1.0, 0.0);\n vec2 texelSize = 1.0/previousRTSize;\n\n vec3 dtr = vec3( 1, 1, getDepth( uv + offset.yy * texelSize) );\n vec3 dtc = vec3( 0, 1, getDepth( uv + offset.zy * texelSize) );\n vec3 dtl = vec3( -1, 1, getDepth( uv + offset.xy * texelSize) );\n\n vec3 dml = vec3(-1, 0, getDepth( uv + offset.xz * texelSize) );\n vec3 dmc = vec3( 0, 0, getDepth( uv ) );\n vec3 dmr = vec3( 1, 0, getDepth( uv + offset.yz * texelSize) );\n\n vec3 dbl = vec3(-1, -1, getDepth( uv + offset.xx * texelSize) );\n vec3 dbc = vec3( 0, -1, getDepth( uv + offset.zx * texelSize) );\n vec3 dbr = vec3( 1, -1, getDepth( uv + offset.yx * texelSize) );\n\n vec3 dmin = dtl;\n if ( dmin.z > dtc.z ) dmin = dtc;\n if ( dmin.z > dtr.z ) dmin = dtr;\n\n if ( dmin.z > dml.z ) dmin = dml;\n if ( dmin.z > dmc.z ) dmin = dmc;\n if ( dmin.z > dmr.z ) dmin = dmr;\n\n if ( dmin.z > dbl.z ) dmin = dbl;\n if ( dmin.z > dbc.z ) dmin = dbc;\n if ( dmin.z > dbr.z ) dmin = dbr;\n\n return vec3(uv + texelSize.xy * dmin.xy, dmin.z);\n}\n\nvec3 find_closest_fragment_5tap(const in vec2 uv)\n{\n vec2 texelSize = 1.0/previousRTSize;\n vec2 offset = vec2(1.0, -1.0);\n\n vec3 dtl = vec3(-1, 1, getDepth( uv + offset.yx * texelSize) );\n vec3 dtr = vec3( 1, 1, getDepth( uv + offset.xx * texelSize) );\n\n vec3 dmc = vec3( 0, 0, getDepth( uv) );\n\n vec3 dbl = vec3(-1, -1, getDepth( uv + offset.yy * texelSize) );\n vec3 dbr = vec3( 1, -1, getDepth( uv + offset.xy * texelSize) );\n\n vec3 dmin = dtl;\n if ( dmin.z > dtr.z ) dmin = dtr;\n if ( dmin.z > dmc.z ) dmin = dmc;\n\n if ( dmin.z > dbl.z ) dmin = dbl;\n if ( dmin.z > dbr.z ) dmin = dbr;\n\n return vec3(uv + dmin.xy * texelSize, dmin.z);\n}\n\nvec4 clip_aabb(const in vec4 aabb_min, const in vec4 aabb_max, vec4 p )\n{\n const float FLT_EPS = 1e-8;\n vec4 p_clip = 0.5 * (aabb_max + aabb_min);\n vec4 e_clip = 0.5 * (aabb_max - aabb_min) + FLT_EPS;\n\n vec4 v_clip = p - p_clip;\n vec4 v_unit = abs(v_clip / e_clip);\n float ma_unit = max(v_unit.x, max(v_unit.y, v_unit.z));\n\n if (ma_unit > 1.0)\n return p_clip + v_clip / ma_unit;\n else return p;\n}\n\n#if HAS_VELOCITY_BUFFER == 0\nvec2 computeScreenSpaceVelocity(const in vec3 worldPosition) {\n vec4 currentPositionClip = currentProjectionViewMatrix * vec4(worldPosition, 1.0);\n vec4 prevPositionClip = lastProjectionViewMatrix * vec4(worldPosition, 1.0);\n\n vec2 currentPositionNDC = currentPositionClip.xy / currentPositionClip.w;\n vec2 prevPositionNDC = prevPositionClip.xy / prevPositionClip.w;\n\n if(prevPositionNDC.x >= 1.0 || prevPositionNDC.x <= -1.0 || prevPositionNDC.x >= 1.0 || prevPositionNDC.y <= -1.0) {\n return vec2(0.0);\n }\n return 0.5 * (currentPositionNDC - prevPositionNDC);\n}\n#endif\nvec4 currentRTTexelToLinear1(vec4 a){\n if(isinf(a.x) || isinf(a.y) || isinf(a.z) || isinf(a.w)){\n return vec4(1.);\n }\n return currentRTTexelToLinear(a);\n}\nvec4 computeTAA(const in vec2 uv, const in vec2 screenSpaceVelocity, const in float feedbackScale) {\n// vec2 jitterOffset = jitterSample/previousRTSize; // todo\n vec2 uvUnJitter = uv;\n\n vec4 currentColor = currentRTTexelToLinear(texture2D(currentRT, uvUnJitter));\n vec4 previousColor = previousRTTexelToLinear(texture2D(previousRT, uv - screenSpaceVelocity));\n const vec3 offset = vec3(1., -1., 0.);\n vec2 texelSize = 1./previousRTSize;\n\n float texelSpeed = length( screenSpaceVelocity );\n\n // todo pick only the neighbors which are not background?\n vec4 tl = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.yx * texelSize));\n vec4 tc = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.zx * texelSize));\n vec4 tr = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.xx * texelSize));\n vec4 ml = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.yz * texelSize));\n vec4 mc = currentColor;\n vec4 mr = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.xz * texelSize));\n vec4 bl = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.yy * texelSize));\n vec4 bc = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.zy * texelSize));\n vec4 br = currentRTTexelToLinear1(texture2D(currentRT, uvUnJitter + offset.xy * texelSize));\n\n vec4 corners = 2.0 * (tr + bl + br + tl) - 2.0 * mc;\n mc += (mc - (corners * 0.166667)) * 2.718282 * 0.3;\n mc = max(vec4(0.0), mc);\n\n vec4 min5 = min(tc, min(ml, min(mc, min(mr, bc))));\n vec4 max5 = max(tc, max(ml, max(mc, max(mr, bc))));\n\n vec4 cmin = min(min5, min(tl, min(tr, min(bl, br))));\n vec4 cmax = max(min5, max(tl, max(tr, max(bl, br))));;\n\n cmin = 0.5 * (cmin + min5);\n cmax = 0.5 * (cmax + max5);\n previousColor = clip_aabb(cmin, cmax, previousColor);\n\n float lum0 = luminance(currentColor.rgb);\n float lum1 = luminance(previousColor.rgb);\n float unbiased_diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2));\n float unbiased_weight = 1.0 - unbiased_diff;\n float unbiased_weight_sqr = unbiased_weight * unbiased_weight;\n float k_feedback = mix(feedBack.x, feedBack.y, unbiased_weight_sqr);\n\n return mix(currentColor, previousColor, clamp(k_feedback * feedbackScale, 0., 1.));\n\n}\n\nvec3 getWorldPositionFromViewZ(const in vec2 uv, const in float viewDepth) {\n vec2 uv_ = 2. * uv - 1.;\n float xe = -(uv_.x + projection[2][0]) * viewDepth/projection[0][0];\n float ye = -(uv_.y + projection[2][1]) * viewDepth/projection[1][1];\n return (inverseViewMatrix * vec4(xe, ye, viewDepth, 1.)).xyz;\n}\n\nvoid main() {\n\n // gl_FragColor.rgb = vec3(getDepth( vUv ));\n // gl_FragColor.a = 1.;\n // return;\n // vec2 jitterOffset = jitterSample/previousRTSize;\n\n #if HAS_VELOCITY_BUFFER == 0 // todo why not using closest fragment in velocity buffer?\n #if QUALITY == 1\n vec3 c_frag = find_closest_fragment_3x3(vUv);\n #else\n vec3 c_frag = find_closest_fragment_5tap(vUv);\n #endif\n #else\n vec3 c_frag = vec3(vUv, 0.);\n #endif\n\n bool bg = firstFrame;\n\n #if BACKGROUND_TAA // this is required for edge artifacts in msaa\n\n// float d = getDepth(vUv);\n float d = c_frag.z;\n float edgef = min(1., max(0., 1.- (d*100. - 99.)));\n\n #else\n\n bg = bg || c_frag.z > 0.999;\n\n #endif\n\n if( firstFrame ) {\n\n gl_FragColor = currentRTTexelToLinear1(texture2D(currentRT, vUv));\n\n }\n else {\n #if HAS_VELOCITY_BUFFER == 0\n // #if LINEAR_DEPTH == 0\n // float sampleViewZ = getViewZ( c_frag.z );\n // #else\n float sampleViewZ = mix(-cameraNearFar.x, -cameraNearFar.y, c_frag.z);\n // #endif\n vec3 worldPosition = getWorldPositionFromViewZ(c_frag.xy, sampleViewZ);\n vec2 screenSpaceVelocity = computeScreenSpaceVelocity(worldPosition);\n #else\n vec2 screenSpaceVelocity = getVelocity(c_frag.xy);\n #endif\n\n// float previousDepth = getDepth(vUv - screenSpaceVelocity);\n\n// screenSpaceVelocity *= min(1., edgef);\n// screenSpaceVelocity *= (d >= 0.99) ? 0. : 1.;\n// screenSpaceVelocity *= abs(d-previousDepth) > 0.01 ? 0. : 1.;\n// screenSpaceVelocity *= 0.;\n\n // todo add velocity scale also\n #if BACKGROUND_TAA\n gl_FragColor = computeTAA(vUv, screenSpaceVelocity * edgef, edgef);\n #else\n gl_FragColor = computeTAA(vUv, screenSpaceVelocity, 1.);\n #endif\n\n// gl_FragColor = firstFrame /* || previousDepth > 0.999*/ ? currentRTTexelToLinear1(texture2D(currentRT, vUv)) : computeTAA(vUv, screenSpaceVelocity, edgef);\n// gl_FragColor = vec4(1. - max(0., (d - 0.9) * 10.0), 0., 0., 1.0);\n// gl_FragColor = vec4(10. * length(screenSpaceVelocity));\n// gl_FragColor = vec4(abs(d-previousDepth) > 0.1, 0., 0., 1.);\n// gl_FragColor = vec4(abs(d-previousDepth) > 0.1, 0., 0., 1.);\n\n }\n\n #include <colorspace_fragment>\n\n #ifdef DEBUG_VELOCITY\n float sampleViewZ = mix(-cameraNearFar.x, -cameraNearFar.y, c_frag.z);\n vec3 worldPosition = getWorldPositionFromViewZ(c_frag.xy, sampleViewZ);\n vec2 screenSpaceVelocity = computeScreenSpaceVelocity(worldPosition);\n// screenSpaceVelocity *= min(1., edgef);\n gl_FragColor = vec4(10. * length(screenSpaceVelocity), 0., 0., 1.);\n #endif\n\n}\n","import {\n Camera,\n CopyShader,\n ExtendedShaderPass,\n GBufferPlugin,\n generateUiConfig, getOrCall,\n ICamera,\n IPassID,\n IPipelinePass,\n IRenderManager,\n IScene,\n IWebGLRenderer,\n matDefineBool,\n MaterialExtension,\n Matrix4,\n PipelinePassPlugin,\n ProgressivePlugin,\n serialize,\n ThreeViewer,\n uiConfig,\n UiObjectConfig,\n uiToggle,\n uiVector,\n uniform, ValOrFunc,\n Vector2,\n WebGLMultipleRenderTargets,\n WebGLRenderTarget,\n} from 'threepipe'\nimport taaShader from './shaders/temporalaa.glsl'\n\nconst passId = 'taa'\ntype TemporalAAPassId = typeof passId\n\n/**\n * Temporal Anti-Aliasing Plugin\n *\n * This plugin uses a temporal anti-aliasing pass to smooth out the final image when the camera or some mesh is moving\n * @category Plugins\n */\nexport class TemporalAAPlugin\n extends PipelinePassPlugin<TemporalAAPluginPass<TemporalAAPassId>, TemporalAAPassId> {\n static readonly PluginType = 'TAA'\n static readonly OldPluginType = 'TemporalAAPlugin' // todo swap\n readonly passId = passId\n\n // readonly materialExtension: MaterialExtension = uiConfigMaterialExtension(this._getUiConfig.bind(this), TemporalAAPlugin.PluginType)\n\n constructor(enabled = true) {\n super()\n this.enabled = enabled\n this.setDirty = this.setDirty.bind(this)\n }\n\n\n private _stableNoise = true\n /**\n * Same as BaseRenderer.stableNoise Use total frame count, if this is set to true, then frameCount won't be reset when the viewer is set to dirty.\n * Which will generate different random numbers for each frame during postprocessing steps. With TAA set properly, this will give a smoother result.\n */\n @uiToggle('Stable Noise (Total frame count)')\n get stableNoise(): boolean {\n return this._viewer?.renderManager.stableNoise ?? this._stableNoise\n }\n set stableNoise(v: boolean) {\n if (this._viewer) this._viewer.renderManager.stableNoise = v\n this._stableNoise = v\n }\n\n @uiConfig() declare protected _pass?: TemporalAAPluginPass<TemporalAAPassId>\n\n private _gbufferUnpackExtension = undefined as MaterialExtension|undefined\n private _gbufferUnpackExtensionChanged = ()=>{\n if (!this._pass || !this._viewer) throw new Error('TemporalAAPlugin: pass/viewer not created yet')\n const newExtension = this._viewer.renderManager.gbufferUnpackExtension\n if (this._gbufferUnpackExtension === newExtension) return\n if (this._gbufferUnpackExtension) this._pass.material.unregisterMaterialExtensions([this._gbufferUnpackExtension])\n this._gbufferUnpackExtension = newExtension\n if (this._gbufferUnpackExtension) this._pass.material.registerMaterialExtensions([this._gbufferUnpackExtension])\n else this._viewer.console.warn('TemporalAAPlugin: GBuffer unpack extension removed')\n }\n\n protected _createPass() {\n if (!this._viewer) throw new Error('TemporalAAPlugin: viewer not set')\n if (!this._viewer.renderManager.gbufferTarget || !this._viewer.renderManager.gbufferUnpackExtension)\n throw new Error('TemporalAAPlugin: GBuffer target not created. GBufferPlugin is required.')\n const applyOnBackground = !!this._viewer.renderManager.msaa\n const t = new TemporalAAPluginPass(this.passId, ()=>this._viewer?.getPlugin(ProgressivePlugin)?.target, applyOnBackground)\n return t\n }\n\n dependencies = [GBufferPlugin, ProgressivePlugin] // todo use gbufferUnpackExtension from render manager to support depth buffer plugin as well.\n\n onAdded(viewer: ThreeViewer) {\n super.onAdded(viewer)\n this._gbufferUnpackExtensionChanged()\n viewer.renderManager.addEventListener('gbufferUnpackExtensionChanged', this._gbufferUnpackExtensionChanged)\n viewer.renderManager.addEventListener('resize', this._pass!.onSizeUpdate)\n // viewer.materialManager.registerMaterialExtension(this.materialExtension)\n }\n\n onRemove(viewer: ThreeViewer) {\n viewer.renderManager.removeEventListener('gbufferUnpackExtensionChanged', this._gbufferUnpackExtensionChanged)\n viewer.renderManager.removeEventListener('resize', this._pass!.onSizeUpdate)\n super.onRemove(viewer)\n // viewer.materialManager.unregisterMaterialExtension(this.materialExtension)\n }\n\n uiConfig: UiObjectConfig = {\n type: 'folder',\n label: 'TemporalAA Plugin',\n onChange: this.setDirty.bind(this),\n children: [\n ...generateUiConfig(this) || [],\n ],\n }\n\n protected _beforeRender(scene: IScene, camera: ICamera, renderManager: IRenderManager): boolean {\n if (!super._beforeRender(scene, camera, renderManager)) return false\n const pass = this.pass\n const v = this._viewer\n if (!pass || !v) return false\n\n const frame = renderManager.frameCount\n pass.taaEnabled = frame <= 1 && scene.renderCamera === scene.mainCamera\n if (!pass.taaEnabled) return false\n\n const cam = camera\n if (!cam) return false\n\n cam.updateMatrixWorld(true)\n\n cam.updateShaderProperties(pass.material) // for cameraNearFar\n\n pass.updateCameraProperties(cam)\n\n pass.target = v.getPlugin(ProgressivePlugin)!.target as any\n return true\n }\n\n // region to be done or removed\n\n // static AddTemporalAAData(material: IMaterial, params?: IMaterialUserData['TemporalAA'], setDirty = true): IMaterialUserData['TemporalAA']|null {\n // const ud = material?.userData\n // if (!ud) return null\n // if (!ud[TemporalAAPlugin.PluginType]) {\n // ud[TemporalAAPlugin.PluginType] = {}\n // }\n // const data = ud[TemporalAAPlugin.PluginType]!\n // data.enable = true\n // params && Object.assign(data, params)\n // if (setDirty && material.setDirty) material.setDirty()\n // return data\n // }\n\n /**\n * This uiConfig is added to each material by extension\n * @param material\n * @private\n */\n // private _getUiConfig(material: IMaterial) {\n // const config: UiObjectConfig = {\n // type: 'folder',\n // label: 'TemporalAA',\n // children: [\n // {\n // type: 'checkbox',\n // label: 'Enabled',\n // get value() {\n // return material.userData[TemporalAAPlugin.PluginType]?.enable ?? true\n // },\n // set value(v) {\n // let data = material.userData[TemporalAAPlugin.PluginType]\n // if (v === data?.enable) return\n // if (!data) data = TemporalAAPlugin.AddTemporalAAData(material, undefined, false)!\n // data.enable = v\n // material.setDirty()\n // config.uiRefresh?.(true, 'postFrame')\n // },\n // onChange: this.setDirty,\n // },\n // ],\n // }\n // return config\n // }\n\n // endregion\n}\n\nexport class TemporalAAPluginPass<Tid extends IPassID> extends ExtendedShaderPass implements IPipelinePass<Tid> {\n before = ['progressive']\n after = [] // leave empty so that this is placed right before progressive\n required = ['render', 'progressive']\n\n public target: ValOrFunc<WebGLRenderTarget|undefined>\n public readonly passId: Tid\n\n constructor(pid: Tid, target: ValOrFunc<WebGLRenderTarget|undefined>, applyOnBackground = true) {\n super({\n vertexShader: CopyShader.vertexShader,\n fragmentShader: taaShader,\n uniforms: {\n currentRT: {value: null},\n previousRT: {value: null},\n previousRTSize: {value: new Vector2()},\n cameraNearFar: {value: new Vector2()},\n lastProjectionViewMatrix: {value: new Matrix4()},\n currentProjectionViewMatrix: {value: new Matrix4()},\n projection: {value: new Matrix4()},\n inverseViewMatrix: {value: new Matrix4()},\n jitterSample: {value: new Vector2()},\n firstFrame: {value: true},\n },\n defines: {\n ['QUALITY']: 1,\n ['UNJITTER']: 0,\n ['BACKGROUND_TAA']: applyOnBackground ? 1 : 0,\n },\n\n }, 'currentRT', 'previousRT')\n this.passId = pid\n this.onSizeUpdate = this.onSizeUpdate.bind(this)\n this.target = target\n this.clear = false\n this.needsSwap = true\n\n }\n\n /**\n * to switch with ssaa, for internal use only, dont set from outside\n */\n taaEnabled = true\n\n render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, readBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) {\n if (!this.taaEnabled || !this.enabled) {\n this.needsSwap = false\n return\n }\n this.needsSwap = true\n\n this.uniforms.previousRT.value = getOrCall(this.target)?.texture ?? null\n\n super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)\n\n // ;(renderer as any).baseRenderer.blit(writeBuffer.texture, this.target, {clear: false}) // this is done in the progressive plugin\n\n this.uniforms.lastProjectionViewMatrix.value.copy(this.uniforms.currentProjectionViewMatrix.value)\n\n this.uniforms.firstFrame.value = false\n\n }\n\n\n updateCameraProperties(camera?: Camera): void {\n if (!camera) return\n this.uniforms.currentProjectionViewMatrix.value.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)\n // this.uniforms.lastProjectionViewMatrix.value.copy(this.lastProjectionViewMatrix_)\n // this.uniforms.ProjectionMatrix.value.copy(this.projectionMatrix_)\n this.uniforms.inverseViewMatrix.value.copy(camera.matrixWorld)\n }\n onSizeUpdate() {\n this.uniforms.firstFrame.value = true\n this.setDirty()\n }\n setSize(width: number, height: number) {\n super.setSize(width, height)\n this.onSizeUpdate()\n }\n\n @serialize() @uniform()\n @uiVector('Feedback', undefined, 0.0001)\n feedBack: Vector2 = new Vector2(0.88, 0.97)\n\n @uiToggle()\n @matDefineBool('DEBUG_VELOCITY', undefined, false, undefined, true)\n debugVelocity = false\n\n uiConfig: UiObjectConfig = {\n type: 'folder',\n label: 'Temporal AA Pass',\n onChange: this.setDirty,\n children: [\n {\n type: 'checkbox',\n label: 'Enabled',\n property: [this, 'enabled'],\n onChange: ()=>this.onSizeUpdate(),\n },\n ...generateUiConfig(this)?.filter(c=>c && (c as any).label !== 'Enabled') || [],\n ],\n }\n\n}\n\ndeclare module 'threepipe' {\n // interface IMaterialUserData {\n // [TemporalAAPlugin.PluginType]?: {\n // enable?: boolean\n // }\n // }\n}\n","import {\n BufferGeometry,\n Camera,\n Color,\n DoubleSide,\n GBufferRenderPass,\n ICamera,\n IObject3D,\n IRenderManager,\n IScene,\n IUniform,\n IWebGLRenderer,\n LinearSRGBColorSpace,\n MaterialExtension,\n Matrix3,\n Matrix4,\n NoBlending,\n PipelinePassPlugin,\n Scene,\n ShaderMaterial,\n shaderReplaceString,\n Texture,\n TextureDataType,\n ThreeViewer,\n uiFolderContainer,\n uiImage,\n UnsignedByteType,\n Vector2,\n WebGLMultipleRenderTargets,\n WebGLRenderer,\n WebGLRenderTarget,\n} from 'threepipe'\nimport VelocityBufferUnpack from './shaders/VelocityBufferPlugin.unpack.glsl'\nimport ssVelocityVert from './shaders/VelocityBufferPlugin.mat.vert.glsl'\nimport ssVelocityFrag from './shaders/VelocityBufferPlugin.mat.frag.glsl'\nimport {TemporalAAPlugin} from '../postprocessing/TemporalAAPlugin'\n\n// type VelocityBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget\nexport type VelocityBufferPluginTarget = WebGLRenderTarget\nexport type VelocityBufferPluginPass = GBufferRenderPass<'velocityBuffer', VelocityBufferPluginTarget>\n/**\n * Velocity Buffer Plugin\n *\n * Adds a pre-render pass to render the normal buffer to a render target that can be used for postprocessing.\n * @category Plugins\n */\n@uiFolderContainer('Velocity Buffer Plugin (for TAA)')\nexport class VelocityBufferPlugin\n extends PipelinePassPlugin<VelocityBufferPluginPass, 'velocityBuffer'> {\n\n readonly passId = 'velocityBuffer'\n public static readonly PluginType = 'VelocityBuffer'\n public static readonly OldPluginType = 'VelocityBufferPlugin' // todo swap\n\n target?: VelocityBufferPluginTarget\n @uiImage('Velocity Buffer' /* {readOnly: true}*/) texture?: Texture\n readonly material: SSVelocityMaterial = new SSVelocityMaterial()\n\n // @onChange2(VelocityBufferPlugin.prototype._createTarget)\n // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)\n readonly bufferType: TextureDataType // cannot be changed after creation (for now)\n\n protected _createTarget(recreate = true) {\n if (!this._viewer) return\n if (recreate) this._disposeTarget()\n\n if (!this.target) this.target = this._viewer.renderManager.createTarget<VelocityBufferPluginTarget>(\n {\n depthBuffer: true,\n // samples: v.renderManager.composerTarget.samples || 0,\n samples: 0,\n type: this.bufferType,\n // magFilter: NearestFilter,\n // minFilter: NearestFilter,\n generateMipmaps: false,\n colorSpace: LinearSRGBColorSpace,\n })\n this.texture = this.target.texture\n this.texture.name = 'velocityBuffer'\n\n if (this._pass) this._pass.target = this.target\n }\n protected _disposeTarget() {\n if (!this._viewer) return\n if (this.target) {\n this._viewer.renderManager.disposeTarget(this.target)\n this.target = undefined\n }\n this.texture = undefined\n }\n\n protected _createPass() {\n this._createTarget(true)\n if (!this.target) throw new Error('VelocityBufferPlugin: target not created')\n this.material.userData.isGBufferMaterial = true\n const v = this._viewer!\n const pass = new class extends GBufferRenderPass {\n private _firstCall = true\n render(renderer: IWebGLRenderer, writeBuffer?: WebGLRenderTarget | WebGLMultipleRenderTargets | null, readBuffer?: WebGLRenderTarget | WebGLMultipleRenderTargets, deltaTime?: number, maskActive?: boolean) {\n if (v.renderManager.frameCount > 0) return\n if (!this.enabled || !this.camera) return\n const mat = this.overrideMaterial as ShaderMaterial\n mat.uniforms.currentProjectionViewMatrix.value.copy(this.camera.projectionMatrix).multiply(this.camera.matrixWorldInverse)\n if (this._firstCall) {\n mat.uniforms.lastProjectionViewMatrix.value.copy(mat.uniforms.currentProjectionViewMatrix.value)\n this._firstCall = false\n }\n super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)\n mat.uniforms.lastProjectionViewMatrix.value.copy(mat.uniforms.currentProjectionViewMatrix.value)\n }\n }(this.passId, this.target, this.material, new Color(0.5, 0.5, 0.5), 1) // clear color 0.5 means 0 velocity\n const preprocessMaterial = pass.preprocessMaterial\n pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData[VelocityBufferPlugin.PluginType]?.disabled)\n pass.before = ['render']\n pass.after = []\n pass.required = ['render']\n return pass as any\n }\n\n // automatically register the unpack extension with TAA plugin\n protected readonly _attachToTaa: boolean\n\n constructor(\n bufferType: TextureDataType = UnsignedByteType,\n enabled = true,\n _attachToTaa = true\n ) {\n super()\n this.enabled = enabled\n this.bufferType = bufferType\n this._attachToTaa = _attachToTaa\n }\n\n onAdded(viewer: ThreeViewer) {\n super.onAdded(viewer)\n if (this._attachToTaa) {\n const taa = viewer.getPlugin(TemporalAAPlugin)\n if (taa) taa.pass?.material.registerMaterialExtensions([this.unpackExtension])\n else viewer.console.warn('VelocityBufferPlugin: TemporalAAPlugin not found, set attachToTaa to false if not required')\n }\n }\n\n onRemove(viewer: ThreeViewer): void {\n this._disposeTarget()\n if (this._attachToTaa) viewer.getPlugin(TemporalAAPlugin)?.pass?.material.unregisterMaterialExtensions([this.unpackExtension])\n return super.onRemove(viewer)\n }\n\n unpackExtension: MaterialExtension = {\n shaderExtender: (shader)=>{\n if(this.isDisabled()) return\n shader.fragmentShader = shaderReplaceString(shader.fragmentShader,\n '#pragma <velocity_unpack>',\n '\\n' + VelocityBufferUnpack + '\\n')\n },\n computeCacheKey: ()=>this.isDisabled() ? '' : 'vb',\n extraUniforms: {\n tVelocity: ()=>({value: !this.isDisabled() ? this.target?.texture:null}),\n },\n extraDefines: {\n ['HAS_VELOCITY_BUFFER']: ()=>!this.isDisabled() && this.target?.texture ? 1 : undefined,\n },\n priority: 100,\n isCompatible: () => true,\n }\n\n setDirty() {\n super.setDirty();\n this.unpackExtension.setDirty?.()\n }\n\n protected _beforeRender(scene: IScene, camera: ICamera, renderManager: IRenderManager): boolean {\n if (!super._beforeRender(scene, camera, renderManager)) return false\n const pass = this.pass\n if (!pass) return false\n if (renderManager.frameCount > 0) return false\n pass.scene = scene\n pass.camera = camera\n camera.updateShaderProperties(pass.overrideMaterial as ShaderMaterial)\n return true\n }\n\n}\n\n\ndeclare module 'threepipe' {\n interface IMaterialUserData {\n [VelocityBufferPlugin.PluginType]?: {\n /**\n * Disables rendering to the velocity buffer.\n */\n disabled?: boolean\n }\n }\n}\nexport class SSVelocityMaterial extends ShaderMaterial {\n\n constructor() {\n super({\n vertexShader: ssVelocityVert,\n fragmentShader: ssVelocityFrag,\n uniforms: {\n cameraNearFar: {value: new Vector2(0.1, 1000)},\n alphaMap: {value: null},\n alphaTest: {value: null},\n alphaMapTransform: {value: /* @__PURE__*/ new Matrix3()},\n currentProjectionViewMatrix: {value: new Matrix4()},\n lastProjectionViewMatrix: {value: new Matrix4()},\n },\n blending: NoBlending, // todo?\n })\n }\n\n extraUniformsToUpload: Record<string, IUniform> = {\n modelMatrixPrevious: {value: new Matrix4().identity()},\n }\n\n private _previousWorldMatrices: Record<string, Matrix4> = {}\n\n // this gets called for each object.\n onBeforeRender(_r: WebGLRenderer, _s: Scene, _c: Camera, _geometry: BufferGeometry, object: IObject3D) {\n const prevMatrix = this._previousWorldMatrices[object.uuid]\n this.extraUniformsToUpload.modelMatrixPrevious.value.copy(prevMatrix ?? object.matrixWorld)\n\n // todo: make sure all objects are only rendered once.\n if (prevMatrix) {\n prevMatrix.copy(object.matrixWorld)\n } else {\n this._previousWorldMatrices[object.uuid] = object.matrixWorld.clone()\n }\n\n // todo: add support for all this in the shaders.\n let mat = object.material\n\n if (Array.isArray(mat)) { // todo: add support for multi materials.\n mat = mat[0]\n }\n this.uniforms.alphaMap.value = mat?.alphaMap ?? null\n this.uniforms.alphaTest.value = !mat || !mat.alphaTest || mat.alphaTest < 0.0000001 ? 0.001 : mat.alphaTest\n\n let x = this.uniforms.alphaMap.value ? 1 : undefined\n if (x !== this.defines.USE_ALPHAMAP) {\n if (x === undefined) delete this.defines.USE_ALPHAMAP\n else this.defines.USE_ALPHAMAP = x\n this.needsUpdate = true\n }\n x = mat?.userData.ALPHA_I_RGBA_PACKING ? 1 : undefined\n if (x !== this.defines.ALPHA_I_RGBA_PACKING) {\n if (x === undefined) delete this.defines.ALPHA_I_RGBA_PACKING\n else this.defines.ALPHA_I_RGBA_PACKING = x\n this.needsUpdate = true\n }\n\n this.side = mat?.side ?? DoubleSide\n }\n}\n","#include <packing>\n// todo use this after threepipe update\n// include <gbuffer_unpack>\n\nuniform float intensity;\nuniform float opacity;\nuniform vec2 tDiffuseSize;\nvarying vec2 vUv;\nuniform float weight;\n\n#if PASS_STEP == 0\nuniform vec4 prefilter;\nvec4 Prefilter (vec4 c) {\n // float brightness = max(c.r, max(c.g, c.b));\n // float contribution = max(0., brightness - prefilter.x);\n // contribution /= max(brightness, 0.00001);\n // return vec4((c.rgb) * contribution, c.a);\n // return (c - vec3(prefilterThreshold)) * contribution;\n\n #ifdef HAS_GBUFFER\n #if !BACKGROUND_BLOOM\n if(getDepth(vUv) > 0.999) {\n return vec4(0.0);\n }\n #endif\n #endif\n\n float brightness = max(c.r, max(c.g, c.b));\n float soft = brightness + prefilter.x * (prefilter.y - 1.);\n soft = clamp(soft, 0., prefilter.z);\n soft = soft * soft * prefilter.w;\n float contribution = max(soft, brightness - prefilter.x);\n contribution /= max(brightness, 0.001);\n return vec4(c.rgb * contribution, c.a);\n\n}\n#endif\n\nvec4 Sample (vec2 uv) {\n return min(vec4(MAX_INTENSITY, MAX_INTENSITY, MAX_INTENSITY, 1.), tDiffuseTexelToLinear( texture2D( tDiffuse, uv ) ));\n}\n\nvec4 SampleBox (vec2 uv, float delta) {\n vec4 o = vec2(-delta, delta).xxyy / tDiffuseSize.xyxy;\n vec4 s =\n Sample(uv + o.xy) + Sample(uv + o.zy) +\n Sample(uv + o.xw) + Sample(uv + o.zw);\n return s * 0.25;\n}\n\nint getBloomBit(in int number) {\n #ifdef WebGL2Context\n return (number/4) % 2;\n #else\n return int(mod(floor(float(number)/4.), 2.));\n #endif\n}\n\nvoid main() {\n #if PASS_STEP == 0 //prefilter + down\n\n #ifdef GBUFFER_HAS_FLAGS\n int doBloom = getBloomBit(getGBufferFlags(vUv).a);\n #else\n int doBloom = 1;\n #endif\n\n gl_FragColor = float(doBloom) * weight * Prefilter(SampleBox(vUv, 1.));\n gl_FragColor.a = 1.;\n\n #elif PASS_STEP == 1 //down\n\n gl_FragColor = weight * (SampleBox(vUv, 1.));\n gl_FragColor.a = 1.;\n\n #elif PASS_STEP == 2 //up\n\n gl_FragColor = (SampleBox(vUv, 0.5));\n gl_FragColor.a = 1.;\n\n #elif PASS_STEP == 3 //final\n\n vec4 texel = tSourceTexelToLinear ( texture2D(tSource, vUv) );\n vec4 bloom = intensity * SampleBox(vUv, 0.5).rgba;\n float brightness = max(bloom.r, max(bloom.g, bloom.b));\n texel.rgb += bloom.rgb;\n texel.a = min(1., texel.a + brightness);\n gl_FragColor = texel;\n\n #elif PASS_STEP == 4 //debug\n\n vec4 texel = vec4(0.);\n texel.rgb += intensity * SampleBox(vUv, 0.5).rgb;\n texel.a = 1.;\n gl_FragColor = texel;\n\n #endif\n\n #include <colorspace_fragment>\n\n}\n","import {\n AdditiveBlending,\n CopyShader,\n ExtendedShaderPass,\n GBufferPlugin,\n GBufferUpdater,\n GBufferUpdaterContext,\n generateUiConfig,\n HalfFloatType,\n IMaterial,\n IMaterialUserData,\n IPassID,\n IPipelinePass,\n IViewerEvent,\n IWebGLRenderer,\n matDefineBool,\n MaterialExtension,\n NoBlending,\n onChange,\n onChange2,\n PipelinePassPlugin,\n serialize,\n ThreeViewer,\n uiConfig,\n uiConfigMaterialExtension,\n uiFolderContainer,\n UiObjectConfig,\n uiSlider,\n uiToggle,\n uniform,\n updateBit,\n Vector2,\n Vector4,\n WebGLMultipleRenderTargets,\n WebGLRenderTarget,\n} from 'threepipe'\nimport hdrBloom from './shaders/hdrBloom.glsl'\n\nconst passId = 'bloom'\ntype BloomPassId = typeof passId\n\n/**\n * Bloom Plugin\n *\n * Adds HDR Bloom post-processing effect to the scene\n * @category Plugins\n */\nexport class BloomPlugin\n extends PipelinePassPlugin<BloomPluginPass<BloomPassId>, BloomPassId>\n implements GBufferUpdater {\n static readonly PluginType = 'Bloom'\n static readonly OldPluginType = 'BloomPlugin' // todo swap\n readonly passId = passId\n\n readonly materialExtension: MaterialExtension = uiConfigMaterialExtension(this._getUiConfig.bind(this), BloomPlugin.PluginType)\n\n protected _createPass() {\n const pass = new BloomPluginPass(this.passId, Math.min(8, this._viewer?.renderManager.maxHDRIntensity || 8))\n return pass\n }\n\n @uiConfig() declare protected _pass?: BloomPluginPass<BloomPassId>\n\n dependencies = [GBufferPlugin]\n\n onAdded(viewer: ThreeViewer) {\n super.onAdded(viewer)\n const gbuffer = viewer.getPlugin(GBufferPlugin)\n if (gbuffer) {\n gbuffer.registerGBufferUpdater(this.constructor.PluginType, this.updateGBufferFlags.bind(this))\n this._pass!.material.registerMaterialExtensions([gbuffer.unpackExtension])\n } else viewer.addEventListener('addPlugin', this._onPluginAdd) // todo subscribe to remove plugin\n viewer.materialManager.registerMaterialExtension(this.materialExtension)\n }\n private _onPluginAdd = (e: IViewerEvent)=>{ // not really required since gbuffer is now a dependency\n if (e.plugin?.constructor?.PluginType !== GBufferPlugin.PluginType) return\n const gbuffer = e.plugin as GBufferPlugin\n gbuffer.registerGBufferUpdater(this.constructor.PluginType, this.updateGBufferFlags.bind(this))\n this._pass?.material.registerMaterialExtensions([gbuffer.unpackExtension])\n this._viewer?.removeEventListener('addPlugin', this._onPluginAdd)\n }\n onRemove(viewer: ThreeViewer) {\n viewer.removeEventListener('addPlugin', this._onPluginAdd)\n const gbuffer = viewer.getPlugin(GBufferPlugin)\n gbuffer?.unregisterGBufferUpdater(this.constructor.PluginType)\n gbuffer && this._pass?.material.unregisterMaterialExtensions([gbuffer.unpackExtension])\n viewer.materialManager.unregisterMaterialExtension(this.materialExtension)\n super.onRemove(viewer)\n }\n\n updateGBufferFlags(data: Vector4, c: GBufferUpdaterContext): void {\n if (!c.material || !c.material.userData) return\n const disabled = c.material.userData[BloomPlugin.PluginType]?.enable === false ||\n c.material.userData.pluginsDisabled\n const x = disabled ? 0 : 1\n data.w = updateBit(data.w, 2, x)\n }\n\n uiConfig: UiObjectConfig = {\n type: 'folder',\n label: 'Bloom Plugin',\n onChange: this.setDirty.bind(this),\n children: [\n ...generateUiConfig(this) || [],\n ],\n }\n\n static AddBloomData(material: IMaterial, params?: IMaterialUserData['Bloom'], setDirty = true): IMaterialUserData['Bloom']|null {\n const ud = material?.userData\n if (!ud) return null\n if (!ud[BloomPlugin.PluginType]) {\n ud[BloomPlugin.PluginType] = {}\n }\n const data = ud[BloomPlugin.PluginType]!\n data.enable = true\n params && Object.assign(data, params)\n if (setDirty && material.setDirty) material.setDirty()\n return data\n }\n\n /**\n * This uiConfig is added to each material by extension\n * @param material\n * @private\n */\n private _getUiConfig(material: IMaterial) {\n const config: UiObjectConfig = {\n type: 'folder',\n label: 'Bloom',\n children: [\n {\n type: 'checkbox',\n label: 'Enabled',\n get value() {\n return material.userData[BloomPlugin.PluginType]?.enable ?? true\n },\n set value(v) {\n let data = material.userData[BloomPlugin.PluginType]\n if (v === data?.enable) return\n if (!data) data = BloomPlugin.AddBloomData(material, undefined, false)!\n data.enable = v\n material.setDirty()\n config.uiRefresh?.(true, 'postFrame')\n },\n onChange: this.setDirty,\n },\n ],\n }\n return config\n }\n}\n\n@uiFolderContainer('Bloom Pass')\nexport class BloomPluginPass<Tid extends IPassID> extends ExtendedShaderPass implements IPipelinePass<Tid> {\n uiConfig?: UiObjectConfig = undefined\n before = ['screen']\n after = ['render', 'progressive']\n required = ['render']\n public readonly passId: Tid\n\n constructor(pid: Tid, maxIntensity = 16) {\n super({\n vertexShader: CopyShader.vertexShader,\n defines: {\n ['PASS_STEP']: 1,\n ['MAX_INTENSITY']: Math.min(maxIntensity, 16),\n },\n uniforms: {\n tSource: {value: null},\n tDiffuse: {value: null},\n // intensity: {value: 0.2},\n opacity: {value: 1.0},\n // prefilter: {value: new Vector4(1, 0.5, 0, 0)},\n tDiffuseSize: {value: new Vector2()},\n weight: {value: 1},\n // tNormalDepth: {value: null},\n // tGBufferFlags: {value: null},\n },\n fragmentShader: hdrBloom,\n }, 'tDiffuse', 'tSource')\n this.passId = pid\n this._updateWeights = this._updateWeights.bind(this)\n this._thresholdsUpdated = this._thresholdsUpdated.bind(this)\n this._updateWeights()\n this._thresholdsUpdated()\n this.clear = true\n\n // for tweakpane UI. todo: check if required and why\n // ;(this as any).userData = {setDirty: ()=>{\n // this.setDirty()\n // }}\n\n }\n\n @uniform() prefilter = new Vector4(2, 0.5, 0, 0)\n\n @uiSlider('Threshold', [0, 2])\n @onChange(BloomPluginPass.prototype._thresholdsUpdated)\n @serialize() threshold = 2\n\n @uiSlider('Soft Threshold', [0, 1])\n @onChange(BloomPluginPass.prototype._thresholdsUpdated)\n @serialize() softThreshold = 0.5\n\n @uiSlider('Intensity', [0, 3])\n @serialize() @uniform() intensity = 0.2\n\n @uiToggle('Background Bloom')\n @serialize()\n @matDefineBool('BACKGROUND_BLOOM')\n backgroundBloom = false\n\n @uiSlider('Iterations', [2, 7], 1)\n @onChange2(BloomPluginPass.prototype._updateWeights)\n @serialize() bloomIterations = 4\n\n private _currentIterations = 0 // could be less than bloomIterations based on canvas size\n\n @uiSlider('Radius', [0, 1], 0.01)\n @onChange2(BloomPluginPass.prototype._updateWeights)\n @serialize() radius = 0.6\n\n @uiSlider('Power', [0.2, 10], 0.01)\n @onChange2(BloomPluginPass.prototype._updateWeights)\n @serialize() power = 1\n\n private _thresholdsUpdated() {\n this.prefilter.x = this.threshold\n this.prefilter.y = this.softThreshold\n this.prefilter.z = 2 * this.prefilter.x * this.prefilter.y\n this.prefilter.w = this.uniforms?.prefilter ? .125 / (this.uniforms.prefilter.value.z + 0.00001) : 0\n }\n\n @uiToggle('Debug')\n bloomDebug = false\n\n private _weights: any = []\n\n render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, readBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) {\n const renderManager = renderer.renderManager\n // prefilter\n this.material.defines.PASS_STEP = 0\n\n this.clear = true\n\n this.needsSwap = false\n\n const source = readBuffer\n if (!source) {\n console.warn('BloomPluginPass: No source to read from')\n return\n }\n this.needsSwap = true\n\n let sizeMultiplier = 0.5 // todo are we starting with 0.25? this should be 1 maybe? but more memory...\n let width = source.width * sizeMultiplier\n let height = source.height * sizeMultiplier\n const textures: any[] = []\n let currentDestination = renderManager.getTempTarget({sizeMultiplier: 1, type: HalfFloatType}) as any as WebGLRenderTarget\n textures.push(currentDestination)\n let currentSource = source\n this.material.needsUpdate = true\n this.material.uniforms.weight.value = this._weights[0]\n\n\n super.render(renderer, currentDestination, currentSource, deltaTime, maskActive)\n\n currentSource = currentDestination\n\n const ci = this._currentIterations\n\n let i = 1\n const iter = Math.max(2, this.bloomIterations)\n for (; i < iter; i++) {\n width /= 2\n height /= 2\n sizeMultiplier /= 2\n if (height < 2 || width < 2) {\n break\n }\n currentDestination = renderManager.getTempTarget({sizeMultiplier, type: HalfFloatType}) as any as WebGLRenderTarget\n textures.push(currentDestination)\n\n this.material.defines.PASS_STEP = 1\n\n let modifiedWeight = this._weights[i]\n // if(i > 1)\n {\n modifiedWeight = this._weights[i - 1] !== 0 ? this._weights[i] / this._weights[i - 1] : this._weights[i]\n }\n this.material.uniforms.weight.value = modifiedWeight\n this.material.needsUpdate = true\n super.render(renderer, currentDestination, currentSource, deltaTime, maskActive)\n\n currentSource = currentDestination\n this._currentIterations = i + 1\n }\n\n // console.log(this._currentIterations)\n\n if (ci !== this._currentIterations)\n this._updateWeights(false)\n\n this.clear = false\n\n const oldAutoClear = renderer.autoClear\n renderer.autoClear = false\n for (i -= 2; i >= 0; i--) {\n currentDestination = textures[i]\n textures[i] = undefined\n\n this.material.defines.PASS_STEP = 2\n this.material.transparent = true\n this.material.blending = AdditiveBlending\n // this.material.blendSrc = OneFactor;\n // this.material.blendDst = OneFactor;\n this.material.needsUpdate = true\n renderer.autoClear = false\n super.render(renderer, currentDestination, currentSource, deltaTime, maskActive)\n this.material.blending = NoBlending\n\n renderManager.releaseTempTarget(currentSource as any)\n\n currentSource = currentDestination\n }\n\n this.clear = true\n\n renderer.autoClear = oldAutoClear\n\n renderer.autoClear = true\n\n if (this.bloomDebug) {\n this.material.defines.PASS_STEP = 4\n this.material.needsUpdate = true\n super.render(renderer, writeBuffer, currentSource, deltaTime, maskActive)\n } else {\n this.uniforms.tSource.value = source.texture\n this.material.defines.PASS_STEP = 3\n this.material.need