BRDF 材質貼圖
阿新 • • 發佈:2018-11-28
Segment 0 關鍵詞
- BRDF 材質,一種材質紋理,用來貼反射光的。
- PBS physical based shading, 基於物理成像演算法。
- microfacet model,一種表面模型,是diffuse加上反射,反射一般由微表面分佈函式D,反射係數函式F,自遮擋G組成。請注意這個模型是對每一個pixel都成立的。
- uv的界限。在Unity裡面,也不知道是我操作不好還是怎樣,一旦UV在0和1這兩個座標,採到的顏色就是中度灰。。。 貌似是開集?!
Segment 1 如何用BRDF 材質貼圖?
- Unity Demo shadow gun裡面有一段BRDF的敘述, 參見文章《ShadowGun shader 解析》
裡面的核心就是這幾個東西。
第一個: BRDF 材質
第二個: BRDF 材質取樣演算法,請注意取樣演算法和這個BRDF texture高度相關。下面的code是使用
halfLambert的NdotL作為x軸,NdotH做為Y軸。
這裡其實我完全不理解為何NdotH可以作為Y軸!!! 因為NdotH在camera可見的球面上是可以有負數的!!!
// Half vector
fixed3 halfDir = normalize (lightDir + viewDir);
// N.L
fixed NdotL = dot (s.Normal, lightDir);
// N.H
fixed NdotH = dot (s.Normal, halfDir);
// remap N.L from [-1..1] to [0..1]
// this way we can shade pixels facing away from the light - helps to simulate bounce lights
fixed biasNdotL = NdotL * 0.5 + 0.5;
// lookup light texture
// rgb = diffuse term
// a = specular term
fixed4 l = tex2D (_BRDFTex, fixed2(biasNdotL, NdotH));
- Disney BRDF 玩法,請參考s2012_pbs_disney_brdf_notes_v2.pdf
同樣的,裡面也有幾個重要的地方
第一個: BRDF 材質。這些個材質都是從MERL 100裡面抓的。MERL 100是日本公司真的挨個材質拿儀器量測出來的,可以說相當準。
第二個:MERL 100裡面材質座標對應關係。
這裡要高度注意!,x軸表示thedaH的角度,而不是餘弦值。y軸表示thedaD的角度,而不是餘弦值!!!
第三個:怎麼用程式實現。這裡可坑死我了。。。
首先就遇到了,thedaH大於90度的情況(見下圖),這怎麼處理?! 是全部給成黑色值?還是材質本身的diffuse值?亦或是BRDF材質的值?
搞成黑色的肯定不行,太蛇了。材質本身的diffuse值?感覺在光暗邊界的地方又會有比較大的變化,看起來不自然。那麼就只能用BRDF材質的值了,既然決定了用BRDF材質的值,那麼用裡面的哪個值呢?思來想去,用thedaH接近90度的是比較好的。
圖裡面可以看到,從球面右側的點到左側的點,thedaH的變化差不多是從45度到0度再到-135度。這裡就出現了負數的thedaH。實際上真實世界不存在負的thedaH的,因為要麼光被物體遮住了,要麼視線被物體遮住了。這也是為什麼在thedaH大於90度的時候我們可以用接近90度的值來模擬。
//歸一化halfvector
fixed3 halfVector = normalize( viewDir + lightDir );
//計算thedaH的反餘弦值。這裡千萬注意反餘弦求出來的範圍是[0,PI]!
//我之前一直以為是[-0.5PI, 0.5PI],血崩
//這裡要除以PI之後還要再乘以2的原因是,thedaH在90度的時候對應的值是1,而不是0.5
fixed thedaH = acos(dot(halfVector, s.Normal))/3.14*2;
fixed thedaD = acos(dot(lightDir, halfVector))/3.14*2;
//這裡有兩種方式處理背面的暗面,一種是用這種if語句判斷一下,把暗面都用BRDF texture裡面接近90度的來替換。
if(thedaH>0.99){
thedaH = 0.99;
}
fixed4 c;
c = tex2D(_BRDFTex, fixed2(thedaH, thedaD));
//這裡是第二種方式處理暗面,就是用diffuse NdotL的方法把暗面處理成黑的,同時在明暗過度的地方因為diffuse的餘弦
//值的緣故,會過度的很平滑。
//用乘法的原因是要混合一下,不能用加法。
//乘以atten的原因是需要用atten去做shadow receive
//min(8*max(0,dot(s.Normal, lightDir)),1)的目的是讓NdotL在明暗過度的時候激烈一些。怎麼操作的呢?
//就是讓NdotL這個曲線在小於0的時候等於0,在大於0的時候斜率變大8倍(這樣就會超出1了,所以還要在大於1的地方截斷一下)
c.rgb = s.Albedo * c.rgb * min(8*max(0,dot(s.Normal, lightDir)),1)*atten;
實際結果
BRDF texture