【RTR4笔记】 第九章 基于物理的着色

PBR, BRDF

Posted Kongouuu's Blog on February 11, 2022

Physically Based Shading

9.3 BRDF

我们在根据物理去进行着色的时候,考虑到的是每个像素点所接受到的光,也就是:

\[L_i(c,-v)\\ c:摄像机位置\\ v:通过像素的射线方向\]

这时候我们也做了一个假设,那就是空气本身是完全干净的。那么我们所得到的入射光的值,将是物体朝着我们射线方向的出射光的强度:

\[L_i(c,-v) = L_o(p,v)\\ p: 射线和物体的交点\]

所以说我们这里最注重的部分就是物体怎么反射光到我们的摄像机。这时候被引入的概念就是BRDF(Bidirectional Reflectance Distribution Function), 一般用 f(l,v) 表示, l为入射光方向,v为出射光方向。也就是说BRDF就是用来计算单个方向的入射光有多少能够反射到我们的目标出射方向。

有了BRDF来计算能反射多少光,我们就可以通过下面的反射方程算出一个点会往我们的摄像机打多少光:

\[L_o(p,v) = \int_{l\in\Omega}f(l,v)L_i(p,l)(n\cdot l)dl\\ 为了更简单的显示,可以忽略掉点p\\ L_o(v)=\int_{l\in\Omega}f(l,v)L_i(l)(n\cdot l)dl\\\]
  1. f(l,v): BRDF,计算多少光被反射到目标方向
  2. Li(p,l): 当前点接受的入射光强
  3. n · l: 也就是cos(θ),用于根据入射光和法线的夹角修正接收的入射光强。由于光打到平面时如果没有平行于法线,并不能把所有能量到传到平面上,所以有这个量来处理对应情况的光强衰减。
  4. 积分Ω: 这个就是指积分于半球范围内,我们会考虑一个点/平面在一个半球范围内接受到的所有的光。

因为我们所有的方向向量都是可以把他拆分成θΦ来形容的,因此我们的可以把反射方程写成下面的形式:

\[L_o(\theta _o, \phi _o)=\int^{2\pi}_{\phi_i=0}\int^{\pi/2}_{\theta_i=0}f(\theta _i, \phi _i,\theta _o, \phi _o)L_i(\theta _i, \phi _i)\cos{\theta_i}\sin{\theta_i}d\theta_i d\phi_i\\\]

如果我们想要知道使用的 BRDF 对能量守恒造成了什么影响,我们可以假设全范围的入射光强度都为 1 。然后去计算一个方向的入射光往所有方向打的能量总合。我印象里 Kulla Conty 补偿中有用到这个手法:

\[R(l)=\int_{v\in\Omega}f(l,v)(n\cdot v)dv\\\]

同理,我们也可以去计算所有方向的光往同一个方向打的总合:

\[R(v)=\int_{v\in\Omega}f(l,v)(n\cdot l)dl\\\]

### 9.3.1 Lambertian BRDF

兰伯特的BRDF是最简单的一个BRDF,主要是在模拟一个很简单的次表面散射的情况(简单的漫反射)。它的公式如下:

\[f_{lambert}(l,v)=\frac{c_{diff}}{\pi}\]
  1. cdiff: 物体本身的漫反射颜色,也就是材质的一部分。
  2. 分母的π: 半球范围内的cosθ积分的结果就是π, 这里的BRDF的意思就是反射光会平均的往所有方向去。

我们把这个BRDF带入到能量守恒的方程R(l)中会得到:

\[R(l)=\int_{v\in\Omega}f(l,v)(n\cdot v)dv\\\\ =\pi f(l,v) = c_{diff}\]

也就是说我们反射的能量将会是材质的颜色,是个非常简单的一个模型。

9.4 光照 Illumination

我们在考虑了BRDF之后,还需要决定一下Li,也就是光强的输入。 在我们的渲染方程中,原本考虑了整个半球范围的光源输入去做积分来算总合,但这一章节还不会接触到全局光照。我们这里考虑的是有限的光源,并且都是没有面积的,因此也不会用到积分,不过计算起来也非常的简单:

\[L_o(v)=\sum^{n}_{i=1}f(l_i,v)\ c_i\ (n\cdot l_i)dl\\ c_i:第i个光源的颜色/强度\]

这样算出来的值在大部分时候会偏小,不过更加的物理正确。如果我们要同时考虑到全局光照的影响的话,一般都是使用上面的方法去计算局部光源的光照值。不过书中因为只考虑局部光照,给出了一个较为不真实的方案:

\[L_o(v)=\pi\sum^{n}_{i=1}f(l_i,v)\ c_i\ (n\cdot l_i)dl\\\]

书中在我们的计算中多乘以了一个π。原因是因为我们本身的渲染方程的前提是全方位都有光,在这种局部的模型中会得出一个非常暗的计算结果。为了在有限光源的前提下得出一个较为能看的结果,这里把单个光源的能量乘了π当作全方向的光源输入。

9.5 菲涅尔反射 Fresnel Reflectance

在上面的部分中介绍了一些比较基础的概念,例如BRDF 和反射/渲染方程。之前提到的兰伯特BRDF只是里面的一种,为了得到一个更真实的物理效果我们需要去研究更多的物理现象来得出更真实的 BRDF 来渲染。这里主要更多还是在讲高光的部分。

菲涅尔效应是说在不同的角度下,光的反射率会不一样,因此我们需要去模拟这样的效果来达到更真实的渲染。

9.5.1 外反射

外反射我们可以假定为光透过空气打到其它的物体表面上时发生的反射。这时候我们可以把反射本身写成一个函数 F(θ) ,有以下性质:

  1. 当我们的光和法线的夹角为0时, F(θ)将会是一个写死的高光颜色固定值。
  2. 当我们夹角逐渐增加的时候, F(θ)的值也会跟着增加;在夹角为最大值90的时候, F(θ)=1

实际上菲涅尔的反射本身是特别复杂的,还包括着物体的折射度等考量。不过还是有一个近似的方法,Schlick 近似

\[F(\theta) = F_0+(1-F_0)(1-(\cos{\theta}))^5\\ or\\ F(n,l)= F_0+(1-F_0)(1-(n\cdot l))^5\\\]

F0为我们手动设定的一个特征高光颜色。这里我们可以用很多已知的数字,也可以手动根据折射率去计算:

\[F_0=(\frac{n-1}{n+1})^2\]

9.5.2 典型的菲涅尔反射值

  • 绝缘体: 0.06或更低
  • 金属:0.5或更高
  • 半导体:0.2到0.45
  • 水:((n1-n2)/(n1+n2))^2

一般来说我们的渲染模型会有一个高光值 F0 ,以及漫反射颜色P

我们在输入上一般会提供两个参数,颜色以及金属度

如果金属度为1,那么 F0 为输入颜色, P为黑。

如果金属度为0,那么F0就是绝缘体常数(比如0.06), P为输入颜色

9.5.3 内反射

内反射一般来说我们不会在渲染中遇到。因为我们是在形容光从半透明物体打到空气时的效果。如果要去计算一个内反射的效果,那么我们需要知道折射的方向,或者折射度,这在实时渲染中是偏昂贵的。

9.6 微观几何 Microgeometry

在一个基础的模型上我们一个像素内指向的表面可能并不是个平面,而是较为复杂的一个表面。例如可能会凹凸不平,导致实际上会有多个且分散的法线方向。当然这是和表面粗糙度有关的,如果很粗糙那么对环境的反射会比较模糊,并且高光也会比较朦胧。大部分时候我们的发现分布都会是各向同性的,也就是说是旋转对称的(围绕原始法线各方向对称)。

除了法线分布以外,还有一个很大的问题就是自遮蔽。就是说如果我们的表面是粗糙的,那光线很有可能会打不到一部分的面积上,因为被突出的部分遮蔽。

不过被遮蔽的同时还有另一个效果,就是说如果入射角度和法线接近,那么我们的光会砸到所有粗糙的平面,造成复杂的反射情况。如果入射角度接近90,那么光无法抵达任何位于下面的凹槽,而是会在光滑的顶部反射,形成一个比较光滑的结果:

还注意的是当凸起的部分遮蔽一些凹槽的时候,光的能量是不会消失的,而是会散射出去,可能到其他表面也可能没有。

9.7 微表面理论 Microfacet Theory

我们在理论上探讨过微观几何之后,就要讨论到怎么去把他放到渲染力。现在有很多的思路运用 微表面理论 去调整 BRDF模型 来达到类似的效果。一般来说我们会考虑用一个表面法线 m 去帮助定义整个微表面的 BRDF:

\[micro-BRDF:\ f_\mu(l,v,m)\]

整个微表面 BRDF 是范围比较广的一个理论,也就是说它其实是个大类,里面有很多不同的 BRDF 模型。大部分时候我们都会使用 高光微表面BRDF 来形容物体的反射现象以及高光。不过也有很多其他的选择,比如漫反射的或是衍射的微表面 BRDF 模型。

9.7.1 法线分布 D(m)

微表面模型的一个很大的关键在于如何去形容法线分布。在微表面模型里,一个表面会有多个分散的法线,并且我们射入的光因此也都会输出至向不同的方向。所以我们需要计算的就是, 有多少比例的微表面法线指向我们的目标法线m。我们会用一个法线分布函数(NDF) D(m) 来形容这个分布。

法线分布函数 D(m)有一个特性:

\[\int_{m\in \Omega}D(m)dm = 微表面面积总和\]

但我们终究是在宏观的表面上去计算,所以我们需要把结果投影到宏观的表面上,并且同时做归一化处理:

\[\int_{m\in \Omega}D(m)(n\cdot m)dm =1\]

这实际上是对我们D(m)函数的一个限制。我们的函数必须是用来形容我们所有拥有法线m的微表面的面积比例。也就是说我们实际上要得到的数字是:

\[D(m)=\frac{法线m的微表面面积}{总微表面面积}\]

但是我们并不知道微表面面积为多少,但我们可以假设宏观表面的面积为 1。要实际绘制出来就是像下方的图这样:

在宏观表面的面积为1的时候,我们的微表面总面积肯定是 大于1 的。不过当我们把所有的微表面投影到宏观表面上,那么面积就会变回 1。这里实际上在做的就是确保我们的微表面总面积在投影到宏观表面的时候,面积可以归一化到1, 具体投影就是乘以 (n · m)

不过渲染一般会考虑到视线方向v,当我们宏观的把一片面积到视线方向的平面的时候,我们知道如果法线方向平面面积为 1,那么视线方向平面则是 (n · v)。因此我们的微表面法线分布的限制也可以写成下面的格式:

\[\int_{m\in \Omega}D(m)(v\cdot m)dm = n \cdot v\]

总结一下就是:

  1. D(m) 可以算出一个微表面有多少比例的面积朝向 m
  2. 微表面的面积是大于宏观表面的面积的
  3. 通过使用微表面法线点乘宏观法线,可以得到所有微表面面积投影到宏观表面上时的面积
  4. 所有微表面加一起投影到宏观表面上的面积为 1 (用于计算百分比)

9.7.2 几何遮蔽 G(m,v)

我们的光照模型一般来说都是考虑到摄像机看到了什么。在微表面模型下,会有很多的微表面因为被遮蔽导致我们摄像机会无法看到,因此会降低我们摄像机接收到的能量。因此我们使用了一个函数 G(m,v) 来形容有多少比例的法线为m的微表面是我们看得到的。并且遮蔽函数要满足下面的特性:

\[\int_{m\in \Omega}G_1(m,v)D(m)(v\cdot m)dm = v \cdot n\]

这里稍微解释一下特性的意义:

  1. G(m,v):代表法线为m的微表面有多少比例是可见的
  2. G(m,v) D(m):可见法线分布,代表有多少法线为m的表面,并且其中有多少可见性
  3. (v · m):这里是投影到视线平面

简单来说就是限制了一下,在所有的法线方向加起来的可见的发现分布要等于微观的可见度。

一般来说我们会使用Smith G来做几何遮蔽函数:

\[G_1(m,v)=\frac{\chi^+(m\cdot v)}{1+\Lambda(v)}\\ \chi^+(x)=1, where\ x>0\\ \chi^+(x)=0, where\ x\le0\\ 广义来说:G_1(m,v)=\frac{1}{1+\Lambda(v)}\]

而里面的Lambda函数则是要根据我们选择的NDF来推导。

9.7.3 宏观BRDF

假设我们有了微观的BRDF fμ, 法线分布,和几何遮蔽,我们就可以推出宏观的BRDF:

\[f(l,v)=\int_{m\in \Omega}f_\mu(l,v,m)G_2(l,v,m)D(m)\frac{m \cdot l}{|n \cdot l|}\frac{m \cdot v}{|n \cdot v|}dm\]
关于G2

我们上面提到的法线分布和几何遮蔽的影响,都是考虑的对视线的影响。比如说遮蔽在视线方向对光强的衰减这样子,不过我们摄像机收到的光它并不是从物体直接发出来的,而是物体反射光源过来的能量。也就是说,所有的遮蔽的影响,都会在光进去微表面的时候产生一次影响,出来的时候再产生一次影响,所以我们会得出一个新的遮蔽函数G2

\[G_2(l,v,m)=G_1(v,m)G_1(l,m)\]

这是最简单的写法,事实上有特别多不同更复杂的G2的表达方式

关于后面两个分数

我们前面的部分的推到得出了例如这样的等式:

\[\int_{m\in \Omega}G_1(m,v)D(m)(v\cdot m)dm = v \cdot n\\\]

就是说我们如果要给视线方向的遮蔽做限制,最后的积分的和会是 (v · n) 的值。如果我们要考虑到的是一个比例,而不希望单次计算的范围处于 [0,(v · n)]这个区间,那么我们就要把点乘移到左边作为分母:

\[\frac{m \cdot v}{|n \cdot v|}\]

这里目前提到的所有东西都是处于概念的,例如宏观BRDF:

\[f(l,v)=\int_{m\in \Omega}f_\mu(l,v,m)G_2(l,v,m)D(m)\frac{m \cdot l}{|n \cdot l|}\frac{m \cdot v}{|n \cdot v|}dm\]

这里面的任何函数都没有进行一个实际的编写,因为光照模型是很复杂的,我们在考虑次表面折射以及反射的时候是完全不同的计算方式。因此微表面模型这里实际上是提供了一个较大的框架,然后细节实现是根据情况去决定的。

9.8 表面反射的 BRDF模型

我们比较经常用到微表面模型的地方是在做表面反射的高光。高光这里我们要考虑的是光的反射和我们视线方向有多接近,所以一般使用半程向量h:

\[h=\frac{l+v}{||l+v||}\]

在高光上我们基本上是不需要考虑到任何微表面法线不等于半程向量的情况,也就是说我们并不需要考虑所有的微表面方向的情况,更好的可以去模拟这个反射的BRDF:

\[只考虑m=h\\ f_{spec}(l,v)=\frac{F(h,l)G_2(l,v,h)D(h)}{4|n\cdot l||n\cdot v|}\]

关于分母为什么是4,大致上是我们把微元看成h后造成的结果:Earl Hammon: PBR Diffuse Lighting for GGX+Smith Microsurfaces, GDC 2017.

9.8.1 法线分布函数

形状不变性

在详细的讨论不同的 NDF 前,要说一下书中提到的一个概念,那就是 形状不变性(Shape-Invariant)

形状不变性代表着我们的粗糙度的降低会拉伸我们的表面,而不是改变表面形状。实际上在渲染中的作用如下:

  1. 可以更好的推出各项异性的函数。我们一般都会用各项同性的分布,也就是没有方向性的分布。不过在特殊的材质,例如头发上,我们可能会希望微表面分布有方向性,所以会有这个需求。
  2. 有形状不变性,才能更简单的推导出遮蔽项 Smith G
  3. 可以推导重要性采样

抄自:【基于物理的渲染(PBR)白皮书】(四)法线分布函数相关总结

输入

一般来说我们的法线分布函数的输入为 [0,1]α,代表粗糙粗。

9.8.1.1 Blinn-Phong NDF

这是一个比较早期的NDF函数,计算起来较为便宜,但是现在已经渐渐的被替代。它不具有形状不变性

它的公式如下:

\[D(m)=\frac{a_p+2}{2\pi}(n\cdot m)^{a_p}\]

要注意的是这里的粗糙度值 α 跟我们平时使用的并不在一个区间。一般来说,我们会使用一个映射来让平时的[0,1]区间的α 转换到这个模型的值:

\[a_p=2a^{-2}-2\]

会被淘汰的原因是因为它本身效果并没有特别的好,加上并不具备形状不变性所以除了跑得快以外并没有太大的优势。

9.8.1.2 Beckmann

这是一个我们现在也会经常用到的法线分布,并且具有形状不变性。

公式如下:

\[D(m)=\frac{1}{\pi a^2(n\cdot m)^4}exp(\frac{(n\cdot m)^2-1}{a^2(n\cdot m)^2})\]

9.8.1.3 GGX (Trowbridge-Reitz)

GGX 算是现在最经常倍使用费分布函数了。它也具有形状不变性,主要的优势在于它的分布曲线在边缘两侧并没有跟Beckmann一样截断,使得高光边缘更加平滑:

公式如下:

\[D(m)=\frac{a^2}{\pi((n\cdot m)^2(a^2-1)+1)^2}\]

一般来说使用GGX的话我们可以得出一个Lambda函数:

\[\Lambda(a)=\frac{-1+\sqrt{1+\frac{1}}}{2}\]

不过对于我们Shader来说,算根号其实并没有很便宜,因此也被提出了一个近似的几何遮蔽函数:

\[G_1(s) \approx \frac{2(n\cdot s)}{(n \cdot s)(2-a)+a}\\\]

这里的s可以是光线向量l或是视线向量v

9.8.1.4 GTR

BeckmannGGX,我们把分布的尾部进行了延长来达到不一样的效果。不过GGX本身还只是固定的分布形状,不过只要对其进行一定的修改就可以得到一个可控的分布形状函数了,那就是GTR。不过GTR他并不是形状不变的,所以也是推出过后的三年才成功的有它的几何遮蔽函数被提出。

GTR公式如下:

\[D(m)=\frac{c}{(1+(n\cdot m)^2(a^2-1))^\gamma}\]

γ为调整我们形状的额外参数,当γ2的时候,GGX = GTR

c是根据我们的粗糙度以及γ去推出来的一个常数,这里不展开。

9.8.1.5 各项异性分布

各项异性分布主要是用于形容一些特殊的材质,比如说头发这种。详细的公式这里不提,主要是讲一下思路吧。

各项异性分布的主要思路就是,我们的横竖的粗糙度是不一样的,也就是有一定的方向性。为了达到这个效果我们需要提供给我们的BRDF两个粗糙度,一个是x的粗糙度一个是y的,表达横竖的粗糙度的不一样。

除了粗糙度的两个参数以外还需要提供TBN坐标的参数,这样才能找到正确的横轴竖轴。

9.8.2 Multiple-Bounce Surface Reflection

我们目前提出的微表面BRDF并没有考虑到光线在表面的多次弹射。我们的假设是几何遮蔽的能量直接消失了,但实际上并非如此。

这里书上并没有相信的去介绍,不如看Games202Kulla-Conty

9.9 次表面散射BRDF

前面的章节提到的是高光的BRDF,这里讲一下次表面散射,或者说漫反射BRDF

9.9.1 次表面反照率 Albedo

Albedo 代表着到达表面的能量有多少没有被吸收。一般来说我们可以把它当作漫反射的颜色。但当我们把它看作一个物理性质而非单纯的颜色的时候,在值的选择上也要多多注意。例如说材质本身是不应该吸收掉所有的能量的,所以我们不能把所有通道都设置成接近0的值。

9.9.3 光滑的次表面模型

如果我们的粗糙的细节是小于次表面散射的范围的时候,我们就需要考虑怎么去混合漫反射BRDF和高光。书中有提到的是如果粗糙度的细节比次表面散射要大,我们就不用考虑到微表面模型;不过个人感觉就渲染来说我们完全可以把粗糙度定义成小于次表面散射距离的微表面粗糙度来去模拟我们的渲染场景。

首先为什么要做混合,我们可以理解成光能打到表面上较光滑的地方所产生的高光的能量,并不会被用于漫反射。也就是说,我们不能把光能用于做两者的计算然后直接加一起,会能量不守恒。

最简单的混合方式就是把我们的 菲涅尔函数 当作参考,因为它代表着我们的物体反射了多少光。那么我们可以理解成没被直接反射的光都是用于漫反射的:

\[原本:\\ f_{diffuse}(l,v)=\frac{\rho_{ss}}{\pi}\\ 能量守恒:\\ f_{diffuse}(l,v)=(1-F(n,l))\frac{\rho_{ss}}{\pi}\]

不过这里做了一个假设,那就是进来的光在出射时是均匀分布的(考虑到pi)。所以后面推出了一个更真实的方案,不过前提是菲涅尔使用Schlick近似:

\[f_{diffuse}(l,v)=\frac{21}{20\pi}(1-F_0)\rho_{ss}(1-(1-(n\cdot l)^5))(1-(1-(n\cdot v))^5)\]

9._

后面都是一些特殊情况使用的渲染模型。一般来说可能会因为复杂性,以及开销所以没有特别普遍。

需要的时候会重温的。