• Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF


    在实时渲染中Physically-Based Rendering(PBR)中文为基于物理的渲染
    它能为渲染的物体带来更真实的效果,而且能量守恒

     


     

    稍微解释一下字母的意思,为对后文的理解有帮助,
    从右到左
    L为光线方向,H为半角向量,L是和V的中间,N为法线方向,V为我们眼睛的观察方向(相机看的方向),R为反射方向
    Torrance-Sparrow光照模型的镜面反射公式
     
    D为法线分布函数(NDF)
    F为反射函数(Fresnel 函数)
    G为阴影遮罩函数(几何函数),未被shadow或mask的比例
    此处的E就是上面的V
    Cook-Torrance光照模型的镜面反射公式
     
    接下来我们只用Cook-Torrance光照来做实验

    通常来说BRDF是关于表面多种属性的反射结果之间的线性组合(在实时渲染中一般只考虑diffuse和specular两种即可)

    这是unity的specular

     
    看起来是不是特别像塑料?还是粗糙的塑料

    接下来我们就来讨论D函数的不同带来的specular的不同

    BlinnPhong的分布函数

    Call of Duty : black Ops 2/使命召唤:黑色行动2就使用了它
     
    在http://segmentfault.com/blog/wwt_warp/1190000000436286中说明
    Microfacet 微平面模型是广泛应用的对rough surface建模的工具, blinn,ward,beckmann都属于microfacet的推导结果。基本思想也很简单,就是用很小的微平面的组合去模拟粗糙的物体表面。而这 些微小的平面元则可以当做完美的反射或者折射表面。每个microfacet把一个入射方向的光反射到单独的一个出射方向,这取决于microfacet 的法向m。当计算BRDF的时候,光源方向l和视线方向v都得给定。这意味着在表面上的所有microfacet中,只有刚好把l反射到v的那部分对 BRDF有贡献。在下图中,我们可以看到这些有效microfacet的表面法向m正好在l和v的中间,也就是h。
     

     
    D 为法线分布函数(NDF),在大部分表面上,microfacet的方向不是均匀分布的。Microfacet的法线越接近宏表面的法线,就越光滑。这个 分布由microfacet的法线分布函数D(m)来定义。函数D()决定了specular高光的大小、亮度和形状。 法线分布函数一般有类似于“粗糙度”这样的参数。

    F为反射函数(Fresnel 函数),计算光学上的反射比率。
    分母4(n•l)(n•v)是个校正因子,用来校正从microfacet的局部空间转到整体表面的数量差异。
    V为能见度函数,阴影遮罩分类为透视缩减
    关于G ,
    http://www.klayge.org/wiki/index.php/%E5%9F%BA%E4%BA%8E%E7%89%A9%E7%90%86%E7%9A%84BRDF
    给出了解释
     
    G为阴影遮罩函数(几何函数),未被shadow或mask的比例
    在 上半部分,平的宏表面为绿色,粗糙的微观表面为蓝色。m = h的 microfacet标记为红色。宏表面投到视线方向就是左上角的绿线。同时,投出来的单个红色的microfacet显示成独立的红线。左下图表示在没 有遮挡项的情况下,红色的microfacet加起来的面积,结果就是有效面积大于总面积,所以BRDF的反射能量错误地大于了接收能量。右下图里红色区 域考虑了遮挡,重叠的区域不再计算多次,所以有效面积小于总面积。
     
    α为Specular的强度,Specular的强度是根据光泽度gloss确定的

    Schlick提出的fresnel方法:
     
     
    rf0是反射颜色,也是roughness粗糙程度
     
    遮挡项使用了Schlick-Smith提出的方法
     

    处于能量守恒考虑,漫反射多了镜面反射就要少,反之亦然
    所以:
     
     

    效果如下:
     

    看起来很像抛光的大理石效果吧


    主要代码如下:

    插入代码:

    #define PIE 3.1415926535	
    		float4 frag(v2f i) :COLOR
    		{
    			float3 viewDir = normalize(i.viewDir);
    			float3 lightDir = normalize(i.lightDir);
    			float3 H = normalize(lightDir + viewDir);
    			float3 N = normalize(i.normal);
    
    			float d = (_SP + 2) / 8 * pow(dot(N, H), _SP) / 4 * PIE;
    			float f = _SC + (1 - _SC)*pow((1 - dot(H, lightDir)), 5);
    			float k = 2 / sqrt(PIE * (_SP + 2));
    			float v = 1 / ((dot(N, lightDir)*(1 - k) + k)*(dot(N, viewDir)*(1 - k) + k));
    
    			float all = d*f*v;
    
    			float3 c = tex2D(_MainTex, i.uv_MainTex);
    						float3 diff = dot(lightDir, N);
    						diff = (1 - all)*diff;
    		return float4(c *(diff+all), 1)  * _LightColor0;
    		}


    _SC    为specular color,_SP 为specular power

    Phong的分布函数

     
    为D值(NDF)
    cosθ值为N与H的点积,以下皆是如此,H为半角向量,也就是light direct光线方向和view direct视角的一半。α即为本函数的specular power
     
    这是不同specular power的实现曲线,越高代表约粗糙,越低代表约光滑
    效果也很不错
     
    核心代码如下:

    		float d = (_SP + 2) / 8 * pow(dot(N, H), _SP)/(2*PIE);

    Beckmann的分布函数

     

    secθ为1/cosθ;

    曲线如下
     
    本曲线为α值为0.75时,局部出现了0的0次方,这与phong不同,使得粗糙表面的统一分部可行
     
    如果超过了0.75就会出现上面这种不该出现的情况,局部出现了近似0的最小值
     

    这是Beckmann和phong的对比(粉色为Beckmann,蓝色为phong)


    左面的图是关于粗糙表面的曲线,两者都差不多,对于右侧光滑的表面,两者相差很多。
    效果如下:
     

    主要代码如下:

    	                float cosT = dot(N, H);
    			float secT = 1 / cosT;
    			float d = pow(E, -((1 - pow(cosT, 2))*pow(secT, 4) / _AB))*pow(secT, 4) / (PIE*pow(_AB, 2));




    Trowbridge-Reitz(GGX)的分布函数

     

    为实现方程

    曲线如下:
     
    左侧是参数值较低的情况,右侧是参数值较高的情况
    左侧有点像Beckmann(越高越粗糙),右侧也是像Beckmann出现了最粗糙的地方
     

    这是与phong的比较


    他们两个差不多,但是Trowbridge-Reitz高的地方比较尖,矮的地方拖尾较长
    与phong的最终比较。
     

    效果如下:


     
    个人感觉这是效果第二好的方法

    核心代码如下:

    				float cosT = dot(N, H);
    				float d = pow(_SP, 2) / (PIE *pow((1 + (-1 + pow(_SP, 2))*pow(cosT, 2)), 2));



    Shifted Gamma Distribution的分布函数

     
    没错,就是这么长,
    alpha和gamma都是外部可控变量
     
    效果如下:
     
    个人认为效果最好的方法,可控变量多,出来的效果多,但是感觉计算起来很消耗啊
    代码如下:

    #define E 2.71828
    			float4 frag(v2f i) :COLOR
    			{
    				float3 viewDir = normalize(i.viewDir);
    				float3 lightDir = normalize(i.lightDir);
    				float3 H = normalize(lightDir + viewDir);
    				float3 N = normalize(i.normal);
    				float cosT = dot(H, N);
    				float secT = 1 / cosT;
    				float d = (pow(E, -(_SP*_SP + (1 - cosT * cosT)*secT *secT) / _SP))* pow(_SP, -1 + _TP)*pow(secT, 4)
    					*pow(_SP*_SP + (1 - cosT *cosT)*secT *secT, -_TP)
    					/ (PIE * _GM);
    
    				float f = _SC + (1 - _SC)*pow((1 - dot(H, lightDir)), 5);
    				float k = 2 / sqrt(PIE * (_SP + 2));
    				float v = 1 / ((dot(N, lightDir)*(1 - k) + k)*(dot(N, viewDir)*(1 - k) + k));
    
    				float all = d*f*v;
    
    				float3 c = tex2D(_MainTex, i.uv_MainTex);
    				float3 diff = dot(lightDir, N);
    				diff = (1 - all)*diff;
    				return float4(c *(diff + all), 1) * _LightColor0;
    			}





    最后放上一个全家福

     

    参考:

    1. Mathematica Notebook for the SIGGRAPH 2013 talk “ Background: Physics and Math of Shading”

    2. PhysicallyBased Lighting in Call of Duty: Black Ops

     

                                                      ----- by wolf96


  • 相关阅读:
    使用POI读写Word doc文件
    用纯css改变下拉列表select框的默认样式
    深入探究JFreeChart
    arcgis api for javascript中使用proxy.jsp
    【Itext】7步制作Itext5页眉页脚pdf实现第几页共几页
    iText5报表_页眉与页脚
    JFreeChart柱状图单组柱子的不同颜色显示
    如何自定义FusionCharts图表上的工具提示?
    span 文本内容超过宽度自动换行
    JS正则表达式验证账号、手机号、电话和邮箱
  • 原文地址:https://www.cnblogs.com/zhanlang96/p/4325409.html
Copyright © 2020-2023  润新知