在实时渲染中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

Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF的更多相关文章

  1. Unity3d 基于物理渲染Physically-Based Rendering之最终篇

    前情提要: 讲求基本算法 Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF plus篇 Unity3d 基于物理渲染Physically-B ...

  2. unity3d 基于物理渲染的问题解决

    最近1个月做了unity 次世代开发的一些程序方面的支持工作,当然也是基于物理渲染相关的,主要还是skyshop marmoset的使用吧,他算是unity4.x版本 PBR的优秀方案之一了但在使用以 ...

  3. PBR:基于物理的渲染(Physically Based Rendering)+理论相关

    一: 关于能量守恒 出射光线的能量永远不能超过入射光线的能量(发光面除外).如图示我们可以看到,随着粗糙度的上升镜面反射区域的会增加,但是镜面反射的亮度却会下降.如果不管反射轮廓的大小而让每个像素的镜 ...

  4. Unity3d 基于物理渲染Physically-Based Rendering之实现

    根据前文的例子http://blog.csdn.net/wolf96/article/details/44172243(不弄超链接了审核太慢)弄一下真正的基于物理的渲染逃了节课= =,弄了一下.公式和 ...

  5. 基于物理渲染的渲染器Tiberius计划

    既然决定实现一个光栅化软件渲染器,我又萌生了一个念头:实现一个基于物理渲染的渲染器.

  6. Physically Based Rendering

    Microfacet Models for Refraction through Rough Surfaces 这篇论文...名字被我忘记了 找了好久...之前存电脑里的 ggx beckmann 找 ...

  7. Thinking in Unity3D:基于物理着色(PBS)的材质系统

    关于<Thinking in Unity3D> 笔者在研究和使用Unity3D的过程中,获得了一些Unity3D方面的信息,同时也感叹Unity3D设计之精妙.不得不说,笔者最近几年的引擎 ...

  8. Physically Based Render in Game 序

    基于物理渲的渲染理论,从SIGGRAPH06被Naty Hoffman等人提出后,近年来也越来越多的被各大游戏公司所采用,几乎已经是次世代游戏的标准特性,也是每个3D游戏工作者所必备的知识,尽管每年S ...

  9. Physically Based Shader Development for Unity 2017 Develop Custom Lighting Systems (Claudia Doppioslash 著)

    http://www.doppioslash.com/ https://github.com/Apress/physically-based-shader-dev-for-unity-2017 Par ...

随机推荐

  1. Photoshop CS4序列号过期的问题

    1)在网络上搜寻一些PS CS4序列号: 如1330-1221-6824-4838-0308-6823,1330-1283-7461-4574-7002-2504,1330-1795-2880-537 ...

  2. 234. Palindrome Linked List

    题目: Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) t ...

  3. NSMutableString

    /*可变字符串,注意NSMutableString是NSString子类*/ //注意虽然initWithCapacity分配字符串大小,但是不是绝对的不可以超过此范围,声明此变量对 性能有好处 NS ...

  4. C++:虚函数的引入

    5.4虚函数5.4.1 虚函数的引入 //例5.19 虚函数的引例 #include<iostream> using namespace std; class MyBase{ //声明基类 ...

  5. swift:高级运算符(位运算符、溢出运算符、优先级和结合性、运算符重载函数)

    swift:高级运算符 http://www.cocoachina.com/ios/20140612/8794.html 除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和 ...

  6. python 有关矩阵行列的存取 np.array

    初始化 a = range() a = np.array(a) a = a.reshape(,) a [[ 0  1  2  3]  [ 4  5  6  7]  [ 8  9 10 11]  [12 ...

  7. 10个提供免费PHP脚本下载的网站

    本文将重点介绍10个PHP脚本的免费资源下载站.之前推荐 <16个下载超酷脚本的热门网站>,这些网站除了PHP脚本,还有JavaScript.Java.Perl.ASP等脚本.如果你已是脚 ...

  8. Parallel WebDriver executions using TestNG

    In this post, we will see how does one make use of TestNG to kick off parallel UI tests using WebDri ...

  9. svn is already under version control问题解决

    svn ci 时出现 xx is already under version control,然后无法提交,出现这个问题的原因是你所提交的文件或目录是其他SVN的东西,即下面有.svn的目录,需要先把 ...

  10. bzoj3931: [CQOI2015]网络吞吐量

    将最短路图找出来,跑maxflow即可.有注意到数据范围.然后输出的时候%dWA了三次QAQ... #include<cstdio> #include<cstring> #in ...