【URP】Unity[視差遮擋貼圖]原理剖析實踐
【從UnityURP開始探索游戲渲染】專欄-直達
視差遮擋貼圖(Parallax Occlusion Mapping, POM)介紹
視差遮擋貼圖是視差貼圖技術的高階實現,通過?光線步進(Raymarching)算法?精確計算視線與高度圖的交點,模擬復雜表面(如磚墻、巖石)的幾何遮擋效果。相比標準視差貼圖,POM能更真實地表現深度變化和自陰影,適用于高精度材質表現。
核心原理
- ?分層深度檢測?將視線方向在切線空間分解為多層(通常8-15層),逐層采樣高度圖,通過二分法逼*視線與表面的交點。
- ?動態采樣優化?根據視角與法線的夾角動態調整采樣層數(**行視角增加層數,垂直視角減少層數),*衡精度與性能。
- ?遮擋關系重建?通過交點處的UV偏移修正紋理采樣位置,模擬凹凸表面的光線遮擋效果。
Unity URP 實現示例與原理詳解
代碼關鍵點解析
-
?動態采樣層數?
- 通過
lerp(_MaxSamples, _MinSamples, saturate(dot(float3(0,0,1), viewDirTS)))實現視角自適應分層,優化性能。
- 通過
-
?光線步進循環?
- 循環比較當前層高度與采樣深度,找到首個交點區間,避免全精度遍歷。
-
?二分法優化?
- 在初步交點區間內使用
lerp插值計算精確UV,減少采樣次數。
- 在初步交點區間內使用
-
ParallaxOcclusionMapping.shader
Shader "Universal Render Pipeline/POM" { Properties { _MainTex("Albedo", 2D) = "white" {} _NormalMap("Normal Map", 2D) = "bump" {} _HeightMap("Height Map", 2D) = "white" {} _ParallaxScale("Height Scale", Range(0, 0.1)) = 0.05 _MinSamples("Min Samples", Int) = 8 _MaxSamples("Max Samples", Int) = 15 } SubShader { Tags { "RenderPipeline"="UniversalPipeline" } HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURE2D(_HeightMap); SAMPLER(sampler_HeightMap); float _ParallaxScale; int _MinSamples, _MaxSamples; float2 ParallaxOcclusionMapping(float3 viewDirTS, float2 uv) { // 動態計算采樣層數 int numSamples = (int)lerp(_MaxSamples, _MinSamples, saturate(dot(float3(0,0,1), viewDirTS))); float layerHeight = 1.0 / numSamples; float2 deltaUV = _ParallaxScale * viewDirTS.xy / viewDirTS.z / numSamples; // 光線步進初始化 float currentLayerHeight = 0; float2 currentUV = uv; float currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r; // 分層檢測 [loop] for (int i = 0; i < 15; ++i) { if (currentLayerHeight >= currentDepth) break; currentUV -= deltaUV; currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r; currentLayerHeight += layerHeight; } // 二分法精確交點 float2 prevUV = currentUV + deltaUV; float prevDepth = currentDepth - layerHeight; float weight = (currentLayerHeight - currentDepth) / (prevDepth - currentDepth); return lerp(currentUV, prevUV, weight); } ENDHLSL Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 viewDirTS : TEXCOORD1; }; Varyings vert(Attributes IN) { Varyings OUT; VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz); OUT.positionCS = posInput.positionCS; // 轉換視角方向到切線空間 VertexNormalInputs normInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS); float3 viewDirWS = GetWorldSpaceViewDir(posInput.positionWS); OUT.viewDirTS = TransformWorldToTangent(viewDirWS, normInput.tangentWS, normInput.bitangentWS, normInput.normalWS); OUT.uv = IN.uv; return OUT; } half4 frag(Varyings IN) : SV_Target { // 計算POM偏移后的UV float2 pomUV = ParallaxOcclusionMapping(normalize(IN.viewDirTS), IN.uv); // 采樣最終紋理 half4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, pomUV); half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, pomUV)); return half4(albedo.rgb, 1); } ENDHLSL } } }
技術對比與性能建議
| 特性 | 標準視差貼圖 | 陡峭視差貼圖 | POM |
|---|---|---|---|
| ?采樣次數? | 單次 | 5-15次 | 8-15次+二分法 |
| ?陡峭表面精度? | 低 | 中 | 高 |
| ?適用*臺? | 移動端 | PC/主機 | 高端PC |
| ?推薦參數? | Scale=0.02 |
Scale=0.05 |
Scale=0.03-0.07 |
實際應用中需注意:
- 高度圖建議使用法線貼圖的Alpha通道節省資源
- 過高的
_ParallaxScale可能導致邊緣拉伸,建議不超過0.1 - 移動端可減少
_MaxSamples至8層以降低開銷
【從UnityURP開始探索游戲渲染】專欄-直達
(歡迎點贊留言探討,更多人加入進來能更加完善這個探索的過程,??)

視差遮擋貼圖(POM)是一種高級渲染技術,通過光線步進算法精確計算視線與高度圖的交點,模擬復雜表面的幾何遮擋效果。相比標準視差貼圖,POM能更真實表現深度變化和自陰影,適用于高精度材質。其核心是分層深度檢測和動態采樣優化,根據視角動態調整采樣層數,并通過二分法逼近精確UV。Unity URP實現中,關鍵點包括動態采樣層數、光線步進循環和二分法優化。技術對比顯示POM精度最高但性能消耗較大,適合PC/主機平臺,移動端需減少采樣層數。建議高度圖使用法線貼圖的Alpha通道節省資源,控制_ParallaxScal
浙公網安備 33010602011771號