
three.js TSLBilateralBlurNode 保边模糊后处理节点完全解析【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsBilateralBlurNode 是 three.js TSLThree Shading Language体系中的一个后处理节点用于在平滑图像的同时保留锐利边缘。本文基于仓库中 BilateralBlurNode 文档 与 完整实现源码覆盖其导入方式、构造参数、属性与方法的完整 API 说明并深入源码剖析其两遍分离 空间/颜色双权重的滤波原理最后给出在 Godrays 场景中的真实接线示例帮助你在 WebGPU 后处理管线中直接落地保边模糊。核心概念双边模糊为什么能保边BilateralBlurNode 实现的是双边滤波Bilateral Filter思想。与标准高斯模糊对所有像素一视同仁不同双边模糊在采样邻域像素时会分析其强度/颜色与中心像素的差异如果邻近像素与中心像素差异过大说明此处存在边缘该像素就会被排除或大幅降权从而在平滑噪声的同时保住边缘轮廓。从源码的 TSL 着色代码BilateralBlurNode.js 中的setup()方法可以看到完整的双权重模型空间权重spatial weight按采样距离衰减的高斯系数由sigma控制颜色权重color weight按亮度差luminance difference计算的高斯函数exp( -0.5 * diff² / sigmaColor² )由sigmaColor控制最终权重 空间权重 × 颜色权重所有加权采样按总权重归一化后输出。这种空间 颜色双核设计是双边模糊区别于普通高斯模糊的根本所在也是该节点在 Godrays光晕射线步进等效果中用于抑制噪声伪影的关键。导入方式BilateralBlurNode 是一个 addon附加模块不在核心包默认导出中必须显式导入。package.json 中通过./addons: ./examples/jsm/Addons.js将three/addons别名映射到examples/jsm目录import { bilateralBlur } from three/addons/tsl/display/BilateralBlurNode.js;注意导出的是 TSL 函数bilateralBlur而非类本身。从源码末尾可以看到该函数的定义BilateralBlurNode.js#L375export const bilateralBlur ( node, directionNode, sigma, sigmaColor ) new BilateralBlurNode( convertToTexture( node ), directionNode, sigma, sigmaColor );一个容易忽略的细节TSL 函数入口会用convertToTexture( node )把传入的任意节点先转换为纹理节点。这意味着你可以直接把一个计算节点如另一个后处理 pass 的输出、或任意vec4类型的 TSL 表达式作为输入而不必先手动渲染到渲染目标——这是 TSL 后处理组合能力的体现。构造函数与参数const blurPass new BilateralBlurNode( textureNode, directionNode, sigma, sigmaColor );由于通常通过 TSL 函数构造更常见的写法是const blurPass bilateralBlur( inputNode ); // 使用全部默认参数各参数说明如下与文档 BilateralBlurNode.html.md 一致默认值与源码构造函数 BilateralBlurNode.js#L38 相互印证参数类型默认值说明textureNodeTextureNode必填表示效果输入的纹理节点经 TSL 函数传入时会先被convertToTexture转换directionNodeNode.(vec2\|float)null定义模糊的方向和半径。为null时源码中使用vec2( 1 )作为单位方向见 BilateralBlurNode.js#L237sigmanumber4控制空间核spatial kernel。值越大模糊半径越宽sigmaColornumber0.1控制强度核intensity kernel。值越大允许颜色差异更大的像素被混合到一起值越小保边越严格sigma与sigmaColor的配合关系值得注意sigma决定看多远sigmaColor决定多大的颜色差还算同一侧。想强模糊但严格保边可增大sigma同时保持较小的sigmaColor反之则整体混合更平滑。属性详解BilateralBlurNode 继承链为EventDispatcher → Node → TempNode → BilateralBlurNode其类声明见 BilateralBlurNode.js#L22。可读写属性如下.textureNode : TextureNode效果的输入纹理节点。.directionNode : Node.(vec2|float)定义模糊的方向和半径。.sigma : number / .sigmaColor : number同构造参数运行期可修改以实时调节模糊强度与保边程度。.resolutionScale : number分辨率缩放比例默认1。它决定了中间渲染目标的实际尺寸setSize()中会以Math.round( width * resolutionScale )计算宽度/高度BilateralBlurNode.js#L148-L157。将其设为0.5之类的值可以在视觉上几乎无损地降低模糊 pass 的开销是后处理中常见的性能优化手段。.updateBeforeType : string默认frame即NodeUpdateType.FRAME。含义是该节点在每帧的主渲染之前执行一次updateBefore()完成模糊渲染而不是在 TSL 编译期静态生成。源码中显式赋值BilateralBlurNode.js#L130覆盖了 TempNode 的默认行为——这与文档页 TempNode 文档 中.updateBeforeType的说明对应。方法与实现机制.updateBefore( frame ) —— 每帧的两遍模糊这是整个效果的核心执行入口。从 BilateralBlurNode.js#L164-L211 的实现看流程为通过RendererUtils.resetRendererState保存当前渲染器状态依据输入纹理的实际宽高调用setSize()并同步中间纹理的type水平 pass将_passDirection设为(1, 0)渲染一个QuadMesh到_horizontalRT垂直 pass临时把textureNode.value指向水平 pass 的输出将_passDirection设为(0, 1)再渲染到_verticalRT恢复textureNode.value与渲染器状态。即一次双边模糊被拆解为水平、垂直两个一维 passseparable 分解。这与高斯模糊的分隔化技巧一致将二维卷积分解为两个一维卷积把O(k²)的采样量降到O(2k)。源码注释也明确写道Bilateral blur is applied in two passes (horizontal, vertical)BilateralBlurNode.js#L80-L86。需要注意的是严格意义上颜色权重基于亮度差的高斯不可完全分隔化两遍分离是一种工程近似——它保留了大部分保边效果同时获得了分隔化模糊的性能收益。空间系数的计算_getSpatialCoefficients()BilateralBlurNode.js#L342-L355按给定半径生成高斯系数const kernelRadius this.sigma * 2 3; // 核半径由 sigma 决定 const sigma kernelRadius / 3; // 内部 sigma 取半径的 1/3 coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( sigma * sigma ) ) / sigma );注意区分两个 sigma构造函数参数sigma默认 4用于推导核半径sigma * 2 3而系数公式中的内部 sigma 是半径 / 3。这意味着默认参数下核半径为 11即每次采样沿方向各偏移 1~10 个像素。系数在 CPU 端预计算后以float()常量形式进入 TSL 循环。.setup( builder ) —— TSL 代码生成setup()被 NodeBuilder 调用负责把模糊逻辑翻译成 TSL 代码并创建渲染QuadMesh所用的NodeMaterialBilateralBlurNode.js#L230-L318。其内部blur()函数的关键片段const kernelSize this.sigma * 2 3; const spatialCoefficients this._getSpatialCoefficients( kernelSize ); const centerColor sampleTexture( uvNode ); const centerLuminance luminance( centerColor.rgb ); const colorSigmaFactor float( -0.5 ).div( float( this.sigmaColor * this.sigmaColor ) ); for ( let i 1; i kernelSize; i ) { const uvOffset vec2( direction.mul( invSize.mul( i ) ) ); // 沿 /− 两个方向对称采样 const sample1 sampleTexture( uvNode.add( uvOffset ) ); const sample2 sampleTexture( uvNode.sub( uvOffset ) ); // 颜色权重亮度差的高斯 const colorWeight exp( diff.mul( diff ).mul( colorSigmaFactor ) ); // 双边权重 空间权重 × 颜色权重 colorSum.addAssign( sample.mul( spatialWeight.mul( colorWeight ) ) ); weightSum.addAssign( spatialWeight.mul( colorWeight ) ); } return colorSum.div( max( weightSum, 0.0001 ) ); // 权重归一化其中invSize1/分辨率由setSize()维护用于把像素偏移换算成 UV 偏移。归一化时以max( weightSum, 0.0001 )兜底避免边缘处权重和接近零导致的除零问题。.getTextureNode() / .setSize() / .dispose()getTextureNode()返回一个PassTextureNode它绑定_verticalRT.texture即垂直 pass 的最终输出这就是接入后续 TSL 链路的出口。源码中还有一行this._textureNode.uvNode textureNode.uvNodeBilateralBlurNode.js#L113让输出纹理继承输入节点的 UV 变换保证后续组合时采样坐标一致。setSize( width, height )按resolutionScale缩放后重建两个中间RenderTarget并更新_invSize。注意它在updateBefore()中每帧都会根据输入纹理实际尺寸被调用一次因此通常无需手动调用。dispose()释放两个中间渲染目标与模糊NodeMaterialBilateralBlurNode.js#L325-L332。效果不再使用时应调用避免 GPU 资源泄漏。关于 TempNode 继承BilateralBlurNode 继承自 TempNode。从 TempNode 源码结构看其build()会在生成阶段检测该节点是否被多处引用hasDependencies()若是则生成临时变量缓存中间结果防止同一效果被重复求值。因此bilateralBlur()返回的节点在 TSL 图中被多次使用时不会导致模糊计算重复执行。实战示例Godrays 场景中的双边模糊仓库中的官方示例 webgpu_postprocessing_godrays.html 展示了bilateralBlur的典型用途GodraysNode 文档页GodraysNode.html.md明确建议计算完 godrays 后对结果施加 Bilateral Blur 以缓解射线步进与噪声伪影。示例中的接线方式webgpu_postprocessing_godrays.html#L138-L161import { bilateralBlur } from three/addons/tsl/display/BilateralBlurNode.js; // beauty pass const scenePass pass( scene, camera ); const scenePassColor scenePass.getTextureNode( output ); const scenePassDepth scenePass.getTextureNode( depth ); // godrays const godraysPass godrays( scenePassDepth, camera, pointLight ); const godraysPassColor godraysPass.getTextureNode(); // blur对 godrays 结果做保边模糊使用全部默认参数 const blurPass bilateralBlur( godraysPassColor ); const blurPassColor blurPass.getTextureNode(); // composite用 depthAwareBlend 把模糊后的光晕合成回场景 const outputBlurred depthAwareBlend( scenePassColor, blurPassColor, scenePassDepth, camera, { blendColor, edgeRadius, edgeStrength } ); renderPipeline.outputNode outputBlurred;这个组合体现了 TSL 后处理的链式特征每个节点通过getTextureNode()输出纹理节点直接作为下一个节点的输入bilateralBlur只需一行即可完成输入 → 两遍模糊 → 输出纹理的全过程。示例中还通过 GUI 开关在outputBlurred与未模糊的outputRaw之间切换方便直观对比保边模糊对光晕噪声的抑制效果。适用前提该节点走 WebGPU 后处理管线示例使用WebGPURenderer与RenderPipeline与 TSL 文档页 中bilateralBlur的函数签名条目一致。API 速查API签名说明TSL 函数bilateralBlur( node, directionNode, sigma, sigmaColor )创建节点输入节点自动convertToTexture构造函数new BilateralBlurNode( textureNode, directionNode null, sigma 4, sigmaColor 0.1 )直接以纹理节点构造.getTextureNode(): PassTextureNode获取效果输出纹理节点.setSize( width, height )—按resolutionScale设置中间渲染目标尺寸.updateBefore( frame )—每帧执行水平/垂直两遍模糊.setup( builder ): PassTextureNode由 NodeBuilder 调用生成 TSL 模糊代码.dispose()—释放中间 RenderTarget 与材质不再使用时应调用相关文档与源码BilateralBlurNode 文档页、实现源码、TempNode 基类、TSL 函数索引、Godrays 实战示例。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考