


引言
在灯光渲染中,点光、平行光和聚光灯的实现较为容易,原因是要么光源为一个定点或者光线方向恒定。但范围光Area Light四面八方的光源来自连续的表面,实现方法有本质上的不同。对于离线渲染可以通过路径追踪(蒙特卡洛积分)暴力解算,但是在实时渲染中需要更为巧妙的方式实现。

Sponza scene illuminated by a textured, rectangular area light. [1]
前几天我和同学复刻了一下基于LTC (Linear Transformed Cosines) 的实时范围光渲染,所以打算写一篇笔记分享一下学习过程。教程[3]和文献[1][4]来源会放在文末的引用中。如果笔记内容有误请多多指正
前置知识
前置知识涉及部分辐射计量学、渲染方程以及简易积分。我当时由于这部分基础不太牢(学的不系统),在后续的数学推导中浪费了很多时间去补充,所以这个部分我尽量写细一些。
立体角
用于描述一个立体方位和辐照积分的积分域,通常用ω表示,而θ和φ(或者α)可以看做是空间极坐标系中两个用于描述方位的角度。需要注意的是θ是法向和特定方向的夹角,而不是特定方向和水平圆面的夹角,这个定义会影响后续积分时的表达。

立体角和方位角的符号定义 [2]
值得一提的是,对于立体角的球面积分的过程为:
所以可以理解为在单位球面上的面积微元,其与两个方位角的微分关系为
通过计算球面某一区域的面积即可求得该面积所对应的立体角
辐照度 Irradiance
这个物理量我感觉是PBR中最总要的一个,其定义为:
为辐射通量,在图形学中一般表示为光的亮度(vec3,)
为受光面积,其中为正对面积,的作用是将受光面积转换为正对面积
特别的是,对于辐照度半球积分,假设任意方向的辐照度都为1
所以在图形学中,辐照度积分都要除以π来归一化:
渲染方程
一定程度上,出射光线等于无数条入射光线的加权和,其中BDRF函数()就代表着出射光和某一条入射光的比例关系,我认为这是实现Area Light的核心思想之一

根据光路可逆,左边的可以理解为出射光,右侧的几根为入射光,球面函数为
范围光
在给定视角方向(需要求出出射光线的方向)、法线方向。现在需要做的就是在范围光的光源(也就是自发光多面体)上进行加权采样,最后求得该出射光方向上的光线。

BRDF即渲染方程中的fr,也就是一定程度上的权重 [1]
积分转化
很明显,在实时渲染中无法对几何体进行采样,也无法让BRDF和几何体的球面投影进行相乘,因为时间复杂度太大了。不过学者[1][4]提出了一种很牛的思路,将几何体面采样转换为有限的边缘求和(积分)

投影变换 [4]
某N变形几何体的球面投影(传统积分的对象)为
其球面投影在圆盘面上的投影为
其二者的换算关系为
这个换算过程可以理解为投影,跟辐照度中正对面积的是一个原理
于是神奇的事情发生了,通过积分换底(Jacobian),从原来的球面立体角的面积变成了投影的面积
边缘积分
接下来的任务就是计算多面体在球面的投影在圆盘的投影的面积

signed sum [4]
这部分的操作是逐边缘计算有符号子面积然后求和。给出任意两个圆心指向顶点的向量,公式为:
相当于先求出一个完整的圆弧(两个顶点矢量和球面上的圆弧组成)的面积,然后投影回圆盘
所有边的积分式为:
LTC Linearly Transformed Cosines
Learn OpenGL CN没有翻译这篇文章,我也没找到中文翻译。大致意思是由多种线性变换一个半球余弦分布函数的集合。

原始权重分布函数Do [1]
假设我们的范围光是均一的,辐射通量为定值,则积分公式:
其中不难看出这个积分(加权和)所采用的权重分布函数为上图中的第三种,Clamped Cosine(正半球余弦分布)
其中的同样是为了保证权重的半球积分结果为1
线性变换
很明显,现在的求和方式仅能针对粗糙度为0的情况,来自自发光多边形的入射光全部以漫反射发射出去,为了模拟BRDF对于粗糙度变化、视角变化(甚至材质各向异性变化),需要将初始权重分布函数应用一个三乘三线性变换矩阵去拟合入射光和反射光在不同材质和摄像机角度下的权重分布。这也就是这个技术中“Linearly Transformed”的含义

变化过程可视化 [1]
由此一来,对入射光的权重分布函数将获得近似BRDF的权重分布,即:
这里顺便把渲染方程中的一起乘上了,毕竟是权重分布而不是简单地BRDF分布

拟合情况 [1]
但是根据边缘积分公式的推导,边缘积分仅适用于的分布情况,所以在渲染中要将自发光多面体进行变换。从而计算边缘积分,这步非常重要。

将D变换回Do,将同样过程应用在多面体上 [1]
最终的渲染方程就是
其中第一项是漫反射项,第二项是高光项
和是典中典之菲涅尔过渡,
是指自发光多面体(Polygonal Light)展开的话就是
关于三乘三矩阵
将粗糙度变换、各向异性变换、摄像机角度变换,三个三乘三矩阵依次相乘即可得出用于拟合BRDF的变换矩阵,即
又因为a,b,c,d的具体数字只与两个变量有关:粗糙度、摄像机与法线的夹角。那么接下来就是典中典之查表环节了。将abcd分别对应像素的RGBA,横纵坐标分别为粗糙度(在论文中为了粗糙度变化平滑,采用作为粗糙度轴)和摄像机与法线的夹角。即可存储为一张LUT,在渲染的时候以读取纹理的形式构建变换矩阵
正半球修正 & 假想球面
现在又遇到一个问题,如果仅按照上文中的公式进行边缘积分,那么分布函数是余弦分布函数而不是正半球余弦分布函数。导致的问题是如果光源在切平面一下或者部分在切平面一下,积分所得的辐射通量是错误的。解决办法有二:一、在积分前逐顶点判断并逐一修正,二、在积分公式上修正。因为分支语句对于shader的性能影响很大,所以教程中采用了后者。
首先第一步改变就是修改了边缘积分公式:
边缘积分中的与法向点乘的部分删除了,以至于积分的结果是个矢量。但目前为止结果不会改变,因为点乘具有分配了率:
但是作者并没有这么做。而是假想了一个球面(Proxy Sphere)。(我更偏向于理解为假想圆面)

右边的球面为假想球面
很重要的一个原因是:只要保证假想球面在余弦分布函数的球面的立体角等于范围光的立体角,那么着色点接收到的来自这两个物体的总辐射通量相等,即该球体等效于范围光光源。这个等效法的好处就是用球面计算正半球修正明显方便于任意形状的范围光光源。
计算假想球面的立体角需要两个参数:球面相对表面法线的倾斜度和张角
。假设积分结果为向量
,那么其模长和方向是带有参数信息的:
举个例子,假设θ低于正半球且张角小于θ,证明该光源全部在负半球,那么表面实际接受到的辐射通量有:
我们设为该光源的形态因子,表示这修正前和修正后的比例。为了实时渲染的效率,可以将形态因子预计算后存成另一个纹理LUT。在渲染过程中通过查表直接获得当前范围光相对物体表面的形态因子,进而求得修正后的辐射通量。
效果

论文中的效果 [1]

我和同学实现的效果
其他三维软件中的Area Light大多以矩形和椭圆形为主,但是论文和教程中使用的技术远远大过如此。LTC可以解决任意面数任意形态的自发光光源,并且支持灯光贴图。
关于灯光贴图

这部分还没实现,大致思路是对贴图先进行预积分,跟IBL一样,然后根据灯光距离和表面粗糙度进行线性插值(大概)
实现方法
详见Learn OpenGL网站[3],里面有详细的代码实现和更全面的文献资料。
特别特别感谢大佬同学帮我在渲染器里实现了范围光,GitHub@theta-lin
引用
[1] Real-Time Polygonal-Light Shading with Linearly Transformed Cosines, ACM Siggraph '16
[2] Understanding the concept of Solid Angle, ENGINEERING STREAMLINED (Youtube)
[3] Area Lights, LearnGL guest articles 2022
[4] Geometric Derivation of the Irradiance of Polygonal Lights, Eric Heitz