深入理解渲染方程
在图形学入门(三):基础着色中,我们讨论了 Phong 反射模型,当时我们提到过 Phong 反射模型不是一个物理模型,而是一个经验模型,这意味着这个模型对光照效果的模拟是不准确的。即便在简单情况下它能近似出一些不错的效果,但随着场景的复杂度提升(例如复杂的光照、复杂的材质等),要想继续用 Phong 反射模型达到很强的真实感就变得越来越困难。例如下面的这幅图 1 中,士兵和长官的铠甲上都投影出了电梯里非常复杂的灯光,在后面的长官的铠甲上还能看到前面两个士兵的投影:
要想在计算机中模拟出这样真实的效果,我们首先要知道真实世界中的光线和物体是如何作用的,例如一束光线照射到物体表面,这束光线的量有多少,光线照射到物体表面后发生了什么,最后光线反射的方向和量又有多少,我们用什么物理单位来衡量这些量等等。这些准确定义的信息是计算出正确的结果的前提,只有先理解这些内容,我们才能更好地模拟光与物体相互作用的过程。
接下来我们会讨论关于辐射度量学(Radiometry)2 的内容,这是一个研究各种电磁辐射强弱的学科。在本文后面我们将会看到,基于辐射度量学构建的渲染方程(Rendering Equation)3 可以准确描述光如何在场景中流动,并在理论上给出了一个完美的结果。而各种各样的渲染技术,就是这个理想结果的一个近似。事实上,只要涉足高质量的实时渲染,渲染方程几乎是绕不开的前置知识。深入理解渲染方程包含的物理意义可以使我们更好地学习高质量真实感渲染的进阶内容。
在正式开始讨论前,我们首先明确要讨论的对象,也就是光的性质。我们知道,光其实有波粒二象性 4,但我们后面的讨论会基于几何光学 5,既不考虑其波动性,也不考虑其粒子性,仍然认为光沿直线传播。
辐射度量学
辐射通量(Radiant Flux)
让我们从最简单的物理量开始讨论,辐射能(Radiant Energy)6
我们更关心单位时间的能量而不是总能量是很符合常理的,例如一个电灯打开的时间越长肯定辐射出的光的总能量越多,但我们关心的主要还是这个灯有多亮,这也就是单位时间的光的能量。
辐射通量是辐射度量学中最基本的物理量。一个表面的辐射通量可以用「通过」该表面的坡印廷向量(Poynting Vector)8
有没有觉得这个说法听起来很熟悉?没错,这正是我们在图形学入门(三):基础着色中计算漫反射分量时提到的内容。当时我们通过下图 9 直观了解入射光和表面法线存在
通过刚刚的讨论,我们就可以明确其中的物理意义了,即光作为一个有向的能量流流过一个表面的时候表面通过的能量并不是光全部的能量,而和角度有关。如果光的能量流比较抽象,我们可以将其想象为水流,如果一块平面平行于水流方向,那么它不会受到水流的力的作用,随着角度倾斜,受到的力会增大,直到垂直于水流方向,此时受到的力最大。
除了辐射通量之外,我们常关注的物理量还有如下三个:
- 辐射强度(Radiant Intensity):用于描述光源发出光的情况
- 辐照度(Irradiance):用于描述物体表面接收到的光的情况
- 辐射率(Radiance):用于描述一条光线
下面我们来分别讨论它们。
辐射强度(Radiant Intensity)
辐射强度
这里提到了「立体角」这个概念,这是一个物体对特定点的三维空间的角度,如下图 11 所示:
这描述看起来就非常难理解,怎么这个角度是「一个物体相对于一个点」的角度?这和我们在二维空间中认知的角度定义很不一样,在二维空间中角度似乎和别的东西没关系,我们不会说角度是一个形状相对于一个点的角度。而且,如果这角度是个圆锥之类的形状可能还比较好理解,上图中这个物体居然具有不规则的形状,那它张开的角度到底应该怎么理解和计算呢?
理解这个概念的关键在于,我们不要把它理解为是类似二维空间那样张开的角度。而应该将计量立体角的球面度作为二维空间中弧度的延伸。在二维空间中我们是如何定义弧度的呢?是用弧长除以半径。对于半径为
一个圆的总弧度是
那么,我们就可以得出:
类似于圆的总弧度,一个球的总球面度是
回到辐射强度的定义公式
因此我们知道球面上面积的微分为:
因此,球面度的微分就为:
在辐射度量学中,我们也会用
辐照度(Irradiance)
辐照度
其中,
辐照度常被称为「强度」(Intensity),但为了和前面我们讨论的「辐射强度」(Radiation Intensity)区分开来,因此起了「Irradiance」这个名字 15。
使用辐照度可以很容易解释我们日常生活中一些常见的现象。例如,我们可以观察到灯泡的亮度会随着距离的增加而逐渐衰减,那么这个衰减的东西是什么呢?是能量消失了么?其实这里衰减的量就是辐照度。对于一个点光源来说,我们可以以其为球心作球面,那么,只要功率不变,那么每个球面上的总辐射通量就是不变的,从下图中我们可以很明显地看出,当辐射通量相同的时候,随着照亮的距离增大,由于球面的表面积增大,因此单位面积的辐射通量(即辐照度)就减少了,这就使得我们感受到灯的亮度降低。由于球面的面积和
辐射率(Radiance)
辐射率
对于点
在上面的定义公式中,因为需要同时计算单位面积和单位立体角的辐射通量,因此我们要对辐射通量
虽然辐射率的定义公式看起来就比之前定义公式的要复杂很多,但我们也不难发现它和前面我们讨论的辐射强度和辐照度的关联,我们前面提到:
- 辐照度是每单位面积的辐射通量
- 辐射强度是每单位立体角的辐射通量
因此我们也可以认为:
- 辐射率是每单位立体角的辐照度
- 辐射率是每单位面积的辐射强度
虽然准确来说,辐照度是指入射表面的辐射通量,如果我们想讨论发射的辐射通量,我们应该使用的物理概念是辐射出射度(Radiant Exitance)18,但如果我们不考虑方向性的问题,只关心的是这里的量有多少的情况下,我们可以直接认为「辐射率是每单位立体角的辐照度」,并将该「入射辐射率」
如下图所示,辐照度
类似地,我们可以说「辐射率是每单位面积的辐射强度」:
如下图所示,辐射强度
这样一来,我们就可以将几个物理概念联系在一起,更好地理解其中的意义。
双向反射分布函数(BRDF)
有了上面的基本物理量的定义之后,我们就可以讨论如何更准确地来描述反射这件事了。前面提到,在 Phong 反射模型中,我们对反射进行了简单的近似处理,但这个近似不具有物理意义。这种不精确的近似手段导致了 Phong 反射模型无法达到更真实的渲染效果,尤其是对于复杂的表面材质和复杂的光照情况。那么,我们应该如何精确地描述光的反射呢?
对于反射这个过程,我们既可以将其理解为光线照射到物体表面后发生弹射,也可以将其理解为是物体表面吸收了某个方向来的入射光的能量后,向某个方向辐射出这个能量的过程。根据前面讨论的内容,我们知道我们可以使用辐照度
我们前面提到,可以认为「辐射率是每单位立体角的辐照度」。那么根据前面讨论的公式
而这样一个「如何在某个
BRDF 描述了光在一个不透明物体表面上反射时的性质,它本质上是一个四元函数,参数中的入射方向
BRDF 的意义是在
正是因为 BRDF 描述了光如何在一个表面上被反射,因此 BRDF 本身就决定了一个物体表面的材质属性,不同材质的物体表面对光的反射情况不同,就可以用不同的 BRDF 来描述。
渲染方程(Rendering Equation)
有了 BRDF 后,我们就知道了反射光与入射光的关系,这样一来,我们终于可以讨论渲染方程的定义了。首先,我们可以将 BRDF 的公式稍作变换,得到平面上任意一点
那么对于该位置
我们之所以只需要在半球上进行积分,是因为我们认为被求表面的是不透明的,其背面的光不对这个表面的反射产生任何贡献。上式已经告诉了我们某个方向上的因为反射而发出光的情况,我们还可以在此基础上加入自发光项
由于我们知道了物体表面的法线,因此我们会将
渲染方程的物理基础是能量守恒定律,在一个特定的位置和方向,出射光