1. 程式人生 > >BRDF 材質貼圖

BRDF 材質貼圖

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
這裡寫圖片描述