Unreal的SSR交點(diǎn)檢測(cè)
關(guān)于 屏幕空間反射骡澈,可以參考文章很多锅纺,我覺(jué)得這篇寫(xiě)得還是蠻好的:Screen Space Glossy Reflections,借個(gè)圖用用:
本文不考慮 粗糙度肋殴,也不考慮 多條反射射線囤锉,只借鑒一下 Unreal 引擎在處理 單條射線光線步進(jìn) 時(shí)對(duì) 交點(diǎn) 的計(jì)算方式,用來(lái)改進(jìn)我的插件 LWRP/URP SSR Water 的反射表現(xiàn)护锤。
直接看下面這段最簡(jiǎn)單的 光線步進(jìn) 代碼片段:
#if SCALAR_BRANCHLESS
float MinHitTime = 1;
float LastDiff = 0;
float SampleTime = StepOffset * Step + Step;
UNROLL
for( int i = 0; i < NumSteps; i++ )
{
float3 SampleUVz = RayStartUVz + RayStepUVz * SampleTime;
// Use lower res for farther samples
float Level = Roughness * (i * 4.0 / NumSteps) + HZB_LEVEL_OFFSET;
float SampleDepth = Texture.SampleLevel( Sampler, SampleUVz.xy, Level ).r;
float DepthDiff = SampleUVz.z - SampleDepth;
bool Hit = abs( DepthDiff + CompareTolerance ) < CompareTolerance;
// Find more accurate hit using line segment intersection
float TimeLerp = saturate( LastDiff / (LastDiff - DepthDiff) );
float IntersectTime = SampleTime + TimeLerp * Step - Step;
float HitTime = Hit ? IntersectTime : 1;
MinHitTime = min( MinHitTime, HitTime );
LastDiff = DepthDiff;
SampleTime += Step;
}
float3 HitUVz = RayStartUVz + RayStepUVz * MinHitTime;
Result = float4( HitUVz, MinHitTime );
這里有一句比較有意思的注釋:
Find more accurate hit using line segment intersection
在判斷出射線和場(chǎng)景相交后官地,Unreal 并不著急返回 當(dāng)前射線終點(diǎn) 對(duì)應(yīng)的 屏幕坐標(biāo),而是根據(jù)上一段射線終點(diǎn)和當(dāng)前射線終點(diǎn)相對(duì)于場(chǎng)景深度的偏移 插值 出一個(gè)更加準(zhǔn)確的 屏幕坐標(biāo)烙懦。
有點(diǎn)繞口驱入,畫(huà)個(gè)圖就明了了:
上圖顯示的是射線深度剛超過(guò)場(chǎng)景深度時(shí)的情形,圖中 CurrentDiff 是正數(shù)氯析,LastDiff 是負(fù)數(shù)亏较,如果考慮正負(fù)號(hào),則交點(diǎn)的屏幕坐標(biāo)計(jì)算公式如下:
HitScreenUV = lerp(LastScreenUV, CurrentScreenUV, -LastDiff / (CurrentDiff - LastDiff)))
上面代碼中的 LastScreenUV 即上一段射線終點(diǎn)對(duì)應(yīng)的 屏幕坐標(biāo)掩缓,CurrentScreenUV 即當(dāng)前射線終點(diǎn)對(duì)應(yīng)的 屏幕坐標(biāo)雪情。
把 -LastDiff / (CurrentDiff - LastDiff) 的分子分母都 乘以-1 即:
HitScreenUV = lerp(LastScreenUV, CurrentScreenUV, LastDiff / (LastDiff - CurrentDiff)))
這樣就和 Unreal 的代碼對(duì)應(yīng)上了。
效果對(duì)比
我在 LWRP/URP SSR Water 的光線步進(jìn)交點(diǎn)計(jì)算中并沒(méi)有上面的 插值 操作你辣,而是判斷出相交后直接返回當(dāng)前射線終點(diǎn)對(duì)應(yīng)的屏幕坐標(biāo)巡通。
配合 抖動(dòng),在 采樣Step數(shù) 和 屏幕分辨率 比較高時(shí)舍哄,這樣的做法表現(xiàn)其實(shí)也還不錯(cuò)宴凉。
不過(guò),當(dāng)我把分辨率調(diào)到 1200 x 600 時(shí)表悬,之前的表現(xiàn)就一般般了跪解,如下圖:
添加插值后,還是 1200 x 600 的分辨率,表現(xiàn)就好多了:
如果把分辨率提到 2160 x 1080 這種常見(jiàn)的手機(jī)分辨率叉讥,表現(xiàn)就更好了:
做為一個(gè)水的shader窘行,這樣就差不多了,因?yàn)榧由纤y后图仓,一切都是浮云:
個(gè)人主頁(yè)
本文的個(gè)人主頁(yè)鏈接:https://baddogzz.github.io/2020/03/06/Accurate-Hit/罐盔。
好了,拜拜救崔!