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 材质贴图_第1张图片
    第二个: 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是日本公司真的挨个材质拿仪器量测出来的,可以说相当准。
BRDF 材质贴图_第2张图片

第二个:MERL 100里面材质坐标对应关系。
这里要高度注意!,x轴表示thedaH的角度,而不是余弦值。y轴表示thedaD的角度,而不是余弦值!!!
BRDF 材质贴图_第3张图片

第三个:怎么用程序实现。这里可坑死我了。。。

首先就遇到了,thedaH大于90度的情况(见下图),这怎么处理?! 是全部给成黑色值?还是材质本身的diffuse值?亦或是BRDF材质的值?
搞成黑色的肯定不行,太蛇了。材质本身的diffuse值?感觉在光暗边界的地方又会有比较大的变化,看起来不自然。那么就只能用BRDF材质的值了,既然决定了用BRDF材质的值,那么用里面的哪个值呢?思来想去,用thedaH接近90度的是比较好的。

BRDF 材质贴图_第4张图片
图里面可以看到,从球面右侧的点到左侧的点,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 材质贴图_第5张图片
BRDF texture
BRDF 材质贴图_第6张图片

你可能感兴趣的:(Shader-学习)