☰
three.js SAOPass 详解:Scalable Ambient Occlusion 屏幕空间环境光遮蔽后处理实现
2026/11/1 3:44:46 网站建设 项目流程

three.js SAOPass 详解:Scalable Ambient Occlusion 屏幕空间环境光遮蔽后处理实现

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

导读

SAOPass(Scalable Ambient Occlusion,可缩放环境光遮蔽)是 three.js 官方提供的后处理(Post-processing)Pass 之一,用于在 WebGL 渲染管线中为场景加入基于屏幕空间近似算法生成的环境光遮蔽效果,让物体接触面、缝隙与角落处产生柔和的"接触阴影",从而显著提升画面立体感与真实感。本指南以 docs/pages/SAOPass.html.md 为骨架,结合examples/jsm/postprocessing/SAOPass.js、examples/jsm/shaders/SAOShader.js与examples/jsm/shaders/DepthLimitedBlurShader.js等源码,从构造方式、参数语义、内部多阶段渲染流程到可运行的完整示例,帮助你直接把它接入自己的 EffectComposer 管线,并理解其底层工作原理。

一、SAOPass 是什么:与 SSAOPass 的定位差异

SAOPass是由社区开发者 Ludobaka 实现的屏幕空间环境光遮蔽算法,其灵感与算法根源来自 Ben Houston 早前的 SAO(Scalable Ambient Occlusion)实现(demo 页面examples/webgl_postprocessing_sao.html中的标注同样注明 "shader by Ben Houston / Post-processing pass by Ludobaka")。

在 three.js 的后处理体系中,同类的算法还有SSAOPass(Screen-Space Ambient Occlusion)。官方对两者的定位差异非常明确:SAOPass提供比SSAOPass更好的质量,但开销也更高。因此在工程选型上,追求性价比可以选择 SSAO;对画面质量敏感、且 GPU 预算允许时再选择 SAO。

SAOPass继承自后处理基类Pass,接口约定与调度行为统一由 examples/jsm/postprocessing/Pass.js 定义。

二、引入与最小接入示例

SAOPass属于 three.js 的 addons(附加组件),不会被包含在核心构建中,必须显式导入:

import { SAOPass } from 'three/addons/postprocessing/SAOPass.js';

将其加入渲染管线的最小示例(与源码 SAOPass.js 中的示例一致):

const composer = new EffectComposer( renderer ); const renderPass = new RenderPass( scene, camera ); composer.addPass( renderPass ); const saoPass = new SAOPass( scene, camera ); composer.addPass( saoPass );

需要注意的是,SAOPass需要接入完整的 EffectComposer 管线才能工作,而不是独立渲染:它依赖RenderPass先把场景渲染进缓冲,并且通常在其后追加一个OutputPass用于颜色空间/色调映射输出(下文完整示例会给出整条管线)。此外示例仓库中还提供官方 Demo 页面examples/webgl_postprocessing_sao.html,可直接打开查看运行效果。

三、构造函数

new SAOPass( scene : Scene, camera : Camera, resolution : Vector2 )

从 SAOPass.js 构造函数 可以看到参数定义:

参数类型说明
sceneScene需要计算环境光遮蔽的场景。Pass 内部会用深度与法线信息重渲染该场景(见第五部分源码分析)。
cameraCamera渲染所用相机。既支持透视相机也支持正交相机,源码通过this.camera.isPerspectiveCamera判断并在 shader 中定义PERSPECTIVE_CAMERA宏(见 SAOShader.js)。
resolutionVector2(可选)效果(AO 纹理)的渲染分辨率,默认值为new Vector2( 256, 256 )。传入后内部会复制一份存于this.resolution。AO 纹理独立于主缓冲,可低于屏幕分辨率以换取性能。

示例:

// 以默认 256×256 分辨率计算 AO const saoPass = new SAOPass( scene, camera ); // 以更高分辨率计算,获得更精细的 AO 细节 const saoPassHi = new SAOPass( scene, camera, new THREE.Vector2( 512, 512 ) );

四、Properties(属性)详解

.camera : Camera

当前用于 AO 计算的相机,与构造参数一致。

.clear : boolean

重写为默认执行一次清屏操作,默认值为true。它覆盖了基类 Pass#clear 的默认false。

.needsSwap : boolean

被重写为关闭缓冲交换,默认值为false。覆盖自 Pass#needsSwap 的默认true。在SAOPass中此标志为false意味着结果直接合成到readBuffer上(内部通过自定义混合完成),而不再与writeBuffer进行交换,这一点与第五部分render()的源码行为一致。

.params : Object

SAO 的全部可调参数,集中存放于该对象,是实际调参的核心入口。下表列出 源码默认值 及官方示例页examples/webgl_postprocessing_sao.html的 GUI 可调范围,并给出各参数作用:

参数默认值GUI 建议范围含义
output0见下输出模式,取SAOPass.OUTPUT枚举值
saoBias0.5[-1, 1]遮蔽判断的偏置量,用于抑制自遮蔽(self-occlusion)产生的黑斑噪点
saoIntensity0.18[0, 1]AO 遮蔽强度,越大阴影越深
saoScale1[0, 10]遮蔽影响半径的空间比例因子,与场景尺度相关
saoKernelRadius100[1, 100]采样核半径(像素级),越大遮蔽范围越广、开销越高
saoMinResolution0[0, 1]归一化的最小遮蔽分辨率阈值,用于控制近处/远处细节参与度
saoBlurtrue开关是否对 AO 纹理执行深度限制的双向高斯模糊以去除噪点
saoBlurRadius8[0, 200]模糊核半径,执行时会被Math.floor取整为整数(见 SAOPass.js)
saoBlurStdDev4[0.5, 150]高斯模糊标准差,决定权重衰减速度
saoBlurDepthCutoff0.01[0.0, 0.1]深度断裂处的视空间截止值(相对比例),防止模糊跨越边缘渗色

其中output对应SAOPass.OUTPUT静态枚举(定义于 SAOPass.js 末尾):

SAOPass.OUTPUT = { 'Default': 0, // 默认混合结果:原画面 × AO 'SAO': 1, // 仅输出 AO 遮蔽因子 'Normal': 2 // 仅输出法线缓冲(调试用) };

各通道可分别用于"最终合成"与"算法调试",便于观察中间结果。

.resolution : Vector2

效果(AO)分辨率,默认(256, 256)。修改后需通过setSize同步内部 RenderTarget。

.scene : Scene

用于渲染 AO 的场景,与构造参数一致。

五、Methods(方法)详解

.render( renderer, writeBuffer, readBuffer, deltaTime, maskActive )

执行 SAO 后处理的核心逻辑(源码位置)。deltaTime与maskActive参数在接口上保留(由EffectComposer传入),当前实现中并未实际使用(形参注释掉)。参数含义与基类一致:

  • renderer:WebGLRenderer 渲染器;
  • writeBuffer:写入缓冲,理论上为本 Pass 的目标缓冲;
  • readBuffer:读取缓冲,可从中拿到上一 Pass 的结果(此处即RenderPass渲染出的原始场景画面);
  • deltaTime:距上一帧的时间差(秒);
  • maskActive:当前是否处于遮罩(mask)激活状态。

.setSize( width, height )

调整 Pass 尺寸(源码)。它会同步三个内部 RenderTarget 的尺寸,并把新的尺寸写入各 shader 的sizeuniform,同时刷新相机投影矩阵相关 uniform。EffectComposer在窗口尺寸变化时会自动调用它,但你也可以在 composer.setSize 中随分辨率手动触发。

.dispose()

释放该实例分配的 GPU 相关资源(源码)。依次释放三个 RenderTarget、五个材质(normalMaterial、saoMaterial、vBlurMaterial、hBlurMaterial、materialCopy)以及fsQuad。当 Pass 不再使用时务必调用,避免显存泄漏。覆盖自 Pass#dispose。

六、内部工作原理:多阶段渲染管线

从源码实现可以还原出SAOPass.render()的完整执行流水线,整个算法被拆解为若干个全屏或场景渲染阶段:

  1. (可选)直通上一 Pass 结果:若renderToScreen为true,先用无混合的materialCopy将readBuffer纹理直接输出到屏幕,保证直通预览与叠加效果一致(SAOPass.js)。
  2. 参数上传与同步:把params.saoBias、saoIntensity、saoScale、saoKernelRadius、saoMinResolution写入saoMaterial的 uniforms,把相机 near/far 传入 SAO 材质与模糊材质;深度截止值按saoBlurDepthCutoff × (camera.far − camera.near)换算为视空间绝对深度差;当saoBlurRadius/saoBlurStdDev变化时通过BlurShaderUtils.configure()重新生成高斯权重与采样偏移(SAOPass.js,权重生成公式见 DepthLimitedBlurShader.js)。
  3. 渲染法线与深度:使用MeshNormalMaterial作为scene.overrideMaterial重渲染整个场景到normalRenderTarget,配合DepthStencilFormat/UnsignedInt248Type的DepthTexture一并得到法线与深度(内部方法_renderOverride,SAOPass.js)。这一阶段是后续 AO 估计的数据来源。
  4. SAO 主计算:将场景法线贴图与深度贴图喂给SAOShader材质,渲染到saoRenderTarget(内部方法_renderPass,SAOPass.js)。
  5. 可选的深度限制模糊:当saoBlur为true时,先垂直(vBlurMaterial,方向(0,1))模糊到blurIntermediateRenderTarget,再水平(hBlurMaterial,方向(1,0))模糊回saoRenderTarget,即两次分离式高斯模糊。
  6. 结果合成:按params.output选择数据源(法线缓冲或 AO 缓冲),对Default模式使用基于DstColorFactor/ZeroFactor的自定义混合把 AO 因子"乘以"到readBuffer上(混合相关设置见 SAOPass.js),其他输出模式则用NoBlending直接覆盖,最终写入readBuffer或屏幕。

需要指出:深度限制模糊(Depth Limited Blur)是保证 SAO 质量的关键——普通高斯模糊会跨越深度不连续处产生边缘渗色(bleeding),而 DepthLimitedBlurShader.js 在采样时一旦发现相邻样本视空间深度差超过depthCutoff便停止累积权重,从而保护边缘轮廓。

七、完整可运行示例

综合examples/webgl_postprocessing_sao.html的官方用法,组装一套含 RenderPass → SAOPass → OutputPass 的完整管线如下:

import * as THREE from 'three'; import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { SAOPass } from 'three/addons/postprocessing/SAOPass.js'; import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; // 1. 基础场景 const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 3, 10 ); camera.position.z = 7; // 2. EffectComposer 管线:RenderPass 先把场景画进缓冲 const composer = new EffectComposer( renderer ); composer.addPass( new RenderPass( scene, camera ) ); // 3. 挂载 SAOPass const saoPass = new SAOPass( scene, camera ); composer.addPass( saoPass ); // 4. 最后输出到屏幕(负责正确的颜色空间转换与色调映射) composer.addPass( new OutputPass() ); // 5. 调参:对象密集的小尺度场景建议适当调低 saoScale、调高 saoIntensity saoPass.params.output = SAOPass.OUTPUT.Default; saoPass.params.saoBias = 0.5; saoPass.params.saoIntensity = 0.3; saoPass.params.saoScale = 2; saoPass.params.saoKernelRadius = 100; saoPass.params.saoBlurRadius = 8; saoPass.params.saoBlurStdDev = 4; // 6. 逐帧渲染 renderer.setAnimationLoop( () => composer.render() );

官方示例场景为 120 个随机分布的小球体组成的球簇(见examples/webgl_postprocessing_sao.html),适合直观观察球体间接触处的 AO 阴影变化。调试时可借助 GUI 把output切到SAOPass.OUTPUT.SAO(纯 AO 图)或SAOPass.OUTPUT.Normal(法线图)查看中间结果。

窗口尺寸变化时需同步更新:

window.addEventListener( 'resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); composer.setSize( window.innerWidth, window.innerHeight ); } );

八、调参与资源管理实践建议

参数调优要点(来自源码与官方示例的 GUI 区间):

  • saoScale需要与场景尺度适配:源码在 shader 中以scale / cameraFar归一化(SAOShader.js),场景偏大时应调高该值,否则 AO 范围会显得过小。
  • saoIntensity控制在0.05 ~ 0.5之间可获得自然效果;过大时接触处会发黑死寂。
  • 出现噪点/闪烁时优先增大saoBlurRadius与saoBlurStdDev,或微调saoBias抑制自遮蔽;而saoBlurDepthCutoff过大会让模糊跨越深度边缘导致"边缘发虚",该值默认0.01已是较稳妥的起点。
  • AO 分辨率resolution默认256×256,属于较保守的性能取向;对高端设备可提高到512×512提升质量,同时观察帧率预算。

资源释放:

当 SAOPass 被移除或应用销毁时,调用saoPass.dispose()释放其内部三个 RenderTarget 与全部材质、全屏四边形资源。

深度缓冲兼容性说明:

SAOPass 内部创建独立深度纹理(DepthStencilFormat+UnsignedInt248Type),不从场景现有深度缓冲复用,因此在需要反转深度缓冲(reversed depth buffer)的渲染器配置下仍可正常工作(shader 内通过USE_REVERSED_DEPTH_BUFFER宏适配,见 SAOShader.js)。该 Pass 仅适用于 WebGL 渲染器(基于Pass/FullScreenQuad体系),WebGPU 渲染器需使用对应的 TSL 后处理实现。

九、相关文件索引

  • 类实现:examples/jsm/postprocessing/SAOPass.js
  • 核心 AO 着色器:examples/jsm/shaders/SAOShader.js
  • 深度限制模糊着色器与权重生成工具:examples/jsm/shaders/DepthLimitedBlurShader.js
  • 后处理基类Pass与FullScreenQuad:examples/jsm/postprocessing/Pass.js
  • 官方示例 Demo:examples/webgl_postprocessing_sao.html
  • 同族对比实现(SSAO):examples/jsm/postprocessing/SSAOPass.js
  • API 文档页:docs/pages/SAOPass.html.md

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询