屏幕空间的动态全局光照(漫反射)
我們知道全局光照是圖形學中一項比較難實現的技術,因為真正要得到全局光照是需要一個點跟所有點的關系計算的,而這個計算如果用在計算機那是不太可能實現的。而現代技術利用了一些比如有向距離場,體素,輻照度等方式實現全局光照,還有就是越來越火的光纖追蹤技術實現全局光照。但是這些技術的消耗都很大,包括內存,cpu,gpu的運算量。那么自然會有一種ss系列的方式來實現全局光照,這種方式會比前面幾種都容易實現并且運算量相對小。
屏幕空間的全局光照也叫SSGI,是一個相對來說比較快速得到間接光的一種全局光照技術,是基于屏幕空間大小來實現的,也就是說不會得到屏幕外的顏色或者光照。他也不是基于光源的,而是基于當前的顏色來做擾動采樣整合得到的光照信息,最后需要做一些抗鋸齒的操作來讓他噪點比較合理。這種方式更合理的使用在密閉空間內,這樣的效果會比較理想。
SSGI一般會有幾個步驟:
1.采樣不同lod的深度圖信息并得到其中最大的深度值。(要執行多少次尋找就看你設置多少次pass)
2.用光線步進的方式,并用噪音圖采樣以圓的周長來找周圍的像素點的顏色,這個顏色是根據當前場景顏色做的(所以ssgi一般放到最后處理)。采樣的點存儲到rt上
3.這時的rt是帶有比較明顯的鋸齒的rt,需要用taa方式過濾一次
4.最后在橫向和縱向做兩次模糊處理。
5.這樣就得到了ssgi的漫反射方向的間接光照了,最后只需要把場景圖和這個間接光rtcombina一下就好了。
?
大體實現:
1.獲取深度圖信息的核心代碼是
float2 uv = i.uv.xy;half4 minDepth = half4(_SSGi_HierarchicalDepth_RT.SampleLevel( sampler_SSGi_HierarchicalDepth_RT, uv, _SSGi_HiZ_PrevDepthLevel, int2(-1.0,-1.0) ).r,_SSGi_HierarchicalDepth_RT.SampleLevel( sampler_SSGi_HierarchicalDepth_RT, uv, _SSGi_HiZ_PrevDepthLevel, int2(-1.0, 1.0) ).r,_SSGi_HierarchicalDepth_RT.SampleLevel( sampler_SSGi_HierarchicalDepth_RT, uv, _SSGi_HiZ_PrevDepthLevel, int2(1.0, -1.0) ).r,_SSGi_HierarchicalDepth_RT.SampleLevel( sampler_SSGi_HierarchicalDepth_RT, uv, _SSGi_HiZ_PrevDepthLevel, int2(1.0, 1.0) ).r);return max( max(minDepth.r, minDepth.g), max(minDepth.b, minDepth.a) );采樣上下左右四個方向的深度獲取最深的。
2.光線步進采樣周圍像素點的核心代碼(有注釋):
float2 UV = i.uv.xy;//當前像素深度float SceneDepth = tex2Dlod(_CameraDepthTexture, float4(UV, 0, 0)).r;//轉成攝像機方向的線性深度float EyeDepth = LinearEyeDepth(SceneDepth);//float LinearDepth = Linear01Depth(SceneDepth);half Roughness = clamp(1 - tex2D(_CameraGBufferTexture1, UV).a, 0.02, 1);float3 WorldNormal = tex2D(_CameraGBufferTexture2, UV) * 2 - 1;float3 ViewNormal = mul((float3x3)(_SSGi_WorldToCameraMatrix), WorldNormal);//攝像機位置,z軸表示深度float3 ScreenPos = GetScreenSpacePos(UV, SceneDepth);//通過攝像機的逆矩陣轉換到世界坐標float3 WorldPos = GetWorldSpacePos(ScreenPos, _SSGi_InverseViewProjectionMatrix);//通過投影矩陣的逆矩陣轉換到攝像機空間float3 ViewPos = GetViewSpacePos(ScreenPos, _SSGi_InverseProjectionMatrix);//攝像機空間方向float3 ViewDir = GetViewDir(WorldPos, ViewPos);//基于世界法線建立切線空間float3x3 TangentBasis = GetTangentBasis( WorldNormal );//uint3 p1 = Rand3DPCG16( uint3( (float)0xffba * abs(WorldPos) ) );//uint2 p = (uint2(UV * 3) ^ 0xa3c75a5cu) ^ (p1.xy);half Out_Mask = 0;half3 Out_Color = 0;[loop]for (uint i = 0; i < (uint)_SSGi_NumRays; i++){//uint3 Random = Rand3DPCG16( int3( p, ReverseBits32(i) ) );//half2 Hash = float2(Random.xy ^ Random.z) / 0xffffu; //根據擾動圖片采樣來獲取周邊的像素和深度half2 Hash = tex2Dlod(_SSGi_Noise, half4((UV + sin( i + _SSGi_Jitter.zw )) * _SSGi_RayCastSize.xy / _SSGi_NoiseSize.xy, 0, 0)).xy;float3 L;//基于圓偏移的點L.xy = UniformSampleDiskConcentric( Hash );L.z = sqrt( 1 - dot( L.xy, L.xy ) );//世界空間float3 World_L = mul( L, TangentBasis );//轉攝像機空間float3 View_L = mul((float3x3)(_SSGi_WorldToCameraMatrix), World_L);float3 rayStart = float3(UV, ScreenPos.z);//對方向最一些偏移,也是基于圓做偏移float4 rayProj = mul ( _SSGi_ProjectionMatrix, float4(ViewPos + View_L, 1.0) );float3 rayDir = normalize( (rayProj.xyz / rayProj.w) - ScreenPos);rayDir.xy *= 0.5;//找偏移附近的uvfloat4 RayHitData = Hierarchical_Z_Trace(_SSGi_HiZ_MaxLevel, _SSGi_HiZ_StartLevel, _SSGi_HiZ_StopLevel, _SSGi_NumSteps_HiZ, _SSGi_Thickness, 1 / _SSGi_RayCastSize.xy, rayStart, rayDir, _SSGi_HierarchicalDepth_RT, sampler_SSGi_HierarchicalDepth_RT);//根據偏移采樣場景顏色float3 SampleColor = tex2Dlod(_SSGi_SceneColor_RT, half4(RayHitData.xy, 0, 0));float4 SampleNormal = tex2Dlod(_CameraGBufferTexture2, half4(RayHitData.xy, 0, 0)) * 2 - 1;float Occlusion = 1 - saturate( dot(World_L, SampleNormal) ); SampleColor *= Occlusion;SampleColor *= rcp( 1 + Luminance(SampleColor) );Out_Color += SampleColor;//對場景大小的遮罩決定顯示范圍Out_Mask += Square( RayHitData.a * GetScreenFadeBord(RayHitData.xy, _SSGi_ScreenFade) );}Out_Color /= _SSGi_NumRays;Out_Color *= rcp( 1 - Luminance(Out_Color) );Out_Mask /= _SSGi_NumRays;//顏色用附近采樣的顏色,透明度用深度決定[branch]if(_SSGi_MaskRay == 1) {return half4( Out_Color * saturate(Out_Mask * 2), EyeDepth );} else {return half4( Out_Color, EyeDepth );}3.做一次TAA,主要是獲取aabb的裁剪盒子,然后得到最小顏色,最大顏色,然后clamp進行過渡,再融合上一幀的顏色和當前幀的顏色。
half2 UV = i.uv.xy;half3 WorldNormal = tex2D(_CameraGBufferTexture2, UV).rgb * 2 - 1;half2 Velocity = tex2D(_CameraMotionVectorsTexture, UV);/Get AABB ClipBoxhalf SS_Indirect_Variance = 0;half4 SS_Indirect_CurrColor = 0;half4 SS_Indirect_MinColor, SS_Indirect_MaxColor;ResolverAABB(_SSGi_RayCastRT, 0, 10, _SSGi_TemporalScale, UV, _SSGi_ScreenSize.xy, SS_Indirect_Variance, SS_Indirect_MinColor, SS_Indirect_MaxColor, SS_Indirect_CurrColor);/Clamp TemporalColorhalf4 SS_Indirect_PrevColor = tex2D(_SSGi_TemporalPrev_RT, UV - Velocity);SS_Indirect_PrevColor = clamp(SS_Indirect_PrevColor, SS_Indirect_MinColor, SS_Indirect_MaxColor);/Combine TemporalColorhalf Temporal_BlendWeight = saturate(_SSGi_TemporalWeight * (1 - length(Velocity) * 2));half4 SS_IndirectColor = lerp(SS_Indirect_CurrColor, SS_Indirect_PrevColor, Temporal_BlendWeight);return SS_IndirectColor;4.做橫向和縱向兩次模糊(blur里面會對顏色和深度都做模糊處理,顏色直接用相加然后除于相加的數量得到,深度需要用2的n次冪來得到一條曲線,下面的CrossBilateralWeight就是深度的過濾方式):
float4 Bilateralfilter_X(PixelInput i) : SV_Target {half2 UV = i.uv.xy;const float Radius = 12.0;return BilateralBlur( Radius, UV, half2(1.0 / _SSGi_ScreenSize.x, 0), _SSGi_TemporalPrev_RT ); }float4 Bilateralfilter_Y(PixelInput i) : SV_Target {half2 UV = i.uv.xy;const float Radius = 12.0;return BilateralBlur( Radius, UV, half2(0, 1.0 / _SSGi_ScreenSize.y), _SSGi_TemporalPrev_RT ); }float CrossBilateralWeight(float BLUR_RADIUS, float r, float Depth, float originDepth) {const float BlurSigma = BLUR_RADIUS * 0.5;const float BlurFalloff = 1.0 / (2.0 * BlurSigma * BlurSigma);float dz = (originDepth - Depth) * _ProjectionParams.z * 0.25;return exp2(-r * r * BlurFalloff - dz * dz); }5.合并就簡單了,就是用當前場景顏色和上面算出來的間接光照相加。
half2 UV = i.uv.xy;half3 BaseColor = tex2D(_CameraGBufferTexture0, UV);half3 SceneColor = tex2D(_SSGi_SceneColor_RT, UV);half3 IndirectIrradiance = tex2D(_SSGi_Bilateral_RT, UV) * _SSGi_GiIntensity;return (IndirectIrradiance * BaseColor) + SceneColor ;總結:ssgi的diffuse方式實現簡單,得到的結果也不錯,但是他并不是正確的,是一個模擬周圍顏色的過程,當你要得到更好的結果時需要擴大獲取深度和顏色擾動的范圍,同樣gpu的壓力也會增大。但他確是一種實時的全局光照方式。當然如果我們只需要一些靜態或可控的動態區域做全局光照,那么用烘焙和球諧光照就可以了。
?
?
?
總結
以上是生活随笔為你收集整理的屏幕空间的动态全局光照(漫反射)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020美赛F奖论文(三):足球团队指标
- 下一篇: 线性调频信号与脉冲压缩