之前的錯誤和欠缺
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修正,這個對最終畫面影響很大,不能不看