關于間接高光的水平遮蔽(Horizon Occlusion)

Horizon Occlusion

第一次看到 Horizon Occlusion 是在讀 Lux Plus 源碼的時候。

Horizon Occlusion 是關于 間接高光水平遮蔽 計算,典型的代碼如下:

// Horizon Occlusion
#if defined (UNITY_PASS_FORWARDBASE)
    #if LUX_HORIZON_OCCLUSION
        gi.indirect.specular *= GetHorizonOcclusion(viewDir, s.Normal, s.worldNormalFace, HORIZON_FADE);    
    #endif
#endif

// Direct lighting uses the Lux BRDF
half4 c = Lux_BRDF1_PBS(s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir,
        // Deferred expects these inputs to be calculates up front, forward does not. So we simply fill the input struct with zeros.
        half3(0, 0, 0), 0, 0, 0, 0,
        nl,
        ndotlDiffuse,
        gi.light, gi.indirect, specularIntensity, s.Shadow);

c.rgb += UNITY_BRDF_GI (s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, s.Occlusion, gi);

在物理著色之前,先計算 間接高光的水平遮蔽,即這里的 GetHorizonOcclusion 函數(shù),函數(shù)就3行,代碼如下:

// Horizon Occlusion for Normal Mapped Reflections: http://marmosetco.tumblr.com/post/81245981087
float GetHorizonOcclusion(float3 V, float3 normalWS, float3 vertexNormal, float horizonFade)
{
    float3 R = reflect(-V, normalWS);
    float specularOcclusion = saturate(1.0 + horizonFade * dot(R, vertexNormal));
    // smooth it
    return specularOcclusion; // * specularOcclusion;
}

題外話,今天在讀 Advanced Terrain Grass 代碼的時候又看到了這個函數(shù),再一看作者,原來是同一個人,:)

作者 forst 有很多經(jīng)典的插件,確實讓我學到了很多,這里給他做一個廣告:forst的商店主頁

好,現(xiàn)在回歸主題,那么什么是 間接高光的水平遮蔽 呢?

原理和實現(xiàn)

關于 Horizon Occlusion,這篇文章 寫的很清楚了,這里再啰嗦一遍。

我們知道Unity 間接高光 的計算依賴 視線向量 相對 法線反射向量,反射向量 的計算代碼如下:

g.reflUVW   = reflect(-worldViewDir, Normal);

考慮一個完全的鏡面,我們不可能接收到鏡面下面的環(huán)境反光,但是當我們引入 法線貼圖 后,法線會偏轉,這個時候我們的反射射線可能會達到鏡面的下面,如下圖所示:

image

如果以這個反射射線去計算環(huán)境高光,就會 漏光 了,如下圖:

image

Horizon Occlusion 就是針對這種 漏光 的處理,加了 Horizon Occlusion 后效果如下:

image

下面看一下作者的計算方式:

float GetHorizonOcclusion(float3 V, float3 normalWS, float3 vertexNormal, float horizonFade)
{
    float3 R = reflect(-V, normalWS);
    float specularOcclusion = saturate(1.0 + horizonFade * dot(R, vertexNormal));
    // smooth it
    return specularOcclusion; // * specularOcclusion;
}

代碼中的 R 即前文提到的 視線向量 相對于 法線 的反射向量。

注意,這里的法線是經(jīng)過 法線貼圖 偏轉后的法線,可能會導致 反射向量 指向模型表面的下方。

這個時候我們還需要 頂點法線頂點法線 可以做為模型正確的面向參考。

我們把 反射向量頂點法線點乘,如果是一個負值,就表明反射向量指向了表面下方,這個時候,我們就需要做反射光遮蔽了。

作者這里給了一個參數(shù) horizonFade 來控制遮蔽強度,代碼還是很簡單的。

結尾

好了,Horizon Occlusion 就介紹到這里。

最后吹一下 Advanced Terrain Grass,代碼看得差不多了,還是有不少值得學習的,后面慢慢寫,先留圖一張:

image

個人主頁

本文的個人主頁鏈接:https://baddogzz.github.io/2020/01/10/Horizon-Occlusion/。

好了,拜拜。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容