PBR實現2.0


之前的錯誤和欠缺

1. 過於簡單的划分diffuse和specular,非常光滑的非金屬材料也是很能反光的
2. 費奈爾效應的處理,F0的選取也比較隨意
3. 沒有GI,更不支持AO

正確划分diffuse和spcular

之前我的PBR實現非金屬無論怎樣光滑都是沒有反光的,這顯然很不對。完全忽略了非金屬的反射

金屬性越高,反射率越高,分給Specular計算的Albedo越多。1.0的金屬會反射所有的光,也就沒有diffuse。但是非金屬不太一樣,0.0的非金屬也仍舊又一定的反射能力,這個就是電介質反射率需要參與計算的地方。

在UnityStandardUtils.cginc中可以找到下面的分離方法,實際上是將Matellic的數值remap到[DielectricSpec,1]的范圍。

inline half OneMinusReflectivityFromMetallic(half metallic)
{
  // We'll need oneMinusReflectivity, so
  // 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
  // store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
  //	1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) = 
  // = alpha - metallic * alpha
  half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
  return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}

inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
  specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
  oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
  return albedo * oneMinusReflectivity;
}

unity_ColorSpaceDielectricSpec里面存的是Unity選用的電介質反射率,alpha通道是1-dielectricSpec

Matellic:0 Gloss:0.5 的效果
)
從左至右:PBR1.0,Standard, PBR2.0, Standard

F0的選取

F0是入射角為0時的反射光,也就是分配給Specular計算的Albedo。之前的PBR實現實現會會整體偏亮就是因為Matellic肯定比正常的F0要大的多。


Matellic:0 Gloss:0.5 的效果


從左至右:PBR1.0,Standard, PBR2.0, Standard

過亮的問題看起來解決了,但是整體感覺更粗糙一些。

為什么我的shader顏色偏黑

這是Gloss:0,Matellic:0~1的對比圖。關閉了場景的環境光。第一行是CustomPBR,第二行是Standard

最早是發現經過之前兩個調整之后,整體顏色偏暗。經過幾個對比測試之后確定是Specular部分偏低。也就是上圖Matellic0.0和Matellic1.0對比在Diffuse為主的非金屬上顏色基本正常,只有Specular的金屬上色彩偏差最大。

這個問題讓我非常迷惑,在一些對比測試中將公式改為和Unity一致也沒有解決這個問題。進一步看到在Unity中對GammaSpace的Specular進行了一次開方修正。
同樣的在CustomPBR中進行處理之后效果就正常了。

對Gamma這塊我還不是很明白,搜了一下Gamma這個修正坑很深。以后再研究,要是哪位有相關的資料分享那就太好了。

沒有GI怎么行

沒有GI的情況下物體在場景里面實在是太突兀,先將Unity 的GI整合進來,學習一下Unity的GI。

UnityGI的處理

環境光照也是光照,也分為diffuse和specular。無論計算過程多么復雜,最后都是要得到環境光照的這兩個分量,然后和基本光照疊加。

Unity的疊加方式。這里是簡化說明,本體在UnityStandardBRDF.cginc中

//indirectDiffuse 就是GI的diffuse
diffuse = (indirectDiffuse + directDiffuse) * diffuesColor;

//directF:計算了Fresnel的specularColor
//indirectSpecular:GI的specular部分,包括各種Probe
//indirectF:計算了GI的Fresnel的SpecularColor
specular = lightColor * directSpecularTerm * directF + indirectSpecular * indirectSpecularTerm * indirectF;

整合UnityGI

會用到的struct在UnityLightingCommon.cginc里。
Unity的GI運算在UnityGlobalIllumination.cginc里。
我們要用的方法是這個

inline UnityGI UnityGlobalIllumination (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn)

我們所需要的間接diffuse 和間接specular就在UnityGI.indirect

需要作的也就是給這個方法湊參數,構造一個UnityGIInput。這個結構體的大部分都很直觀。

i_ambientOrLightmapUV
有lightmap的時候沒有ambientColor,沒有lightmap的時候只有ambientColor。這兩個肯定是互斥的。

box和probe
在Unity中會有兩個SpecCube生效,unity_SpecCube0,場景的反射源。unity_SpecCube1,當前的reflectionProbe,一起提供了IBL所需要的數據(其實應該是IBL的結果吧……待研究)


整合效果

Matellic:1.0 Gloss:0.7 Ambient:Red 在紅色方塊附近有一個ReflectionProbe

弓箭效果

下一步

1. 研究GI,看看能不能自己實現一個。現在GI方面有很多不明
2. Gamma修正,這個對最終畫面影響很大,不能不看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM