【光照】UnityURP[屏幕空间环境光遮蔽SSAO]原理剖析实践

news/2025/10/17 12:43:47/文章来源:https://www.cnblogs.com/SmalBox/p/19147548

【从UnityURP开始探索游戏渲染】专栏-直达

SSAO(Screen Space Ambient Occlusion,屏幕空间环境光遮蔽)是Unity URP中用于模拟物体间环境光遮蔽效果的技术,通过计算像素周围几何体的遮挡关系增强场景深度感和真实感。

技术发展进程

  • 早期阶段‌:传统SSAO算法如Crytek SSAO(2007年)采用半球采样和深度比较,计算开销较大。
  • 优化阶段‌:2010年后出现基于屏幕空间法线的改进算法(如HBAO),减少采样噪声。
  • URP集成‌:Unity 2019年将SSAO作为URP核心后处理效果,支持移动端优化实现。

核心原理

  • 采样阶段‌:在屏幕空间随机采样当前像素周围的深度值,计算遮挡系数。
  • 遮蔽计算‌:通过比较采样点与当前像素的深度差,确定光线被阻挡的概率。
  • 模糊处理‌:使用双边滤波消除噪声,保留边缘细节。

URP实现机制

URP通过ScreenSpaceAmbientOcclusion渲染特性实现:

原理示例

  1. 像素P的深度值为Dp,法线为Np
  2. 在半球范围内随机采样点S(深度Ds)
  3. 若(Ds > Dp)则累计遮蔽值
  4. 最终遮蔽强度 = 1 - (可见采样点/总采样点)

采样阶段

深度纹理获取‌:

  • 通过_CameraDepthTexture获取屏幕空间深度值,将像素坐标转换为视图空间位置

  • 深度纹理获取原理

    在URP中,_CameraDepthTexture通过以下方式生成:

    • 深度图来源‌:URP会在渲染不透明物体后通过CopyDepth Pass将场景深度复制到_CameraDepthTexture,若无法直接复制则启用PreDepthPass逐物体渲染深度
    • 纹理声明‌:需在Shader中正确定义TEXTURE2D(_CameraDepthTexture)或引用URP内置头文件DeclareDepthTexture.hlsl
    • 采样方式‌:使用SAMPLE_TEXTURE2D函数结合屏幕UV坐标获取非线性深度值,需通过Linear01Depth()转换为[0,1]范围的线性深度
  • 像素坐标到视图空间转换流程

    • 屏幕坐标计算‌:

      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)/半径)‌
  • 技术实现细节

    • 深度纹理采样‌:需在URP中启用DepthTextureMode.Depth,通过SAMPLE_DEPTH_TEXTURE获取非线性深度‌
    • 坐标转换‌:屏幕坐标需通过ComputeScreenPos()转换为NDC坐标,再通过_CameraInvProjectionMatrix还原视图空间位置‌
    • 性能优化‌:移动端建议使用SampleSceneDepth()封装方法,避免平台兼容性问题‌

随机采样‌:

  • 在法线半球内生成随机向量(如64个采样点),通过旋转噪声纹理避免带状伪影
  • 伪影(Artifacts)的成因
    • 在屏幕空间环境光遮蔽(SSAO)等算法中,‌带状伪影‌(Banding Artifacts)通常表现为采样点分布不均匀导致的明暗条纹。这种伪影主要由以下原因引起:
      • 固定采样模式‌:当使用固定网格或规则模式生成采样点时,相邻像素的采样方向过于相似,导致光照计算出现周期性重复‌
      • 噪声纹理重复‌:直接使用静态噪声纹理(如Perlin噪声)作为采样方向偏移时,纹理的周期性重复会放大采样点的规律性‌
  • 旋转噪声纹理的解决方案
    • 通过旋转噪声纹理避免伪影的核心原理是‌破坏采样方向的周期性‌,具体实现方式如下:
      • 动态旋转噪声

        • 为每个像素生成唯一的旋转角度(如基于UV坐标或随机种子)
        • 将噪声纹理的UV坐标与旋转矩阵相乘,使噪声图案在空间上动态变化‌
        hlsl
        // 示例:基于UV坐标的旋转
        float2 rotatedUV = mul(noiseUV, rotationMatrix);
        float noiseValue = tex2D(_NoiseTex, rotatedUV);
        
      • 采样点分布优化

        • 在法线半球内生成随机向量时,将噪声值作为方向偏移量:

        随机向量s = normalize(noiseOffset × baseSampleDirection)

        • 通过旋转噪声纹理,使相邻像素的采样方向差异最大化,避免重复模式‌
      • 技术优势

        • 消除周期性‌:旋转后的噪声纹理在空间上无重复规律,破坏带状伪影的数学基础‌
        • 计算高效‌:仅需一次纹理采样和矩阵运算,适合实时渲染‌
  • 实现参考
    • URP中可通过以下步骤实现:
      • 定义噪声纹理_NoiseTex并设置_NoiseUVScale控制缩放
      • 在片元着色器中计算旋转后的噪声值,用于调整采样方向‌

示例

  • 对像素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开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/938826.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ai元人文:讨论一种新的决策科学

Ai元人文:讨论一种新的决策科学 岐金兰: 其实,问题的根源是人们狭隘的偏见, 更是,人们在工程化“价值”时忘记了, 或者是,有意无意地在回避, 在现实中,面对价值冲突时的真实场景和解决方法。 而且,在我们的构…

2025年流量计厂家权威推荐榜单:热式/模拟式/数字式/高压/高温/耐腐蚀/多气体/4-20mA/RS485/分体式/不锈钢高精度流量计公司精选

2025年流量计厂家权威推荐榜单:热式/模拟式/数字式/高压/高温/耐腐蚀/多气体/4-20mA/RS485/分体式/不锈钢高精度流量计公司精选在工业自动化与过程控制领域,流量计作为关键测量设备,其性能直接影响生产效率和产品质…

深入解析:云原生时代的数据流高速公路:深入解剖Apache Pulsar的架构设计哲学

深入解析:云原生时代的数据流高速公路:深入解剖Apache Pulsar的架构设计哲学pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fa…

DeepResearch代码浅析

DeepResearch代码浅析 概述 代码:DeepResearch 主要看一下inference下面的ReAct推理流程。 inference ├── eval_data │ ├── example_with_file.jsonl │ ├── example.jsonl │ └── file_corpus │…

2025年连铸机厂家权威推荐榜单:扇形段/大包回转台/钢包中间罐/结晶器总成/拉矫机/引锭杆/输送辊道/液压剪等核心部件专业供应商

2025年连铸机厂家权威推荐榜单:扇形段/大包回转台/钢包中间罐/结晶器总成/拉矫机/引锭杆/输送辊道/液压剪等核心部件专业供应商在钢铁工业持续转型升级的背景下,连铸机作为钢铁生产流程中的关键设备,其性能直接影响…

机器人控制利器:MPC入门与实践解析 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年10月中国专精特新申报服务机构推荐榜:五强实测

在选择中国专精特新申报服务机构时,关键在于找到最适合自身实际需求的解决方案。工信部中小企业局2024年度《优质中小企业梯度培育工作通报》显示,截至2024年6月,全国累计公告“专精特新小巨人”企业1.2万家,全年申…

静态初始化块的执行顺序

class Root { static { System.out.println("Roota"); } { System.out.println("Rootb"); } public Root() { System.out.println("Rootc"); } } class Mid extends Root { static { Sy…

正态总体中标准化单样本残差的分布推导

本文完整推导了从正态总体 $N(\mu, \sigma^2)$ 中抽取的样本中,单个标准化残差 $W = (X_1 - \bar{X}) / \sqrt{\sum(X_i - \bar{X})^2}$ 的概率密度函数。证明过程利用了赫尔默特变换与多元正态分布的旋转不变性,将 …

2025年轧钢设备厂家权威推荐榜:冷轧机、热轧机源头生产厂家,技术实力与市场口碑深度解析

2025年轧钢设备厂家权威推荐榜:冷轧机、热轧机源头生产厂家,技术实力与市场口碑深度解析行业背景与发展现状钢铁工业作为国民经济的重要支柱产业,其生产装备的技术水平直接关系到产品质量和生产效率。冷轧机与热轧机…

实用指南:在鸿蒙NEXT中发起HTTP网络请求:从入门到精通

实用指南:在鸿蒙NEXT中发起HTTP网络请求:从入门到精通pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&…

10.16 CSP-S 模拟赛总结

ContestT1 我很神秘。数据水理论复杂度 \(O(nk^2)\) 暴力过了。 实际上只要想到对 \(k\) 取模就会做了。因为满足要求的情况即为存在一段 \([l,r]\) 的子区间和对 \(k\) 取模为 \(0\),那么等价于两次前缀和对 \(k\) 取…

远程无钥匙进入(PKE)技术:便利与安全的完美融合

PKE 远程无钥匙进入 在智能汽车时代,远程无钥匙进入(Passive Keyless Entry,简称PKE)已成为提升用户体验的核心技术。它允许车主无需手动操作钥匙,只需携带智能钥匙靠近车辆,即可自动解锁车门或启动引擎。根据20…

灵动岛iPhone状态栏获得高度不对 iOS iPhone14pro iPhone14pro max状态栏获得高度不对

之前的获得状态栏高度代码是(CGFloat)statusRectHeight { if (@available(iOS 13.0, *)) { _statusRectHeight = [UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager.statusBarFram…

string略解

string 字符串类型,下标从0开始。 函数用法 定义 #include <iostream> #include <string>string s;初始化 string s="Hello,World!";sting a="Hello,"; string b="World!"…

《程序员修炼之道》 阅读笔记二

核心概念解读 “软件的熵”也被称为“破窗效应”。一个软件项目,如果出现了第一个糟糕的设计、第一个临时的脏代码、第一个没有修复的bug,而没有人去处理,它就会迅速腐烂。这就像一栋建筑有一扇破窗不被修理,很快就…

是时候告别向日葵、Todesk、TeamViewer了,快速搭建自托管服务器RustDesk

是时候告别向日葵、Todesk、TeamViewer了,快速搭建自托管服务器RustDesk最早我一直在用向日葵,但后来被限速,体验越来越差。远程操作,没必要花钱,于是开始找免费的替代方案。 后来试了ToDesk,刚开始感觉还行,但…

史馆

发言人 内容至“某些人”的一封信 https://www.luogu.com.cn/article/qdk2rgrt行啊,我没开完隐,你谴责我注册时间早,你来看看我注册之后都在干什么你拒绝是你的事,学校怎样做是学校的事,如果你来问我文化课我会拒…

firecrawl 私有部署(test)

firecrawl 私有部署(test) github: https://github.com/firecrawl/firecrawl 文档: https://docs.firecrawl.dev/introduction安装:git clone https://github.com/firecrawl/firecrawl.gitcd firecrawldocker co…

$\text{Catalan}$ 数 卡特兰数

定义公式 \(1\):\(C_n=\begin{pmatrix}2n\\n\end{pmatrix}-\begin{pmatrix}2n\\n-1\end{pmatrix}\)公式 \(2\):\(C_n=\sum_{a+b=n-1}C_aC_b\)公式 \(3\):\(C_n=\frac{4n-2}{n+1}C_{n-1},C_0=1\)其中公式 \(3\) 表明…