尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

three.js TSL BloomNode:基于 WebGPU 渲染管线的泛光(Bloom)后处理节点详解

three.js TSL BloomNode:基于 WebGPU 渲染管线的泛光(Bloom)后处理节点详解 three.js TSL BloomNode基于 WebGPU 渲染管线的泛光Bloom后处理节点详解【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsBloomNode 是 three.js TSLThree Shading Language生态中用于在 WebGPU 渲染管线上实现泛光效果的后处理节点继承自EventDispatcher → Node → TempNode这条类型链。它通过“亮度提取 多级可分离高斯模糊 复合叠加”三阶段流水线将场景中的高亮区域扩散为柔和光晕。读完本文你将掌握bloom()TSL 函数的完整参数体系、选择性泛光的 MRT 实现方式以及从源码层面理解其模糊核、Mip 链与分辨率缩放机制并能直接在 webgpu_postprocessing_bloom.html 等官方示例上动手复现。导入与基本用法BloomNode 是一个 addon 模块需要显式导入见仓库 BloomNode.js 文件头部的three_import注解import { bloom } from three/addons/tsl/display/BloomNode.js;最简用法是把场景 Pass 的输出纹理节点交给bloom()再把原色与泛光结果相加作为RenderPipeline的输出节点const renderPipeline new THREE.RenderPipeline( renderer ); const scenePass pass( scene, camera ); const scenePassColor scenePass.getTextureNode( output ); const bloomPass bloom( scenePassColor ); renderPipeline.outputNode scenePassColor.add( bloomPass );bloom()是一个 TSL 工厂函数内部执行new BloomNode( nodeObject( node ), strength, radius, threshold )见 BloomNode.js#L595因此第一个参数既可以是普通节点也可以先经过nodeObject()包装。官方示例 webgpu_postprocessing_bloom.html 在此基础之上做了完整的工程化演示为场景 Pass 配置了 MSAA 解析优化resolveColorBuffer: true不存储多重采样缓冲加载 GLTF 动画模型并通过 Inspector 参数面板实时调节bloomPass.threshold.value、bloomPass.strength.value、bloomPass.radius.value三个 Uniform 值——这正好印证了这三个属性在运行时是可直接改写的UniformNodefloat。选择性泛光基于 MRT 的 emissive 通道方案默认情况下 BloomNode 作用于整帧图像。若要只让部分对象产生泛光官方推荐的方案是利用emissive材质属性 多渲染目标MRT单独提取自发光通道const renderPipeline new THREE.RenderPipeline( renderer ); const scenePass pass( scene, camera ); scenePass.setMRT( mrt( { output, emissive } ) ); const scenePassColor scenePass.getTextureNode( output ); const emissivePass scenePass.getTextureNode( emissive ); const bloomPass bloom( emissivePass ); renderPipeline.outputNode scenePassColor.add( bloomPass );示例 webgpu_postprocessing_bloom_emissive.html 展示了这套方案的完整形态其中有几个值得注意的细节MRT 的第二通道写作emissive: vec4( emissive, output.a )即把emissiveTSL 节点内置变量暴露材质自发光写入独立的附加纹理对附加纹理设置emissiveTexture.type THREE.UnsignedByteType以“优化带宽”——自发光通道不需要浮点精度降为字节型可减少显存带宽泛光参数取bloom( emissivePass, 2.5, .5 )即 strength 2.5、radius 0.5。除了 emissive 方案示例 webgpu_postprocessing_bloom_selective.html 还演示了逐对象控制通过material.mrtNode mrt( { bloomIntensity: uniform( bloomIntensity ) } )为每个网格写入一个 0/1 的强度通道点击物体时切换该 uniform最终用bloom( outputPass.mul( bloomIntensityPass ) )实现“点谁谁发光”的交互效果。构造函数参数new BloomNode( inputNode, strength, radius, threshold )见 BloomNode.js#L70-L100参数类型默认值说明inputNodeNodevec4必填效果输入节点通常是场景 Pass 的纹理节点strengthnumber | Nodefloat1泛光强度最后乘在复合结果上radiusnumber | Nodefloat0泛光半径必须在[0,1]范围内thresholdnumber | Nodefloat0亮度阈值限制哪些高亮区域参与泛光构造函数中strength.isNode ? strength : uniform( strength )这一写法意味着三个参数既可以传普通数值自动包装为 uniform也可以直接传 Node便于做程序化驱动。此外构造函数还会创建smoothWidth默认uniform( 0.01 )用于调节亮度提取的平滑过渡宽度bloomTintColors长度为 5 的Vector3数组每级 Mip 一个着色默认全白文档注释明确说明可将其改为暖色或用于“anamorphic 风格”的着色_resolutionScale内部私有属性默认0.5见下文。内部渲染流水线源码剖析BloomNode 的核心逻辑在updateBefore(frame)中每帧执行一次完整的渲染序列见 BloomNode.js#L348-L401分三步1. 亮度提取High Pass_renderTargetBrightHalfFloat 类型、无深度缓冲作为目标渲染一个以highPassFn为片元节点的NodeMaterial全屏四边形。默认的高通滤波函数为const luminosityHighPass Fn( ( { input, threshold, smoothWidth } ) { const v luminance( input.rgb ); const alpha smoothstep( threshold, threshold.add( smoothWidth ), v ); return mix( vec4( 0 ), input, alpha ); } );即计算输入亮度v用smoothstep(threshold, threshold smoothWidth, v)生成 0~1 的掩膜alpha再线性混合出“只保留高亮区域”的纹理。threshold决定哪些区域被提取smoothWidth决定过渡的柔和程度。2. 多级可分离高斯模糊源码维护_nMips 5组水平/垂直RenderTarget每级模糊核尺寸为kernelSizeArray [ 6, 10, 14, 18, 22 ]见 BloomNode.js#L425注释指出这些尺寸是为避免块状伪影而调整过的系数。每一级先沿 X 方向_BlurDirectionX (1,0)再沿 Y 方向(0,1)各渲染一次输出链式传递为下一级的输入。模糊核系数按高斯分布0.39894 * exp(-0.5 * i²/σ²) / σσ kernelRadius / 3生成并且源码将相邻采样点合并为单次双线性采样bilinear fetch显著减少纹理读取次数。setSize()中每级尺寸逐次减半resx Math.floor( resx / 2 )形成完整的 Mip 链。3. 复合叠加Composite五路模糊结果按固定权重bloomFactors [ 1.0, 0.8, 0.6, 0.4, 0.2 ]加权求和最后乘以strengthconst lerpBloomFactor Fn( ( { factor, radius } ) { const mirrorFactor float( 1.2 ).sub( factor ); return mix( factor, mirrorFactor, radius ); }, { factor: float, radius: float, return: float } );lerpBloomFactor的妙处在于radius 0时各 Mip 权重就是bloomFactors本身细节保留更多radius 1时权重变为1.2 - factor即[ 0.2, 0.4, 0.6, 0.8, 1.0 ]——低分辨率大范围模糊的权重被放大视觉上泛光扩散得更远更柔。每一路还会乘上对应的bloomTintColors颜色这就是按 Mip 级染色的实现点。最终合成结果写回_renderTargetsHorizontal[0]并通过passTexture()暴露为_textureOutput。关键属性与方法以下属性与方法均可在 BloomNode.js 源码中一一对应属性.inputNode : Nodevec4— 效果输入节点.strength : UniformNodefloat— 泛光强度运行时可改.value.radius : UniformNodefloat— 泛光半径取值范围[0,1]作用见上文lerpBloomFactor.threshold : UniformNodefloat— 亮度阈值.smoothWidth : UniformNodefloat— 默认0.01调节从场景中提取亮度的平滑度.highPassFn : function— 可注入自定义高通滤波器的入口这是做特殊泛光形态如水平拉伸的变形宽银幕泛光的官方扩展点.updateBeforeType : string— 默认frameNodeUpdateType.FRAME覆盖自 TempNode表明效果在updateBefore()中每帧渲染一次.bloomTintColors : ArrayVector3— 每级 Mip 的染色源码中的补充属性文档 API 未单列。方法.getTextureNode() : PassTextureNode— 返回_textureOutput即复合后的泛光纹理节点用于接入outputNode表达式.setResolutionScale( resolutionScale ) : BloomNode— 设置分辨率缩放因子该值会乘上渲染器的宽高。1表示全分辨率源码默认值为0.5意味着模糊金字塔以半分辨率起步这是控制性能开销的主要旋钮.getResolutionScale() : number— 读取当前分辨率缩放.setSize( width, height )— 按_resolutionScale计算并重建全部渲染目标尺寸、更新各级模糊的invSizeuniform。此方法由updateBefore()每帧根据renderer.getDrawingBufferSize()自动调用窗口尺寸变化时无需手动处理.setup( builder ) : PassTextureNode— 被 NodeBuilder 调用构建高通、五组可分离模糊与复合三个NodeMaterial的 TSL 代码.updateBefore( frame )— 每帧执行“提取 → 模糊 → 复合”三步渲染并负责保存/恢复渲染器状态RendererUtils.resetRendererState/restoreRendererState.dispose()— 释放 11 个渲染目标1 个 bright 5 组 H/V及全部NodeMaterial效果不再需要时必须调用。实战技巧自定义 highPassFn 与分辨率调优仓库示例 webgpu_postprocessing_anamorphic.html 完整演示了两个高级技巧的组合const bloomPass bloom( scenePass.getTextureNode(), intensity, radius, threshold ); bloomPass.setResolutionScale( 0.25 ); // 自定义高通滤波器实现 anamorphic 效果 bloomPass.highPassFn Fn( ( { input, threshold, smoothWidth } ) { const v luminance( input.rgb ); const alpha smoothstep( threshold, threshold.add( smoothWidth ), v ); const brightPass rtt( mix( vec4( 0 ), input, alpha ), null, null, { wrapS: THREE.MirroredRepeatWrapping, wrapT: THREE.MirroredRepeatWrapping } ); // 在水平方向做 80 次偏移采样并衰减加权得到横向拉伸的光条 // ... return total.div( samples.div( 3.0 ) ); } );要点有二一是setResolutionScale( 0.25 )把整条模糊金字塔压缩到 1/4 分辨率配合后续合成可显著降低开销二是highPassFn返回的仍是一个 TSL 节点示例里内部还嵌了一层rtt()渲染目标纹理 水平偏移采样循环说明这个钩子的自由度很高——你可以在提取阶段做任何与 UV、时间、方向相关的处理而不必改动 BloomNode 本身的模糊与合成逻辑。调参方面官方示例给出的面板范围可作为经验参考见 webgpu_postprocessing_bloom.html 的 Inspector 参数配置threshold 0~1、strength 0~3、radius 0~1。由于 bloom 结果与场景色相加后还要经过色调映射示例同时暴露了toneMappingExposureMath.pow( value, 4.0 )指数化以便配合调整整体曝光。小结与延伸阅读BloomNode 的设计体现了 TSL 后处理节点的一贯范式setup()只负责声明 TSL 代码与材质updateBefore()负责每帧命令式的多 Pass 渲染结果经PassTextureNode回注节点图。使用它时记住三个关键控制面即可threshold smoothWidth决定“哪些区域参与泛光”radius strength bloomTintColors决定“泛光长什么样”setResolutionScale()决定“花多少性能预算”。结合 MRT 的emissive通道或自定义bloomIntensity通道即可覆盖从全图泛光到逐对象选择性泛光的全部常见需求。更多相关实现可参考BloomNode 源码、基础泛光示例、自发光选择性泛光示例、逐对象选择性泛光示例、Anamorphic 泛光示例。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表