前言
仅为自己的一个学习笔记,参考来源:
[1] https://learnopengl-cn.github.io/07%20PBR/01%20Theory/
[2] https://www.cnblogs.com/timlly/p/10631718.html#31-pbr%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA%E5%92%8C%E6%8E%A8%E5%AF%BC
[3] http://candycat1992.github.io/unity_shaders_book/unity_shaders_book_chapter_18.pdf
[4] https://zhuanlan.zhihu.com/p/33464301
[5] https://zhuanlan.zhihu.com/p/145410416
[6] GAMES101
[7] Dx12龙书
1. 概览
渲染方程(Render Equation)
反射等式(Reflectance Equation)
渲染方程去掉自发光项得到的就是反射等式,如下:
反射率方程:
这里
ω
\omega
ω 为立体角,
L
L
L为radiance,
f
r
f_{r}
fr为BRDF。立体角取极限就是“一条光线”,即一个向量。
Cook-Torrance反射率方程:
上述式子的文字描述:
判断PBR的标准
判断一种PBR光照模型是否是基于物理的,必须满足以下三个条件:
- 基于微平面模型(Be based on the microfacet surface model)。
- 能量守恒(Be energy conserving)。
微平面近似法使用了这样一种形式的能量守恒(Energy Conservation):出射光线的能量永远不能超过入射光线的能量(发光面除外)。为了遵守能量守恒定律,我们需要对漫反射光Diffuse和镜面反射光Specular之间做出明确的区分。
一束光照到材质表面上,通常会分成反射(reflection)部分和折射(refraction)部分。
对于反射部分,在这里我们直接视为镜面反射(specular),也就是说如果模型足够粗糙,表现出来的效果和我们在物理学中的“漫反射现象”是一样的;
而对于折射部分,其中第一部分光线被完全吸收而不会散开,而第二部分则在跟物体微粒发生若干次碰撞之后发射出表面,我们在这里谈的diffuse部分则全部由第二部分构成,这部分光也被称为次表面散射光(subsurface-scattered light)。
通常情况下,PBR会简化折射光,即如下图右侧所示,把第二部分折射光线所射出的位置全部和射入的位置相同,即只需考虑在一个像素内的情况。而有一些被称为次表面散射(Subsurface Scattering)技术的着色器技术将这个问题考虑了进去,那些次表面散射光会从不同于入射点的位置从物体内部再次射出,如下图左侧所示,它们显著的提升了一些诸如皮肤,大理石或者蜡质这样材质的视觉效果,不过伴随而来的则是性能下降代价。
- 使用基于物理的BRDF(Use a physically based BRDF)。
BRDF(Bidirectional Reflectance Distribution Function,中文名称为双向反射分布函数)定量描述了物体表面一点是如何和光进行交互的。大多数情况下,BRDF 可以用 f (l, v)来表示,其中 l 为入射方向和 v 为观察方向(双向的含义)。
如果绕法线旋转入射方向,所有出射方向上的反射和散射光线的相对分布情况仍然不变(即BRDF函数f的值不变),就称为各项同性(isotropic)的 BRDF。与之对应的则是各向异性(anisotropic)的 BRDF。
我们有时也会说一个模型表面是各向同性或各向异性的,比如说拉丝金属、毛发是各向异性的,一般可以说,如果当固定视角和光源方向时旋转这个表面,反射不会发生任何改变,就是各向同性,反之为各项异性。这样的描述其实与上面各向同性/异性的BRDF描述一致——因为固定视角和观察方向旋转表面,等价于固定表面沿法线旋转视角和光源方向。如Blinn-Phong模型就是各向同性。
判断BRDF是否基于物理的,就是看它是否满足交换律(reciprocity)和能量守恒(energy conservation)
交换律要求当交换 l 和 v 的值后,BRDF 的值不变,即:
f ( l , v ) = f ( v , l ) f(l,v) = f(v, l) f(l,v)=f(v,l)
而能量守恒则要求表面反射的能量不能超过入射的光能,即:
∀ l , ∫ Ω f ( l , v ) ( n ⋅ l ) d ω 0 ≤ 1 \forall l, \int_{\Omega }^{}f(l, v)(n \cdot l)d\omega_{0} \leq 1 ∀l,∫Ωf(l,v)(n⋅l)dω0≤1
2. 辐射度量学
上表来源于向往老师博客,为老师两年前所写,其中有一些笔误,不过可以参考一下方便查阅。
注意:
一个英文的表格可以参见wiki:https://en.wikipedia.org/wiki/Radiance
同时wiki也有中文版:https://zh.wikipedia.org/wiki/%E8%BE%90%E5%B0%84%E5%BA%A6%E9%87%8F%E5%AD%A6
辐射能量、辐射通量与立体角
- 辐射能量(Radiant energy):光具有波粒二象性,本质可以看作一种电磁波,因此辐射能量其实就是光这样一种电磁波的电场能量和磁场能量的总和,也叫做电磁波的能量。
- 辐射通量(Radiant Flux):单位时间辐射的能量,也叫辐射功率(Radiant Power)或通量(Flux)。
光是由多种不同波长的能量所集合而成的,而每种波长则与一种特定的(可见的)颜色相关。因此一个光源所放射出来的能量可以被视作这个光源包含的所有各种波长的一个函数。
一般偏向用radiant flux来衡量光线的亮度,因为我们更关心的是单位时间的效果,比如60W的白炽灯泡。 - 球坐标系:在立体角之前想先说说球坐标系。
如上图,我们可以知道球坐标系(r,θ,φ)与直角坐标系(x,y,z)的转换关系:
x=rsinθcosφ.
y=rsinθsinφ.
z=rcosθ. - 立体角(Solid Angle):
可以把自己想象成为一个站在单位球面的中心的观察者,向着投影的方向看。这个投影轮廓的大小就是立体角。锥体的立体角大小定义为,以锥体的顶点为球心作球面,该锥体在球表面截取的面积与球半径平方之比,单位为球面度。
于是结合球坐标系,立体角的微分就是:
立体角的国际制单位是球面度(steradian,sr),一个球有 4 π s t e r a d i a n s 4\pi steradians 4πsteradians (球面积 4 π r 2 4\pi r^{2} 4πr2),这可以积分如下:
而对于各向同性点光源(注意之前都是对材质说的,这里是对光源说的,表现为点光源所有方向上的亮度都与方向无关,即各个方向同样的性质):
辐射强度、辐照度与辐射率
-
辐射强度(Radiant Intensity):在单位球面上,一个光源向每单位立体角所投送的辐射通量。
I = d Φ d ω I = \frac{d\Phi}{d\omega} I=dωdΦ -
辐照度(Irradiance):到达单位面积的辐射通量,单位 瓦/平方米(W/m2)
注意这里是单位面积而非垂直面积。
如上图,同样一组光的照射面积, A 2 = A / c o s θ A_{2} = A / cos\theta A2=A/cosθ
而如点光源的衰减,就能很好地通过irradiance去解释,因为越往外扩散,一圈的面积扩大,单位面积所得的 能量(辐射通量)就少了,即irradiance falloff:
-
辐射率(Radiance):通过单位面积单位立体角的辐射通量,即表示的是,一个拥有辐射强度Φ的光源在单位面积A,单位立体角ω上的辐射出的总能量。直观来看的话,很像是Intensity和irradiance的结合,但是要注意这里是垂直面积,还要考虑 c o s θ cos\theta cosθ,即实际上为 d A ⊥ dA^\perp dA⊥而不是 d A dA dA。
它同时指定了光的方向与照射到的表面所接受到的亮度。
在我的理解中,irradiance更突出”一块区域“的接收,即不在意”光的方向“,更在意”区域的位置“,表现出来其实是所有方向的radiance的一个积分,将区域极限成一个点来说即所有投射到点p上的光线的总和。既然没有方向的信息,那么哪来的 θ \theta θ角度?所以是dA;
而radiance更注重描述一个立体角(或叫一束光)对该区域的影响,即既注重”光的方向“,也在意”区域的位置“,而反应光线方向的因素就在那个 c o s θ cos\theta cosθ中,因此后者考虑的是垂直面积(也就内含了方向这一个因素):
那么有了上面的一些列基础知识,我们进一步可以对radiance做出一些推导:
r a d i a n c e = L = d 2 Φ d A d ω cos θ = d I d A ⊥ = d E d ω c o s θ radiance = L = \frac{d^2\Phi}{ dA d\omega \cos\theta} = \frac{dI}{dA^\perp} = \frac{dE}{d\omega cos\theta} radiance=L=dAdωcosθd2Φ=dA⊥dI=dωcosθdE
这里的I为辐射强度(Radiant Intensity),E为辐照度(Irradiance),进一步就可以得到:
3. 光源
颜色基础
RGBA,计算机中,128位颜色,一个通道32bit,4字节,刚好对应一个float,值0~1;32位颜色,一个通道8bit,对应0 ~ 255的整数,每个通道除以255即可映射到0 ~ 1的区间。
我们用于显示图像数据都是32位颜色,即后台缓冲区都是存的32位颜色数据。
如果在某些情况下需要缩减顶点占用内存的大小,那么不妨将颜色的精度从128位减少到32位。一般来说128位颜色值常用于高精度的颜色运算(例如位于像素着色器中的各种运算),但是最终存储在后台缓冲区中的像素颜色数据却往往都是以32位颜色值来表示。
由于人眼对暗部更敏感,比如自然界中亮度的0.2左右的亮度,对应的就是人眼感受到的中灰色(0.5),而0.5的2.2次方约等于0.2。因此为了让有限的32位颜色通道存有更多的暗部信息,我们还会用一条2.2次幂的曲线去进行gamma矫正,那么对应的就有还原的曲线,即 y = x 2.2 y=x^{2.2} y=x2.2与 y = x 0.454 y=x^{0.454} y=x0.454
由于PBR要求输入都是线性的,所以我们需要把全部的计算放在线性空间,而在着色器的最后做伽马矫正。而在伽马矫正前我们还通常会采用色调映射使LDR的值映射为HDR的值。
三种光源
在真实的物理世界中,所有的光源都是有面积概念的,即所谓的面光源。但是模拟面光源是复杂且消耗高的(我们一般可以用烘焙的手段),因此实时渲染我们通常会使用精确光源(punctual light sources)来近似模拟这些面光源。图形学中常见的精确光源类型有点光源、平行光和聚光灯等,这些精确光源被认为是大小为无限小且方向确定的。
示例代码:
// 存储三种光源的信息
struct Light
{
float3 Strength; // 光源的颜色
float FalloffStart; // point/spot light only
float3 Direction; // directional/spot light only
float FalloffEnd; // point/spot light only
float3 Position; // point/spot light only
float SpotPower; // spot light only
};
// 计算衰减,做了近似
float CalcAttenuation(float d, float falloffStart, float falloffEnd)
{
// Linear falloff.
return saturate((falloffEnd-d) / (falloffEnd - falloffStart));
}
平行光源(directional light)
我们只要存光源的颜色和方向就可以了。
点光源(point light)
对于点光源,光强会根据平方反比定律(inverse squared law)而随着距离函数发生衰减。也就是说距离光源d处的某点光强为:
I
(
d
)
=
I
0
d
2
I(d) = \frac{I_{0}}{d^{2}}
I(d)=d2I0
我们之前已经用irradiance说明过了。
因此我们要存它的颜色、范围(用FalloffStart和FalloffEnd表示,表现在计算衰减的函数中)还有它的位置。
聚光灯光源(spot light)
上图表示的是一个聚光灯以位置Q向方向d发射出半顶角为
ϕ
m
a
x
\phi_{max}
ϕmax的圆锥体范围的光。
聚光灯的衰减一般反应在角度上:
ϕ
m
a
x
\phi_{max}
ϕmax处为0,角度为0处为最强光强。而为了刻画
ϕ
m
a
x
\phi_{max}
ϕmax与衰减,我们用了一个近似:
通过对幂次s来表现
ϕ
m
a
x
\phi_{max}
ϕmax。比如s取为8,所对应描述出来的
ϕ
m
a
x
\phi_{max}
ϕmax就为45度。
因此我们要存它的颜色、Power(表现在计算衰减的函数中,即为上面的那个幂次s)、范围(用FalloffStart和FalloffEnd表示,表现在计算衰减的函数中)还有它的位置。
4. Cook-Torrance BRDF
f r = k d f l a m b e r t + k s f c o o k − t o r r a n c e = k d c π + k s D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) f_r = k_d f_{lambert} + k_s f_{cook-torrance} = k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} fr=kdflambert+ksfcook−torrance=kdπc+ks4(ωo⋅n)(ωi⋅n)DFG
漫反射部分
Lambertian漫反射: f l a m b e r t = c π f_{lambert} = \frac{c}{\pi} flambert=πc
这里的c表示表面颜色,底下的pi直观来看是由于BRDF反应出来是一个固定的入射方向和观察方向,而光线不可能都投射到观察方向,所以要有一个归一项。同时这也保证了能量守恒。具体为何是pi可以根据能量守恒去计算,因为我们假设了漫反射在所有方向上的强度相同而BRDF在半球内的积分值为1。
对于Disney BRDF,它的漫反射项为:
法线分布函数D
法线分布函数D(Normal Distribution Function,或称正态分布函数)从统计学上近似的表示了与某些(中间)向量h取向一致的微平面的比率。
N
D
F
G
G
X
T
R
(
n
,
h
,
α
)
=
α
2
π
(
(
n
⋅
h
)
2
(
α
2
−
1
)
+
1
)
2
NDF_{GGX TR}(n, h, \alpha) = \frac{\alpha^2}{\pi((n \cdot h)^2 (\alpha^2 - 1) + 1)^2}
NDFGGXTR(n,h,α)=π((n⋅h)2(α2−1)+1)2α2
上式h为中间(半程)向量,n为法线,
α
\alpha
α表示表面粗糙度。
D=0.35就表现为微平面中有35%与中间向量h取向一致。
从数学上来看,表面越粗糙,微平面的取向方向就会越随机,那么表现出来就应该和法线的关联越小,即 n ⋅ h n \cdot h n⋅h 的系数越低;反之则与n关联越大,则越容易出现高光(一个非常明亮的斑点)。并且,法线n和h越接近,所反应出来的D函数应当越大,而这里的 α \alpha α取值范围在0~1,即 α 2 − 1 < = 0 \alpha^2 - 1 <= 0 α2−1<=0,于是 n ⋅ h n \cdot h n⋅h越大,D项就越大。而使用 α \alpha α的平方则可以在材质粗糙度上得到更加线性的变化,否则若为1次幂则在光滑与粗糙间插值总是偏粗糙的。因此粗略分析,上式是比较合理的。
几何函数G
几何函数从统计学上近似的求得了微平面间相互遮蔽的比率,这种相互遮蔽会损耗光线的能量。
注意这里表现的是微平面间的,与我们的AO是不一样的。
我们将要使用的几何函数是GGX与Schlick-Beckmann近似的结合体,因此又称为Schlick-GGX:
G
S
c
h
l
i
c
k
G
G
X
(
n
,
v
,
k
)
=
n
⋅
v
(
n
⋅
v
)
(
1
−
k
)
+
k
G_{SchlickGGX}(n, v, k) = \frac{n \cdot v}{(n \cdot v)(1 - k) + k }
GSchlickGGX(n,v,k)=(n⋅v)(1−k)+kn⋅v
这里k是α基于几何函数是针对直接光照还是针对IBL光照的重映射(Remapping) :
k
d
i
r
e
c
t
=
(
α
+
1
)
2
8
,
k
I
B
L
=
α
2
2
k_{direct} = \frac{(\alpha + 1)^2}{8} , k_{IBL} = \frac{\alpha^2}{2}
kdirect=8(α+1)2,kIBL=2α2
由于遮挡既有入射方向的也有出射方向的:
所以我们可以使用史密斯法(Smith’s method)来把两者都纳入其中:
G ( n , v , l , k ) = G s u b ( n , v , k ) G s u b ( n , l , k ) G(n, v, l, k) = G_{sub}(n, v, k) G_{sub}(n, l, k) G(n,v,l,k)=Gsub(n,v,k)Gsub(n,l,k)
菲涅尔方程
菲涅尔(发音为Freh-nel)方程描述的是被反射的光线对比光线被折射的部分所占的比率,这个比率会随着我们观察的角度不同而不同(回想一下全反射现象,就是当入射角大于某一临界角时菲涅尔项为1,即没有折射)。
首先是我们熟知的折射定律:
n1、n2为两种介质的折射率。从全反射现象这种特例我们也能感受出从很大的角去看反光会变多,其实也就是菲涅尔现象,体现在菲尼尔方程中。
实时渲染中我们常使用Fresnel-Schlick近似法求得近似解:
F
S
c
h
l
i
c
k
(
h
,
v
,
F
0
)
=
F
0
+
(
1
−
F
0
)
(
1
−
(
h
⋅
v
)
)
5
F_{Schlick}(h, v, F_0) = F_0 + (1 - F_0) ( 1 - (h \cdot v))^5
FSchlick(h,v,F0)=F0+(1−F0)(1−(h⋅v))5
F0表示平面的基础反射率,即法向入射(0度角入射)的反射率。它是利用所谓折射指数(Indices of Refraction)或者说IOR计算得出的。v为观察方向,h为中间向量。F0越大,F值越大,反光(反射)就应当越强。
5. 两套PBR流程
两套流程分别使用不同的三张贴图存储上面方程中的信息,而Normal、Height等纹理则是公共的。下面挨个来看。
“金属度/粗糙度”流程
- 反照率贴图(Albedo)
基础颜色而已。所有的光照信息都从这个纹理中提取基础颜色。表现出来是每一个纹素的基础反射率F0。 - 金属度贴图
一般是灰度图。 - 粗糙度贴图
粗糙度(Roughness)贴图可以以纹素为单位指定某个表面有多粗糙。
“镜面反射/光泽度”流程
- 漫反射贴图(Diffuse)
漫反射颜色信息,如果是金属diffuse贴图给出的值是纯黑。 - 镜面反射贴图
一般是RGB,可以看到该流程比“金属度/粗糙度”流程多了两个通道。 - 光泽度贴图
光泽度为1减去粗糙度,无他。
“金属度/粗糙度”流程对应到PBR表达式
以https://learnopengl-cn.github.io/07%20PBR/02%20Lighting/为例:
三个贴图:
uniform sampler2D albedoMap;
uniform sampler2D metallicMap;
uniform sampler2D roughnessMap;
albedo和metallic可以去计算菲涅尔项:
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
这样去mix其实是因为,对于金属物体而言,F0参数对不同颜色值的反射率是不同的,这也是为什么F0要用RGB三原色来表示的原因(法向入射的反射率可随波长不同而不同)。而对于非金属而言F0参数对不同颜色值的反射率是相同的,因此三通道表现一致。
roughness在前面已经有很多阐述了,用来算D项和G项。
最后,我们没有把kS乘进去我们的反射率方程中,这是因为我们已经在specualr BRDF中乘了菲涅尔系数F了,因为kS等于F,因此我们不需要再乘一次。
而在漫反射计算中我们也用到了albedo当作漫反射项的c:
Lo += (kD * albedo / PI + specular) * radiance * NdotL;
最后的环境光也用到了:
vec3 ambient = vec3(0.03) * albedo * ao;
vec3 color = ambient + Lo;
因此对于该工作流,总结如下:
- albedo,所有的光照信息都从这个纹理中提取基础颜色,在环境光、漫反射、菲涅尔项用到。
- metallic,金属度,在菲涅尔项用到。
- roughness,粗糙度,在法线分布函数D与几何遮蔽函数G用到。越粗糙法线分布越不平均,微平面相互遮挡可能性越大。
“镜面反射/光泽度”流程对应到PBR流程
这部分仅是我的理解,还没有实际验证,不过至少在基于物理的Blinn-Phong(DX12龙书的第8章)是这样做的:
- diffuse,漫反射,在环境光、漫反射项用到。
- specular,镜面反射,表现为菲涅尔项,直接采样得到F0,然后计算得到菲涅尔项F
- glossiness,光泽度,即1-roughness,计算D和G。
6. 基于图像的光照(Image Based Lighting,IBL)
这部分几乎照搬learnopengl,只是方便自己回顾,建议去看:
https://learnopengl-cn.github.io/07%20PBR/03%20IBL/01%20Diffuse%20irradiance/
基于图像的光照(IBL)是一类光照技术的集合,与直接光照不同,它将周围环境当成一个大光源。IBL通常结合cubemap环境贴图,cubemap通常采集自真实的照片或从3D场景生成,这样可以将其用于光照方程:将cubemap的每个像素当成一个光源。这样可以更有效地捕获全局光照和常规感观,使得被渲染的物体更好地融入所处的环境中。
由于基于图像的光照算法会捕捉部分甚至全部的环境光照,通常认为它是一种更精确的环境光照输入格式,甚至也可以说是一种全局光照的粗略近似。基于此特性,IBL 对 PBR 很有意义,因为当我们将环境光纳入计算之后,物体在物理方面看起来会更加准确。
我们将反射方程拆成漫反射积分项和镜面反射积分项:
L
o
(
p
,
ω
o
)
=
∫
Ω
(
k
d
c
π
)
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
+
∫
Ω
(
k
s
D
F
G
4
(
ω
o
⋅
n
)
(
ω
i
⋅
n
)
)
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i
Lo(p,ωo)=Ω∫(kdπc)Li(p,ωi)n⋅ωidωi+Ω∫(ks4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi
下面逐一来看。
漫反射辐照
这里只关注漫反射部分:
L
o
(
p
,
ω
o
)
=
k
d
c
π
∫
Ω
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
L_o(p,\omega_o) = k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i
Lo(p,ωo)=kdπcΩ∫Li(p,ωi)n⋅ωidωi
有了上面的式子,我们就可以通过卷积去预计算出一个新的立方体贴图,它在每个采样方向——也就是纹素——中存储漫反射积分的结果。其实也就是每个纹素的irradiance。
立方体贴图的卷积
即计算沿着法线N的半球Ω内的每个方向的总平均辐射率。回到公式:
L
o
(
p
,
ω
o
)
=
k
d
c
π
∫
Ω
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
L_o(p,\omega_o) = k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i
Lo(p,ωo)=kdπcΩ∫Li(p,ωi)n⋅ωidωi
我们之前讲过立体角的微分,变形为:
L
o
(
p
,
ϕ
o
,
θ
o
)
=
k
d
c
π
∫
ϕ
=
0
2
π
∫
θ
=
0
1
2
π
L
i
(
p
,
ϕ
i
,
θ
i
)
cos
(
θ
)
sin
(
θ
)
d
ϕ
d
θ
L_o(p,\phi_o, \theta_o) = k_d\frac{c}{\pi} \int_{\phi = 0}^{2\pi} \int_{\theta = 0}^{\frac{1}{2}\pi} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta
Lo(p,ϕo,θo)=kdπc∫ϕ=02π∫θ=021πLi(p,ϕi,θi)cos(θ)sin(θ)dϕdθ
化成离散形式,即求黎曼和:
L
o
(
p
,
ϕ
o
,
θ
o
)
=
k
d
c
π
1
n
1
n
2
∑
ϕ
=
0
n
1
∑
θ
=
0
n
2
L
i
(
p
,
ϕ
i
,
θ
i
)
cos
(
θ
)
sin
(
θ
)
d
ϕ
d
θ
L_o(p,\phi_o, \theta_o) = k_d\frac{c}{\pi} \frac{1}{n1 n2} \sum_{\phi = 0}^{n1} \sum_{\theta = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta
Lo(p,ϕo,θo)=kdπcn1n21ϕ=0∑n1θ=0∑n2Li(p,ϕi,θi)cos(θ)sin(θ)dϕdθ
得到预计算的辐照度图后,就能直接将其用于IBL计算了。但是辐射方程也依赖了位置 p ,我们假设它位于辐照度图的中心。这就意味着所有漫反射间接光只能来自同一个环境贴图,这样可能会破坏现实感(特别是在室内)。渲染引擎通过在场景中放置多个反射探针来解决此问题,每个反射探针单独预计算其周围环境的辐照度图。之后的每个位置点就取离它最近的反射探针之间的辐照度去插值即可。
镜面IBL
这里只关注镜面反射部分:
L
o
(
p
,
ω
o
)
=
∫
Ω
k
s
D
F
G
4
(
ω
o
⋅
n
)
(
ω
i
⋅
n
)
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
=
∫
Ω
f
r
(
p
,
ω
i
,
ω
o
)
L
i
(
p
,
ω
i
)
n
⋅
ω
i
d
ω
i
L_o(p,\omega_o) = \int\limits_{\Omega} k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} L_i(p,\omega_i) n \cdot \omega_i d\omega_i = \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i
Lo(p,ωo)=Ω∫ks4(ωo⋅n)(ωi⋅n)DFGLi(p,ωi)n⋅ωidωi=Ω∫fr(p,ωi,ωo)Li(p,ωi)n⋅ωidωi
注意了,由于一个纹素是有大小的,其中是无穷多个
ω
o
\omega_{o}
ωo观察方向,故在积分中要考虑
ω
o
\omega_{o}
ωo,那么加上
ω
i
\omega_{i}
ωi就有两个方向。在实时状态下,对每种可能的ωi和ωo的组合预计算该积分是不可行的。对此,Epic Games 提出了一个解决方案,他们预计算镜面部分的卷积,为实时计算作了一些妥协,这种方案被称为分割求和近似法(split sum approximation)。
分割求和近似将方程的镜面部分分割成两个独立的部分,我们可以单独求卷积,然后在 PBR 着色器中求和,以用于间接镜面反射部分 IBL(分割求和近似法类似于我们之前求辐照图预卷积的方法,需要 HDR 环境贴图作为其卷积输入):
L
o
(
p
,
ω
o
)
=
∫
Ω
L
i
(
p
,
ω
i
)
d
ω
i
∗
∫
Ω
f
r
(
p
,
ω
i
,
ω
o
)
n
⋅
ω
i
d
ω
i
L_o(p,\omega_o) = \int\limits_{\Omega} L_i(p,\omega_i) d\omega_i * \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i
Lo(p,ωo)=Ω∫Li(p,ωi)dωi∗Ω∫fr(p,ωi,ωo)n⋅ωidωi
卷积的第一部分被称为预滤波环境贴图,它类似于辐照度图,是预先计算的环境卷积贴图,但这次考虑了粗糙度。
我们使用 Cook-Torrance BRDF 的正态分布函数(NDF)生成采样向量及其散射强度,该函数将法线和视角方向作为输入。由于我们在卷积环境贴图时事先不知道视角方向,因此 Epic Games 假设视角方向——也就是镜面反射方向——总是等于输出采样方向ωo,以作进一步近似。这样,预过滤的环境卷积就不需要关心视角方向了。
等式的第二部分等于镜面反射积分的 BRDF 部分。如果我们假设每个方向的入射辐射度都是白色的(因此L(p,x)=1.0 ),就可以在给定粗糙度、光线 ωi 、法线 n、 夹角 n⋅ωi 的情况下,预计算 BRDF 的响应结果。、
预滤波HDR环境贴图
所有可能出射的反射光构成的形状称为镜面波瓣(Specular Lobe)。随着粗糙度的增加,镜面波瓣的大小增加;随着入射光方向不同,形状会发生变化。因此,镜面波瓣的形状高度依赖于材质。 在微表面模型里给定入射光方向,则镜面波瓣指向微平面的半向量的反射方向。考虑到大多数光线最终会反射到一个基于半向量的镜面波瓣内,采样时以类似的方式选取采样向量是有意义的,因为大部分其余的向量都被浪费掉了,这个过程称为重要性采样。
蒙特卡洛积分和重要性采样
大数定律
先说大数定律,回顾一下概率论,有几种大数定律:
- 伯努利大数定律
- 切比雪夫大数定律
- 马尔科夫大数定律
- 辛钦大数定律
无论是哪种大数定律,直观地表现出来都是一个事实:当我们大量重复某一相同的实验的时候,其最后的实验结果可能会稳定在某一数值附近。
而大数定律的一般形式为:
蒙特卡罗方法
蒙特卡罗方法:通过随机采样来估计一个确定但未知的值
其实我觉得更是一种思想。
例如对于搜索,我们同样也能使用蒙特卡罗树搜索,比如来评估一个棋局。
- 首先我们可以从根节点开始,逐层选择最优的子节点,直到到达叶子节点。最优选择则按照上限信心界策略进行选择。
- 如果叶子节点不是一个终止节点(不会导致博弈游戏终止)那么就创建一个或者更多的子节点,选择其中一个C,从C开始进行蒙特卡洛随机模拟,直到博弈游戏结束
- 反向传播,用模拟结果更新当前行动序列的收益值
蒙特卡罗积分
就是把蒙特卡罗方法用在算积分上,其建立在大数定律的基础上,并采用相同的方法来求解积分。
用蒙特卡罗的方法去算,就是蒙特卡罗积分:
在这里我们公式为:
O
=
∫
a
b
f
(
x
)
d
x
=
1
N
∑
i
=
0
N
−
1
f
(
x
)
p
d
f
(
x
)
O = \int\limits_{a}^{b} f(x) dx = \frac{1}{N} \sum_{i=0}^{N-1} \frac{f(x)}{pdf(x)}
O=a∫bf(x)dx=N1i=0∑N−1pdf(x)f(x)
为了求解这个积分,我们在 a 到 b 上采样 N 个随机样本,将它们加在一起并除以样本总数来取平均。pdf 代表概率密度函数 (probability density function),它的含义是特定样本在整个样本集上发生的概率。
进而我们接着引出有偏估计和无偏的概念——某些蒙特卡洛估算是有偏的,这意味着生成的样本并不是完全随机的,而是集中于特定的值或方向;而无偏估计则意味着随着样本数量的不断增加,我们最终将收敛到积分的精确解。有时我们为了更快收敛而选择用有偏的估计去算,只要视觉上能接受就行。
低差异序列
我们可以对一种名为低差异序列的东西进行蒙特卡洛积分,该序列生成的仍然是随机样本,但样本分布更均匀:
当使用低差异序列生成蒙特卡洛样本向量时,该过程称为拟蒙特卡洛积分。拟蒙特卡洛方法具有更快的收敛速度,这使得它对于性能繁重的应用很有用。