GAMES104课程笔记05-Lighting, Materials and Shaders
这个系列是GAMES104-现代游戏引擎:从入门到实践(GAMES 104: Modern Game Engine-Theory and Practice)的同步课程笔记。本课程会介绍现代游戏引擎所涉及的系统架构、技术点以及引擎系统相关的知识。本节课主要介绍现代游戏引擎的实时渲染算法。
在上一节课中我们介绍了渲染的概念,而本节课我们则会开始介绍游戏引擎中具体的渲染算法。渲染是研究光与材质相互作用的学科,因此本节课从光线、材质以及shader三个方面介绍现代游戏引擎中各种经典实时算法的原理。

The Rendering Equation
渲染的本质是求解渲染方程(the rendering equation),它由James Kajiya于1986年提出。

不严格地讲,渲染方程指出空间点\(x\)在方向\(\omega_o\)上出射的能量等于它自身在该方向发射的能量加上来自半球面上所有方向入射并经过反射后反射到\(\omega_o\)上的能量之和。

Complexity of Real Rendering
显然想要直接求解渲染方程是相当困难的。在现实的场景中光线会在物体表面经过多次反射,同时不同的材质也有着天差地别的反射行为。

渲染的难点之一在于阴影,或者说是光的可见性。如何做出合适的阴影效果远比想象中要难得多,在实践中往往需要通过大量的技巧才能实现符合人认知的阴影效果。

其次,场景中往往有着各种类型的光源需要考虑。常见的光源形式包括平行光、点光源、聚光灯、面光源等等。

材质是渲染中最为复杂的因素之一。如何设计符合现实世界的材质模型并且满足实时计算的要求是实时渲染的一大难点。

最后,在渲染时需要考虑光线在场景中不断反射的行为。全局光照一直是渲染的终极目标。

总结一下,渲染的难点可以分为一下三部分:如何计算入射光线、如何考虑材质以及如何实现全局光照。

Starting from Simple
Simple Light Solution
我们从最简单的情况开始考虑。首先考虑对光照进行分解,将反射光分解为漫反射和环境光两部分。这样已经可以实现一些简单的渲染效果。

为了更好地模拟环境光照,我们可以使用环境贴图技术把环境光存储在一个立方体表面上。这样当需要计算入射光线时只要根据方向去进行查询即可。

实际上这样的处理方法在数学上也是解释得通的,它相当于把入射光线分解为低频的漫反射和高频的环境光。

Blinn-Phong Materials
有了光照后我们开始考虑材质。最经典的材质模型是Blinn-Phong材质(Blinn-Phong materials),它把材质的反射行为分解为与方向无关的环境光、与入射和观察角度有关的漫反射以及高光。

当然Blinn-Phong模型也有很多问题,比如说它不遵循能量守恒,同时它也不能描述现实世界中丰富的材质外观。

Shadow
对于阴影问题,早期的处理方法主要是shadow volume,而现代游戏引擎的主流方法则是shadow map。shadow map的处理流程是在光源位置设置一个新的相机并渲染出一张深度图,然后在实际相机进行渲染时对每个点检测它到光源处的深度。如果该深度大于深度图对应位置处的深度则说明该点对于光源是不可见的,即位于阴影中。


shadow map的主要缺陷在于它只能产生「硬阴影」而无法产生现实中渐变的「软阴影」。而且从光源和相机进行采样时往往会使用不同的采样率,这容易导致各种走样和自遮挡的问题。

把上面介绍过的这些技巧组合到一起并且配合美术的精巧设计就可以实现一些不错的渲染效果了。


Pre-computed Global Illumination
全局光照可以显著地提升画面的渲染效果,而它的难点在于如何表示来自其它物体反射的间接光照以及如何计算光照与材质BRDF的积分(卷积)。


Spherical Harmonics
球面谐波函数(spherical harmonics, SH)是实时渲染中表示环境光照的经典方法,不过在介绍SH前我们先来回顾一下Fourier变换的相关理论。Fourier变换指出无限循环的时域信号可以分解为不同频率函数的叠加,同时时域信号的卷积等价于经过Fourier变换后频域信号的乘积。


而SH可以看成是对球面上信号进行分解,可以证明任意的球面信号可以分解为无限多基函数信号的加权和,而且这些基函数是相互正交的。



基于SH我们可以对场景中任意点接收到的环境光照进行分解,一般来说只需要使用1-2阶的低频信号就可以实现合理的渲染效果。


假设我们使用2阶的SH对环境光照进行分解,在RGB3个通道上只需要12个参数即可表示任意点接收到的环境光。进行存储时可以对不同的系数使用不同的精度进行存储,这样任意点的环境光照可以使用RGBA的32bits来表示,换句话说每一点的光场相当于RGBA纹理图像上的一个像素。

Lightmap
lightmap正是基于这种思想而提出来的光照技术。我们可以将场景中每个点的光照离线烘焙到一张纹理图上,然后在渲染时读取纹理值来获得SH表达的环境光照。

当然在进行烘焙时不需要使用包含各种细节的网格,我们只需要使用一个精度相对较低的网格并进行参数化即可。

当美术完成场景建模后就可以开始烘焙了。显然计算lightmap是非常耗时的,但通过lightmap可以实现非常逼真的场景效果,而且在实际渲染时lightmap可以实现场景的实时渲染。



当然lightmap也有一些缺陷,比如说离线烘焙时需要很长的时间、lightmap无法考虑动态的光源、同时存储lightmap也需要非常大的存储空间。

Light Probe
除了lightmap外还可以在空间上设置一些探针(probe)来记录环境光照,而在计算物体的着色时只需要对附近probe进行插值即可估计该点的光照。

当然如何设置这些probe是比较麻烦的。早期的实践中一般是由美术人工进行设置,而目前则可以使用一些自动化的工具来自动生成probe。

如果要考虑材质的反射行为则需要设置专门的反射probe。一般来说这样的反射probe不需要设置很多,但每个probe则需要有更高的精度。

light probe同样可以进行实时渲染。不同于lightmap,基于light probe可以实现动态的场景和物体渲染,而且现代计算机的计算性能也允许对probe进行动态更新。

Physical-Based Material
Microfacet Theory
有了光照后我们来考虑材质。基于物理的材质模型大量使用了微表面理论(microfacet theory)来模拟现实世界中材质,简单来说微表面理论认为材质的表面是由大量方向各异的光滑镜面组成,这些镜面的分布控制了不同材质的反射行为。

光线在物体表面上的反射可以分解为体反射(body reflection)和表面反射(surface reflection)两种。在体反射中光子会进入到物体的内部进行反射然后从物体表面的另一个点射出;而在表面反射中光子则会直接被反射出去。实际上物体表面的漫反射行为对应着体反射,我们可以使用Lambert模型进行表示;而表面反射的行为则可以基于微表面理论使用Cook-Torrance模型来进行表达。将二者组合到一起就构成来材质的BRDF。

Cook-Torrance模型可以拆分为三项。首先是法向分布函数(normal distribution function),它表示材质不同方向上镜面法向的分布情况。法向分布函数包含一个参数\(\alpha\)来表示表面的粗糙度,\(\alpha\)越大表示表面越粗糙反射行为越接近漫反射,\(\alpha\)越小表示表面越光滑反射行为越接近理想镜面反射。

Cook-Torrance模型中的另一项是几何项(geometry attenuation term),它表示不同方向镜面的自遮挡行为。在Cook-Torrance模型中使用Smith模型将几何项分解为出射方向和入射方向两个方向上的可见性乘积,每个方向上的可见性使用GGX模型进行计算。

Cook-Torrance模型中的最后一项是Fresnel项(Fresnel equation),它表示不同材质光滑表面的理想反射行为。在实时渲染中一般使用Schilick近似来计算这一项。

为了获得真实材质的光学参数我们还需要进行实际的测量。MERL BRDF数据库给出了常见材质的BRDF测量结果。

Disney Principled BRDF
为了方便不同背景的从业者进行使用,Disney提出了著名的Disney Principled BRDF来设计不同的材质模型。它的思想是设计材质模型时要尽可能方便艺术家进行理解,而不要过多地使用物理上面的概念。


PBR Specular Glossiness
目前在游戏引擎中最常用的材质模型是specular glossiness模型(SG)。在SG模型中物体的反射行为分解为三张图的叠加:diffuse用来控制漫反射、specular图用来控制Fresnel项、而glossiness图则控制金属材质的粗糙度。把三张图带入BRDF计算公式就可以渲染出非常漂亮的模型。


在长期的实践中为了防止美术在设计时出错,人们还对SG模型进行了封装并得到了metallic roughness模型(MR)。MR模型同样包含三张图:一张base color图表示漫反射、一张roughness图表示粗糙度、还有一张metallic图表示材质的金属度。当metallic值很高时材质会更接近于金属材质产生大量的光泽反射,否则会接近于非金属材质以漫反射为主。


MR模型不太适合表示介于金属和非金属之间的材质,但大多数情况下仍然是工程中的首选。

Image-Based Lighting
IBR本身是使用真实图像作为光照的方法,但结合PBR材质可以实现非常逼真的实时渲染效果。

根据前面介绍过的PBR材质,物体表面的BRDF可以分解为漫反射以及镜面反射两项。我们对这两项分别进行处理在累加起来就可以实现IBR。

Diffuse Irradiance Map
对于漫反射项比较简单,我们首先通过预计算的方法对环境光照按照余弦进行积分,接着把积分后的值存储在一个表格中。实际渲染时进行查表并和漫反射系数进行相乘即可。

Specular Approximation
对镜面反射的处理要相对复杂一些。我们首先需要将镜面反射的积分利用split-sum拆分成光照项(lighting term)和BRDF项(BRDF term)两部分,而这两项都可以通过预计算的方法事先存储在一个表格中。



最后把漫反射项和镜面反射项加起来就得到了完整的环境光照。

在现代3A游戏中基于IBR可以实现非常逼真的图像效果。

Classic Shadow Solution
cascade shadow是实时渲染中阴影的经典处理方法。它的思想是根据距离来调整shadow map的精度,近处的精度高远处的精度低。


当然想要得到合理的阴影效果需要大量的技巧,而且cascade shadow需要大量的存储空间在计算上也需要大量的时间。


现代实时渲染中会更多地使用PCF和PCSS等算法,它们的特点是可以实现更加自然的软阴影效果。



另一种生成软阴影的方法是VSSM,它基于Chebyshev不等式来估计像素上的阴影比例。

把上面介绍过的技术全部结合到一起就可以实现上个世代3A游戏的渲染效果。

Moving Wave of High Quality
随着各种shader模型的提出以及硬件计算性能的进步,上面介绍的实时渲染算法已经不能完全满足人们对画质的需求。

实时光线追踪(real-time ray tracing)就是一个很好的案例。随着显卡性能的提升我们可以把光线追踪算法应用在实时渲染中从而获得更加真实的光照和反射效果。

另一方面实时全局光照(real-time global illumination)也取得了很大的进步。这几年各种实时全局光照算法层出不穷,基于全局光照可以给游戏画面带来质的飞跃。

在材质渲染方面,随着geometry shader的出现人们可以获得几乎无限的几何细节。同时大量基于BSSDF的shader使得人们更准确地描述物理材质与光线的相互作用。

在虚幻5引擎中还使用了virtual shadow map来生成更加逼真的阴影。

Shader Management
本节课最后讨论了游戏引擎中的shader管理问题。在现代3A游戏中每一帧的画面上可能都有上千个shader在运行。


这些大量的shader一方面来自于美术对场景和角色的设计,另一方面不同材质在不同光照条件下的反应也使得程序员需要将不同情况下的shader组合到一起,并通过宏的方式让程序自行选择需要执行的代码。



除此之外不同的平台上往往使用了不同的图形库,在编写shader的时候需要考虑跨平台的问题。

Reference
- Lecture 05:Rendering on Game Engine
- GAMES202 Lecture 03:Real-time Shadows 1
- GAMES202 Lecture 04:Real-time Shadows 2
- GAMES202 Lecture 05:Real-Time Environment Mapping 1
- GAMES202 Lecture 06:Real-Time Environment Mapping 2
- GAMES202 Lecture 10:Real-Time Physically-Based Materials 1
- GAMES202 Lecture 11:Real-Time Physically-Based Materials 2