【光照】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開始探索游戲渲染】專欄-直達
(歡迎點贊留言探討,更多人加入進來能更加完善這個探索的過程,??)

本文探討Unity URP中屏幕空間環境光遮蔽(SSAO)技術的實現。SSAO通過計算像素周圍幾何體的遮擋關系增強場景真實感,其發展經歷了傳統算法、優化改進和URP集成三個階段。核心技術包括深度紋理采樣、法線重建、遮蔽計算和雙邊濾波處理。URP通過ScreenSpaceAmbientOcclusion渲染特性實現,關鍵步驟涉及視圖空間坐標轉換、法線半球采樣和旋轉噪聲紋理優化。文章詳細解析了采樣階段、遮蔽計算和模糊處理的實現原理,并提供了移動端優化建議和參數配置示例,幫助開發者理解并應用這一提升場景真實感的重
浙公網安備 33010602011771號