【光照】UnityURP[屏幕空间环境光遮蔽SSAO]原理剖析实践
【从UnityURP开始探索游戏渲染】专栏-直达
SSAO(Screen Space Ambient Occlusion,屏幕空间环境光遮蔽)是Unity URP中用于模拟物体间环境光遮蔽效果的技术,通过计算像素周围几何体的遮挡关系增强场景深度感和真实感。
技术发展进程
- 早期阶段:传统SSAO算法如Crytek SSAO(2007年)采用半球采样和深度比较,计算开销较大。
- 优化阶段:2010年后出现基于屏幕空间法线的改进算法(如HBAO),减少采样噪声。
- URP集成:Unity 2019年将SSAO作为URP核心后处理效果,支持移动端优化实现。
核心原理
- 采样阶段:在屏幕空间随机采样当前像素周围的深度值,计算遮挡系数。
- 遮蔽计算:通过比较采样点与当前像素的深度差,确定光线被阻挡的概率。
- 模糊处理:使用双边滤波消除噪声,保留边缘细节。
URP实现机制
URP通过ScreenSpaceAmbientOcclusion渲染特性实现:
原理示例
- 像素P的深度值为Dp,法线为Np
- 在半球范围内随机采样点S(深度Ds)
- 若(Ds > Dp)则累计遮蔽值
- 最终遮蔽强度 = 1 - (可见采样点/总采样点)
采样阶段
深度纹理获取:
通过
_CameraDepthTexture获取屏幕空间深度值,将像素坐标转换为视图空间位置深度纹理获取原理
在URP中,
_CameraDepthTexture通过以下方式生成:- 深度图来源:URP会在渲染不透明物体后通过CopyDepth Pass将场景深度复制到
_CameraDepthTexture,若无法直接复制则启用PreDepthPass逐物体渲染深度 - 纹理声明:需在Shader中正确定义
TEXTURE2D(_CameraDepthTexture)或引用URP内置头文件DeclareDepthTexture.hlsl - 采样方式:使用
SAMPLE_TEXTURE2D函数结合屏幕UV坐标获取非线性深度值,需通过Linear01Depth()转换为[0,1]范围的线性深度
- 深度图来源:URP会在渲染不透明物体后通过CopyDepth Pass将场景深度复制到
像素坐标到视图空间转换流程
屏幕坐标计算:
hlsl
float2 screenUV = i.positionCS.xy / _ScreenParams.xy; // 归一化到[0,1]范围
深度值解码:
hlsl
float depth = SAMPLE_TEXTURE2D(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV);
float linearDepth = Linear01Depth(depth, _ZBufferParams); // 转换为线性深度
视图空间重建:
方法一:通过摄像机射线插值
hlsl
float3 viewPos = _CameraInvProjectionMatrix * float4(screenUV * 2 - 1, linearDepth * 2 - 1, 1);
viewPos /= viewPos.w; // 透视除法
方法二:使用URP内置函数
hlsl
float3 viewPos = ComputeViewSpacePosition(screenUV, linearDepth); // 需包含Core.hlsl
坐标系统转换:
- 屏幕坐标 → NDC坐标 → 裁剪空间 → 视图空间,需注意不同API的NDC范围差异(OpenGL为[-1,1],DirectX为[0,1])
深度值特性:
- 原始深度值为非线性分布(透视投影),
Linear01Depth通过_ZBufferParams参数处理平台差异
- 原始深度值为非线性分布(透视投影),
性能优化:
- 移动端建议使用
SampleSceneDepth()封装方法,自动处理平台兼容性问题
- 移动端建议使用
完整实现可参考URP的ScreenSpaceAmbientOcclusion.hlsl文件,其中包含深度采样和空间转换的标准流程
法线重建:
利用深度差计算相邻像素的法线向量,形成法线半球采样空间
深度差计算
- 对当前像素的上下左右四个相邻像素进行深度采样,计算水平/垂直方向的高度差:
hlsl
float u = rightDepth - leftDepth; // 水平差分
float v = bottomDepth - topDepth; // 垂直差分
- 该过程通过
_CameraDepthTexture获取深度值,并转换为线性深度。
法线向量生成
- 将深度差转换为向量后叉乘得到法线:
hlsl
Vector3 vec_u = (1, 0, u); // 水平方向向量
Vector3 vec_v = (0, 1, v); // 垂直方向向量
Vector3 normal = cross(vec_u, vec_v); // 叉乘结果
最终法线需归一化处理,确保长度为1。
法线半球采样空间
- 以重建的法线为基准,在其半球空间内均匀分布采样点,用于环境光遮蔽计算。该空间满足:
- 采样方向与法线夹角≤90°(
max(0, N·S)) - 采样点深度差决定遮蔽强度(
1-saturate(Ds-Dp)/半径)
- 采样方向与法线夹角≤90°(
- 以重建的法线为基准,在其半球空间内均匀分布采样点,用于环境光遮蔽计算。该空间满足:
技术实现细节
- 深度纹理采样:需在URP中启用
DepthTextureMode.Depth,通过SAMPLE_DEPTH_TEXTURE获取非线性深度 - 坐标转换:屏幕坐标需通过
ComputeScreenPos()转换为NDC坐标,再通过_CameraInvProjectionMatrix还原视图空间位置 - 性能优化:移动端建议使用
SampleSceneDepth()封装方法,避免平台兼容性问题
- 深度纹理采样:需在URP中启用
随机采样:
- 在法线半球内生成随机向量(如64个采样点),通过旋转噪声纹理避免带状伪影
- 伪影(Artifacts)的成因
- 在屏幕空间环境光遮蔽(SSAO)等算法中,带状伪影(Banding Artifacts)通常表现为采样点分布不均匀导致的明暗条纹。这种伪影主要由以下原因引起:
- 固定采样模式:当使用固定网格或规则模式生成采样点时,相邻像素的采样方向过于相似,导致光照计算出现周期性重复
- 噪声纹理重复:直接使用静态噪声纹理(如Perlin噪声)作为采样方向偏移时,纹理的周期性重复会放大采样点的规律性
- 在屏幕空间环境光遮蔽(SSAO)等算法中,带状伪影(Banding Artifacts)通常表现为采样点分布不均匀导致的明暗条纹。这种伪影主要由以下原因引起:
- 旋转噪声纹理的解决方案
- 通过旋转噪声纹理避免伪影的核心原理是破坏采样方向的周期性,具体实现方式如下:
动态旋转噪声
- 为每个像素生成唯一的旋转角度(如基于UV坐标或随机种子)
- 将噪声纹理的UV坐标与旋转矩阵相乘,使噪声图案在空间上动态变化
hlsl
// 示例:基于UV坐标的旋转
float2 rotatedUV = mul(noiseUV, rotationMatrix);
float noiseValue = tex2D(_NoiseTex, rotatedUV);
采样点分布优化
- 在法线半球内生成随机向量时,将噪声值作为方向偏移量:
随机向量s = normalize(noiseOffset × baseSampleDirection)
- 通过旋转噪声纹理,使相邻像素的采样方向差异最大化,避免重复模式
技术优势
- 消除周期性:旋转后的噪声纹理在空间上无重复规律,破坏带状伪影的数学基础
- 计算高效:仅需一次纹理采样和矩阵运算,适合实时渲染
- 通过旋转噪声纹理避免伪影的核心原理是破坏采样方向的周期性,具体实现方式如下:
- 实现参考
- URP中可通过以下步骤实现:
- 定义噪声纹理
_NoiseTex并设置_NoiseUVScale控制缩放 - 在片元着色器中计算旋转后的噪声值,用于调整采样方向
- 定义噪声纹理
- URP中可通过以下步骤实现:
示例:
- 对像素P(深度1.0)生成采样点S(随机偏移0.2,0.3),转换后深度为1.15
遮蔽计算阶段
深度比较:
将采样点投影回屏幕空间,比较采样深度与原深度
若采样深度更近(如S深度0.9 < P深度1.0),累计遮蔽值
深度值还原:将采样的非线性深度值通过
Linear01Depth()函数转换为线性空间值(范围[0,1])关键代码:
hlsl
float sampledDepth = Linear01Depth(SAMPLE_TEXTURE2D(_CameraDepthTexture, sampler_CameraDepthTexture, uv), _ZBufferParams);
深度差计算:比较采样点深度与原像素深度,通常结合采样半径进行归一化处理
判定逻辑:
hlsl
if (sampledDepth < currentDepth)
occlusion += (currentDepth - sampledDepth) / _Radius;
边缘处理:当采样点超出屏幕边界时,默认按无遮挡处理或使用边缘像素值
在URP管线中,上述流程通过以下步骤完成:
- 使用
ComputeScreenPos()函数生成屏幕坐标 - 通过
SampleSceneDepth()方法采样深度纹理 - 最终比较采用视图空间Z值而非线性深度,避免透视畸变影响
- 使用
法线权重:
- 根据采样点与法线夹角衰减贡献,排除背面无效采样
公式:
遮蔽因子 = max(0, N·S) * (1-saturate(Ds-Dp)/半径)
- 法线权重部分(N·S)
- 点积运算:
N·S表示表面法线向量N与采样方向向量S的点积,用于评估采样点与法线的夹角关系 - 背面剔除:
max(0, N·S)确保只有法线半球内的采样点(夹角小于90°)产生贡献,排除背面无效采样示例:当N=(0,1,0)且S=(0.3,0.8,0)时,点积结果为0.8,有效权重为0.8
- 点积运算:
- 深度差部分(Ds-Dp)
- 深度比较:
Ds-Dp计算采样点深度与原像素深度的差值,正值表示采样点更近(产生遮挡) - 归一化处理:
saturate(Ds-Dp)将差值钳制在[0,1]范围,避免极端值干扰 - 半径归一化:除以采样半径
_Radius使结果与采样范围无关,保证不同尺度下的一致性
- 深度比较:
- 综合计算
- 遮蔽强度:
1-saturate(Ds-Dp)/半径将深度差转换为可见性系数(0完全遮蔽,1完全可见) - 最终乘积:法线权重与可见性系数相乘,实现物理正确的遮蔽衰减应用场景:当采样点S在法线后方(N·S<0)或未被遮挡(Ds>Dp)时,该点贡献为0
- 遮蔽强度:
该公式在URP中通过ScreenSpaceAmbientOcclusion.hlsl实现,核心代码段如下:
hlsl
float occlusion = 0;
for (int i = 0; i < _SampleCount; i++) {
float3 sampleDir = normalize(_Samples[i]);
float weight = max(dot(normal, sampleDir), 0);
float depthDiff = (SampleDepth(samplePos) - centerDepth) / _Radius;
occlusion += weight * (1 - saturate(depthDiff));
}
模糊处理阶段
- 双边滤波:结合空间距离和颜色差异保留边缘
- 水平/垂直两次高斯模糊,权重计算包含深度差阈值
- 降噪优化:使用低差异序列采样减少噪声,或通过Temporal AA累积帧数据
实际应用
代码说明:
动态启用URP后处理管线
配置SSAO强度、采样半径等关键参数
支持运行时参数调整
SSAOSetup.cs
using UnityEngine;
using UnityEngine.Rendering.Universal; public class SSAOSetup : MonoBehaviour {
void Start() {
var urpAsset = GraphicsSettings.renderPipelineAsset
as UniversalRenderPipelineAsset; // 启用SSAO后处理
urpAsset.GetComponent<UniversalAdditionalCameraData>()
.renderPostProcessing = true; // 参数配置示例
var ssao = ScriptableObject.CreateInstance<ScreenSpaceAmbientOcclusion>();
ssao.Intensity = 1.2f;
ssao.Radius = 0.3f;
ssao.SampleCount = 16;
}
}
使用建议
- 移动端优化:降低
SampleCount至8-12,使用Downsample模式。 - 美术控制:通过材质参数控制不同区域的遮蔽强度。
- 性能监控:在Profiler中观察
SSAO.Pass的耗时
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,)
【光照】UnityURP[屏幕空间环境光遮蔽SSAO]原理剖析实践的更多相关文章
- DirectX11 With Windows SDK--32 SSAO(屏幕空间环境光遮蔽)
前言 由于性能的限制,实时光照模型往往会忽略间接光因素(即场景中其他物体所反弹的光线).但在现实生活中,大部分光照其实是间接光.在第7章里面的光照方程里面引入了环境光项: \[C_a = \mathb ...
- 基于屏幕空间的实时全局光照(Real-time Global Illumination Based On Screen Space)
目录 Reflective Shadow Maps(RSM) RSM 的重要性采样 RSM 的应用与缺陷 Screen Space Ambient Occulsion(SSAO) SSAO Blur ...
- [帖子收集]环境光遮蔽(Ambient Occlusion)
环境光遮蔽,效果示例图 图片左边是一条龙的简单模型,呈现在一个均匀照明的环境中.尽管模型中有一些明暗不同的区域,但大部分光照都是均匀的.虽然模型有着相当复杂的几何形状,但看上去比较光滑平坦,没有明显的 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION)
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION) 学习目标 ...
- Cesium源码剖析---Ambient Occlusion(环境光遮蔽)
Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光 ...
- 在Unity中实现屏幕空间阴影(2)
参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...
- Unity3d 屏幕空间人体皮肤知觉渲染&次表面散射Screen-Space Perceptual Rendering & Subsurface Scattering of Human Skin
之前的人皮渲染相关 前篇1:unity3d Human skin real time rendering 真实模拟人皮实时渲染 前篇2:unity3d Human skin real time ren ...
- Unity3D 屏幕空间雪场景Shader渲染
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...
- U3D屏幕空间到世界空间变换
using UnityEngine; using System.Collections; public class FPSCam : MonoBehaviour { Vector3 lastPos; ...
- 在Unity中实现屏幕空间阴影(1)
接着上篇文章,我们实现了SSR效果. 其中的在屏幕空间进行光线追踪的方法是通用的.借此我们再实现一种屏幕空间的效果,即屏幕空间阴影. 文中的图片来自Catlike coding http://catl ...
随机推荐
- SciTech-Mathmatics - Analysis - FT + FFT + DFT 及其应用: FT(傅立叶变换) 、图像的 Magnitude Spectrum (幅度谱图)及 频率信息
SciTech-Mathmatics - Analysis - FT + FFT + DFT 及其应用: Abbreviations: FT: Fourier Transformation FFT: ...
- ICEE-Interface-SATA的数据与电源接口
**SATA 数据接口(7pins) SATA 电源接口(15pins4Sections: +12V, +5V, +3.3V, GND) ** Sata实物:
- javascript高级程序编程-学习笔记(基础)
1.js实现 js诞生:为了验证字段是否输出(前期) 1.1分类 Esmascript,Dom,Bom 语法,类型,语句,关键字,保留字,操作符,对象 2.script async 立即下载 char ...
- Javaweb的三大组件分别是:Servlet、Filter(过滤器)、Listener(监听器)。
https://blog.csdn.net/zyl1112/article/details/123502971 Javaweb的三大组件分别是:Servlet.Filter(过滤器).Listener ...
- sql优化谓词下推在join场景中的应用
本文分享自天翼云开发者社区<sql优化谓词下推在join场景中的应用>,作者:i****n 列裁剪(分区裁剪):将为查询的字段以及分区过滤,从而减少加载的数据量. Map端聚合配置:在ma ...
- C语言数据结构-链式栈
1.链式栈:链表+栈 栈顶指针作用:表示整个链表内存,充当链表表头 先进后出,链表:表头法插入 (1)先写好链表的东西 (2)之后写栈
- 如何在uni-app 平台快速实现一对一音视频通话应用
"一套代码,多端运行"是很多开发团队的梦想.ZEGO SDK基于uni-app跨平台框架支持iOS.Android.Windows.macOS.HarmonyOS.Web.小程序并 ...
- Origin2024中绘制堆积柱状图,直观展现各个指标数据!
如何在Origin中绘制堆积柱状图,如下,可以直观展现各个指标之间数值(或比例): origin2024中文版下载 操作步骤: 1.打开Origin2024软件,然后在Book1中输入如下示例数据 ...
- DELPHI + uniGUI 开发CentOS环境下的Apache模块遇到的问题
一直以来,用uniGUI做开发都比较顺手,但是缺点是这样做的只能在Windows环境下使用.而我们现在租用的虚拟服务器,往往都是CentOS的环境. 而DELPHI + uniGUI能不能做LINUX ...
- Bolt.new:AI驱动的Web开发神器,让创意闪电般实现!
字数:约3000字|预计阅读时间:10分钟 引言 在这个瞬息万变的数字时代,Web 开发已成为技术领域的核心.然而,传统的编程模式往往让开发者陷入繁琐的环境配置.框架选择.代码编写和部署的泥潭.这不仅 ...