当前位置: 首页 > news >正文

基于预计算的全局光照技术

基于预计算的全局光照技术
实时渲染,意味着渲染算法需要在1/30秒甚至更少的时间内生成当前场景在当前视角下的一张2D图像。尽管这看起来只是在时效上的要求不同,实时渲染和离线渲染采用的的确是两种完全不同的渲染方法和架构。

传统的离线渲染算法基本上都依赖于光线追踪,狭义的光线追踪是指根据一条光线的传播方向寻找该光线与场景中表面最近的一个交点,这个过程的计算成本非常昂贵,所以目前的实时渲染算法通常并不能像离线渲染算法那样计算更完全的光线传播路径,而是仅仅只计算所谓的直接光照,然后间接光照通过其它的一些非直接渲染的方法来实现。由于直接光照往往具有比较好的连贯性,因此人们设计出一种高度并行的图形处理器,用于实现对直接光照的高速实时渲染。

在实时渲染中,直接光照又称为局部光照(local illumination),它的计算相对比较简单。相应地,间接光照又称为全局光照(global illumination, GI),所以通常在实时渲染中我们所说的全局光照其实是指间接光照,实时渲染中的间接光照额计算是相对比较困难的,它们通常会使用一些预计算或者其它一些特殊的近似方法。

对于实时渲染来讲,最简单且高效的方法可能是将光照传输过程中的一些计算缓存起来,然后实时渲染可以使用这些预计算的某种形式的数据结果,这些基于预计算的方法仍然是当前阶段实时渲染中比较核心的全局光照算法。

根据其对渲染方程预计算的位置不同,实时渲染中有多种不同的预计算光照技术,他们也分别拥有自己不同的名称,例如预计算辐射传输,辐射照度缓存,辐射照度/亮度体积等等,传统的教材也大都会对它们进行区分并给以单独介绍,在一些实时引擎渲染引擎(如Unreal Engine和Unity等)中它们也表现为看似完全无关的流程和功能。然而从技术从面,它们却存在着很多联系,例如它们大部分都需要基于球协函数和乘机属性来分离出预计算部分,它们也大都需要借助基于图像的光照技术来实现预计算,并且通过比较对不同渲染方程不同部分的预计算处理,我们可以更深刻地理解这些预计算全局光照技术的思路。所以,我们这里尝试将所有这些基于预计算的全局光照技术放到一个章节,除了介绍它们的基本算法,我们可以更深刻的洞悉它们之间的联系,进而更深刻地理解这些全局光照技术。

10.1 数学基础
本章所有预计算光照技术的数学基础是基函数(basis function)的概念及其相关知识。 在数学中,一个基函数是函数空间和(funtion space)中的一个基,就像欧拉(实数或复数矢量)空间中的一个坐标轴一样。在函数空间中,每个连续的函数都可以表示为这些基函数的一个线性组合,正如矢量空间中的每个矢量都可以表示为各个坐标轴(基矢量)的线性组合一样。例如,对于所有二次多项式都可以表示为基函数组{1,t,t^2}的线性组合a1+bt+ct平方,这里a,b和c表示各个基函数的系数。此外,傅里叶变换其实也是将一个任意函数表示为一系列三角函数的线性组合。

所以,如果已知一组基函数,则我们可以使用一个矢量来表述一个函数,该矢量的各个分量就是对于各个基函数的线性组合系数。给定一个定义在T上的函数f(t)以及一组基函数Bi(t),该函数可以通过下面的积分被投影(projection):

然后,原始函数可通过这些基函数与其对应系数乘积(如图10.1(b)所示)的加和形式(即线性组合)被重建(reconstruction),即:

这里N表示系数的数量,一个连续函数通常需要无穷多个基函数的线性组合才能完全被重建,当基函数的数量有限,或者N小于基函数的数量时,上述基函数的线性组合只是原始函数的一个近似,记为。

上面的例子使用了一系列线性(一次)函数作为基函数,它给出原始函数的一个分段线性近似。在工程上最常用的是使用一组相互正交的多项式作为函数基,正交多项式(orthogonal polynomials)是一组具有一些有趣特性的多项式,即任意两个基函数之间都是相互正交的,所以当任意两个基函数的乘机进行积分时,只有当两者完全相同时才会取得非零值,例如:

上式使用了更严格的限制:两个多项式乘积的积分必须返回0或者1,这些特定子集的基函数被称为标准正交基函数(orthonormal basis function)。

有了上述的理论基础,我们便可以使用一个分量为各个基函数系数的矢量来表述或近似一个连续函数。很显然,每个表面处的光照传输是一个关于BRDF分布函数,可见性及几何项的函数,因此如果场景是静态的,那么该传输函数可以被存储为一个矢量而被重复使用。

然而,这里存在一个问题是,完整表述一个连续函数需要无限多个系数,而光照传输发生于每个表面处,即每个表面位置都需要存储一个系数矢量,这显然面临巨大的存储和计算压力。为此,我们必须对每个表面点使用少量的系数对原函数进行近似,相对于由分段函数构成的基函数(如图10.1所示),由多项式组成的基函数还具有另一种特征,不同的基函数能够表述原函数不同频率域的特征,阶数越高则保留的频率细节越多,但是需要的系数数量也越多,因此这给出一种可能,即使用少量的低阶多项式基函数来近似原函数的低频部分,这即是预计算辐射传输算法的核心思想。

本节首先讨论多项式基函数(如勒让德多项式)的一些特征,以及基于球面坐标系下的一种多项式基函数(即球谐函数),然后将在后面的内容中利用这些基函数的特征来实现预计算辐射传输算法。

10.1.1 勒让德多项式
工程中最感兴趣的一类多项式是勒让德多项式(Legende polynomials),记为Pl(x),其定义域为x属于[-1,1],伴随勒让德多项式Pl,m(x)和Pl,-m(x)是勒让德多项式的一般化或者说一般勒让德方程的解,其中l是一个非负整数,而m=0,…,l,这些多项式返回实数(而一般的勒让德多项式返回复数)值。

对于给定正整数m,伴随勒让德多项式可以表述为一般非伴随勒让德多项式的形式,即:

有了式10.8和10.9,便可以推导出任意参数l和m下的伴随勒让德多形式。

伴随勒让德多项式德另一个参数l的意义是什么呢,通过观察式10.8和10.9,随着l的增加,变量x的指数会越来越大,实际上l表示了伴随勒让德多项式的次数(degree of a polynomial),即多项式中所有单项式中指数次数和的最大值。由此也可以对于每一个固定的l,必须满足m<=l,因为m>l时(即倒数的阶数大于多项式的次数),Pl,m的值始终为0。

图10.2展示了开头一些伴随勒让德多项式的图,不难看出,随着次数l的增大,伴随着勒让德多项式的频率变化变得越来越大,因次我们可以使用少数较低次数的伴随勒让德多项式近似函数的低频部分。

10.1.2 球谐函数
满足拉普拉斯方程的函数称为谐函数(harmonic function),或者调和函数,球谐函数(spherical harmonics, SH)则是将球谐函数限制于球坐标系(spherical coordinates)下的单位球面(unit sphere)上,即它忽略了球坐标系的半径r(因为其被固定于球面上),仅取其方向变化thea和fi。因此正如傅里叶级数(圆上的正弦和余弦函数)是一系列用于表示圆上的方向分布函数,球谐函数可以用来表述球面上的方向分布,例如表面的BRDF分布函数,环境贴图,光照传输,可见性分布等,这些都是仅与方向有关(而与位置无关)的函数。

球谐函数是定义在单位球面上的正交基,因此可以使用以下的参数化:

来描述球谐函数,这里s就是单位球面上的一个位置。球谐函数一般使用符号y表述,其定义为:
这里P正是前面介绍的伴随勒让德多项式,而K仅仅是一个用以实现规则化的缩放系数,其值为:

同伴随勒让德多项式一样,我们使用不同的l和m(满足-l<=m<=l)的组合来获取所有的球谐函数,这是一个类似金字塔的数据结构,如图10.3所示,为了简化表述,通常使用特定的顺序将其转换为一个平坦的一维矢量,所以我们又可以使用下面的序列定义球谐函数:

乘机投影
在前面的内容中,我们已经将整个光照计算分为光源(这里仅考虑与位置无关的环境贴图)和光照传输函数两部分,这两部分均可以看做是仅与方向有关的单位球面上的函数,因此可以分别使用一个球谐函数系数矢量进行近似。如果光照交互所处的表面是漫反射表面,则这两个球面函数乘机的积分是一个常数,因该表面上光照的计算完全可以使用式10.17所示的两个矢量的点乘表示,这两个矢量的分量对应于这两个方向分布函数在球面上的球谐函数投影系数,其中光源的投影系数可以动态调整,而表面上的投影系数可以预计算并缓存起来,以供每一帧执行光照计算,这样就能提高静态场景的计算效率,因为对于静态场景,其光照传输是不变的。

然而,当表面是光泽面时,光源和传输函数乘积的积分不再是一个常数积分值,因为摄像机从不同方向观察到的光照是不一样的,因此它是另一个方向分布函数,此时式10.17不再适用,新的问题转换为对光影函数和传输函数的乘积(其结果为一个方向分布函数)的投影近似,即,设c(s)=a(s)b(s)为两个方向函数的乘积,我们需要对函数c(s)执行另一个投影近似。

根据投影的定义,其投影系数可表示为目标函数和球谐函数乘机的积分,因此函数c(s)的投影系数可由下式计算:

在上式的第二行中,a(s)和b(s)分布被其投影系数近似,是一个三重积分张量(triple product tensor):

是一个3阶对称张量,这意味着如果在每个表面位置处存储完全的光照传输,这需要每个顶点存储一个三维矩阵,这显然占用大量的存储资源。但这里我们可以把光源部分抽取出来,即假设光照传输(即a(s)是已知的(固定的)而光源b(s)是未知可变的,那么表面上每个位置处的光源传输仅需要一个二维矩阵,该矩阵可以通过下式计算而出:)

卷积的投影
本书前面已经讨论过卷积的意义,它通常用来对原始信息进行平滑和过滤,在卷积计算中,一个核函数h分别作用于一个信号函数f的每一个位置处,其结果是一个更平滑的函数,记为h*f。

核函数同样可以用来平滑一个用球谐函数表述的方向分布函数,给定一个圆对称(circular symmetry)的核函数h(z),通过对一个原始函数f执行卷积计算可以得到一个新的球谐函数,注意,这里的核函数h(z)必须是圆对称的,即垂直于z轴的平面上值相同,因此核函数可以只使用一个变量z表述,这样才能保证卷积的结果依然位于球面S上。

在球面上,卷积可以直接作用于方向函数的频率域(类似于傅里叶级数,球谐函数实际上就是方向函数的频率域),即:
在这里插入图片描述
即函数f的卷积就是该函数每个带l内的所有球谐函数fl,m被核函数的m=0项hl,0执行一个缩放操作,因为核函数是圆对称的,所以每个带l内仅只有m=0项(该项球谐函数又称为带谐函数)具有非零值。

该卷积属性提供了一种快速的方法用于计算环境贴图于余弦核函数h(z)=max(z,0)的卷积计算,该操作可以得到一个辐射照度图(irradiance map)。此外,卷积计算还可以用于对环境贴图执行预过滤操作以得到一个更平滑的环境贴图。

10.1.3.2 ZXZXZ 旋转
上述的递推方法不对旋转矩阵的形式做出任何已知的假设,对于每一次旋转的计算,它都从最低的次数开始递归计算,以构建整个旋转矩阵的表达式。根据[xxx],递推方法构建旋转矩阵的计算复杂度约为O(l三次方),对球谐函数系数矢量执行旋转操作的复杂度也为O(l三次方)。

[xxx]发现,球谐函数围绕z轴的旋转具有比较简单的形式和计算量,因此提出一种称为ZXZXZ的旋转方法,该方法构建旋转矩阵的复杂度约为上述递推方法的一半,并且ZXZXZ方法直接推导出一个显示公式然后直接作用于系数矢量,因此省去了显式执行旋转变换的操作,进一步提高了旋转计算的效率。

该方法首先使用符号积分(symbolic intergration)表述出球谐函数旋转矩阵中每个元素的函数,即:
在这里插入图片描述
上式具有什么意义呢?首先,我们需要解释以下什么是符号积分?符号积分跟传统的数值积分具有类似的含义,唯一的区别是数值积分计算的结果是一个确定的值,而符号积分的结果是一个函数,也就是说被积函数中包含除积分变量以外的自变量,可以理解为符号积分就是寻找不定积分的公式形式,一次你上式10.26是一个函数,它给出球谐函数旋转矩阵Rsh中每个元素的形式;其次,那么旋转矩阵是关于谁的函数呢?在式10.26中,R是一个使用欧拉角表述的三维旋转矩阵,因此上述旋转矩阵元素就是关于欧拉角的函数;最后,为什么式10.26可以用来求球谐函数的旋转矩阵呢,这是因为yiR(s)可以看作旋转后的球谐函数基,回想一个函数与某个基函数乘机的积分可以表述为该函数在该基函数上的投影,而坐标变换正是关于每个原始坐标轴(函数)在新坐标系各个基函数上的投影,所以式10.26的积分可以用来表述球谐函数的旋转矩阵。

因此,如果能够直接求出式10.26的形式,则可以直接将其用于球谐函数系数矢量的旋转操作。例如,对于围绕z轴的旋转,其可以表述为以下的形式:
在这里插入图片描述
根据上式,我们可以得出围绕z轴的开头三个带的9x9旋转矩阵为:
在这里插入图片描述

上述的矩阵形式也可以推广到次数更高的球谐函数,其中带l对应的项为lα的正弦和余弦函数。从上述矩阵的结构也可以看出前面介绍的递推方法的思路,因此高次数的旋转矩阵对应的正余弦函数可以展开为低次数旋转矩阵对应的正余弦函数。
上述的矩阵Zα也揭示了一个特征,即围绕z轴的旋转矩阵具有比较简洁而稀疏的形式,因此我们可以设想将整个旋转矩阵Rsh分解为Rα与其他基本矩阵的组合,根据欧拉旋转定理,[xxx]使用了ZXZ的旋转组合,这样三个基本旋转矩阵的其中两个都可以使用上述的围绕z轴的旋转矩阵Rα,剩下的问题只需要计算出围绕y轴的旋转矩阵即可。[xxx]进一步将该基础矩阵分解为三个矩阵的组合:首先围绕x轴旋转90度,然后在围绕z轴旋转β角度,最后在围绕x轴旋转-90度。其中,由于围绕x轴的两次旋转角度是固定的,因此这两个旋转矩阵可以被预存起来,即:
在这里插入图片描述
根据上述过程,原始的旋转矩阵被分解为以下ZXZXZ的旋转矩阵的组合:
在这里插入图片描述

相关文章:

  • java实现多线程(下)
  • 球谐光照——杂谈——待完成
  • 基于体素的全局光照技术
  • 路径追踪技术
  • 辐射度方法
  • [计算机体系结构:量化研究方法]学习笔记:Chapter 1
  • 基于预计算辐射传递的全局光照技术
  • 傅里叶变换
  • PhpStorm插件之Translation
  • 小波变换原理
  • 如何通俗地理解傅立叶变换?
  • T函数
  • 分部积分法
  • mssql sqlserver 使用SSMS运行sql脚本的六种方法分享
  • 探讨基于球谐函数的全局光照
  • 【前端学习】-粗谈选择器
  • 0x05 Python数据分析,Anaconda八斩刀
  • go append函数以及写入
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript标准库系列——Math对象和Date对象(二)
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • 大主子表关联的性能优化方法
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 记一次删除Git记录中的大文件的过程
  • 算法-图和图算法
  • 通过git安装npm私有模块
  • 说说我为什么看好Spring Cloud Alibaba
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ​决定德拉瓦州地区版图的关键历史事件
  • #NOIP 2014# day.1 T2 联合权值
  • #Z2294. 打印树的直径
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • .NET Core Web APi类库如何内嵌运行?
  • .net 受管制代码
  • .NET成年了,然后呢?
  • .NET大文件上传知识整理
  • .net反编译工具
  • ?
  • ??在JSP中,java和JavaScript如何交互?
  • @KafkaListener注解详解(一)| 常用参数详解
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [3300万人的聊天室] 作为产品的上游公司该如何?
  • [BUG] Authentication Error
  • [C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计
  • [C]整形提升(转载)
  • [C++]STL之map
  • [delphi]保证程序只运行一个实例
  • [Git].gitignore失效的原因
  • [GYCTF2020]Ez_Express
  • [iOS开发]iOS中TabBar中间按钮凸起的实现