Real-time Global Illumination
全局光照=直接光照+间接光照
间接光照是比直接光照多一次弹射才被接收的光线,被直接光照照亮的表面作为次级光源,对着色点发出间接光照。
3D空间的方法:RSV、LPV、VXGI
Reflective Shadow Mapping(RSV,反射阴影贴图)
RSV是个2Pass的方法。
想得到次级光源照亮的结果,需要知道:
- 哪些表面能被直接光照照到(被直接光照的表面作为次级光源,可以通过阴影贴图得到)
- 每个次级光源,对着色点的全局光照有哪些贡献
例如shadow map分辨率是512*512,那么如果这512个片元都作为次级光源,计算对全局光照的表面p的贡献,计算量很夸张。相当于计算512*512个次级光源对一点p着色的贡献时,可以认为是将该点作为相机,看向这512*512个次级光源,因为每个次级光源都有各自的BRDF,考虑到对不同方向的反射的贡献会不同,这计算量更加夸张。
于是我们做出假设:反射物是漫反射的,也就是接收到直接光照的次级光源是漫反射的,也就不用纠结表面p对次级光源的观察位置了。
既然所有的次级光源都可能贡献到表面p,那么先考虑一个,然后把每个都积分起来。所以RSM就是在SM的基础上,多储存了一些次级光源必须的信息:深度、世界坐标、法线、光源能量。
如图所示,
我们将原渲染方程中对立体角的积分变成了对patch的积分,当patch足够小的时候,不需要积分直接求和累加即可。现在我们还需要知道
我们已经假设次级光源是漫反射的(BRDF是常数)。所以对于每个次级光源patch,有:
此时我们将
注意,并不是所有RSM中的次级光源都会对p点着色有贡献:
- 可见性(要遮挡问题,完美解决需要在每个
点计算深度图,太困难,不考虑)
- 方向(次级光源能够贡献到的方向只有自身的法线平面,着色点能接收光线的方向,也只有自身的法线平面,所以需要计算二者法线余弦值,以保证光线能正常传播)
- 距离(可以考虑距离p点近的几个次级光源,远的不考虑)
考虑到距离问题,可以选择的加速计算方法:将点投影到RSM中,只找RSM中相近范围内的次级光源,而不是空间距离相近的次级光源,并认为越近距离的次级光源对着色点的着色贡献越多。
RSM的优势:容易实现,属于Shadow map的流程。
劣势:1.随光源数量而性能线性下降 2.不算可见性,所以会有不真实效果 3.做出了许多假设,如diffuse的次级光源、按shadow map上的距离当作实际距离 4.采样率的问题,需要在质量和采样率上做平衡。
Light Propagation Volumes(LPV,光线传播体积)
LPV是一种实时的、无需任何预计算的全局光照级数,它提出了用体素来存储传播间接光照的方法。
LPV基于一个物理事实:沿直线传播的Radiance是不变的(发射什么颜色,接收什么颜色)。它解决了:查询任意着色点,从任意方向接收到的光线。
步骤:
- 需要知道哪些位置是次级光源:使用RSM找到次级光源表面,可以适度降低次级光源数量。
- 将次级光源放置到3D网格中(体素),作为光线传播的起点,统计这些起点格子向各个方向的光线分布:预先将场景划分成3D的网格(网格大小一般要比像素小一个数量级);查看每个网格都包含了哪些次级光源,把网格中每一个次级光源到每个方向的光线相加,得到这个格子向每个方向的光线分布。将这个格子的光线分布投影到2阶球谐函数中。
- 在网格中传播光线:传播方式是,对于每一个格子,向它相邻六个格子传播光线,不考虑可见性,收集它收到周围相邻的六个格子的光线影响,并加起来,投影到球谐函数中,迭代所有格子,直到稳定。
- 已知了任意格子所受的光线,进行渲染:经过传播,已知了每个格子受到相邻六个方向的格子的影响了,所以对于任意的着色点,已知它在某个格子,把这个格子接收到的光线,参与计算。
LPV的问题:按格子整体考虑,把整个网格各个位置收到的影响当作统一的考虑,这样对于在同一网格内的受遮挡的两个位置,收到的光照就不真实了,会发生漏光。比如下图,接收到左边网格向右的光线,
的背面也会收到相同的光线影响。
出现问题的根本原因是:网格划分尺度大于最小的空间内几何体尺度。工业界使用可适应的网格尺度,即级联网格。
Voxel Global Illumination(VXGI,体素全局光照)
VXGI将场景整体变成了体素网格(LPV是将空间网格化,VXGI是将几何网格化),然后将场景体素离散化为树状结构并存储光照计算相关信息,在渲染时通过
锥体追踪来计算光照,相比LPV效果更好。
具体方法:
体素化整个场景,将场景描述成小网格
建立层级结构,将小网格组织成层级的结构
-
pass1,计算直接光照,对于每个体素格子,记录入射光线的分布,记录体素中着色表面的法线分布。对于层级结构,将层级内的体素格子的入射光分布和法线分布考虑在一起(并不意味统计在一起,只是集合在一起),计算该层级的入射光线和法线分布。
VXGI体素化.png -
pass2,从相机出发,发射射线。对于Glossy的表面着色点,沿射线的反射方向进行锥体追踪,按锥体的大小进行层级匹配,查找到差不多大小的相应的体素,认为这个层级的这片区域就对该着色点的全局光照有所贡献。
VXGI锥体追踪.png
对于diffuse表面的着色点,预先给定好追踪锥体的大小和数量,将每个锥体获得的次级光源相加。
VXGI效果比LPV要好,但是开销大,而且需要提前体素化并层级化,对于动态物体实时的体素化非常慢。由于每个着色点都做一次tracing,所以得到的结果接近离线渲染。
屏幕空间的方法:SSAO、SSDO、SSR
屏幕空间方法:用到的所有信息来自于屏幕,相机无法观测到的信息将被丢弃,几乎相当于直接光照。也就是指,依靠已有的渲染结果做后处理。
屏幕空间环境光遮蔽(Screen Space Ambient Occlusion,SSAO)
环境光遮蔽:描述了物体和物体距离较近时遮挡周围漫反射光线产生的更暗的现象,例如物体相交处更暗,即环境光被遮挡的程度。能够突出模型接触位置的互相遮蔽,突出立体感,是一种全局光照的近似。环境光遮蔽是重要的光照现象,是全局光照的重要组成部分,在屏幕空间中实现环境光遮蔽算法简易且具有不错的效果。环境光遮蔽实现很简单,但是很能突出物体的相对位置。
SSAO:是屏幕空间的全局光照的近似。
主要思路:
- 假设:我们虽然不知道一个点的间接光照,但是假设该点的间接光照接收到的所有方向的次级光照都是个常数(和bulin-phong模型的环境光类似)。
- 与bulin-phong模型不同的是,对于不同的着色点,在这里考虑其不同方向的可见性(与建模的天光相同)
SSAO和Phong.png
AO原理.png
我们可以得知,SSAO的思想是:着色点获取的间接光照来自于未被遮挡的远处光线。 - 假设物体是diffuse的。
从渲染方程入手。对于间接光照的渲染方程为:
根据上文的假设,物体的BRDF是diffuse的,即一个常数反照率;同时物体接收到的次级光照也是常数,即入射光为常数。根据乘积积分近似公式:对渲染方程的可视项
拆分可得:
其中,是表面反照率,
是我们假定的次级光照常数,而
则是可见项,我们可以认为它表达的是法线半球面上的面积沿法线垂直向下的投影(projected solid angle,立体角的投影),在底面圆形内对对应的可见性按
值加权平均。所以整体SSAO的渲染方程是可见性项的加权平均*常数,所以可以认为是全局光照的近似。所以想要计算SSAO,只需要计算
这样计算是完全准确的,因为我们假设了二次光源是常数并且表面diffuse,所以后项可以看作常数,是绝对光滑的,符合乘积积分拆分的前提条件。
- 在世界空间:每个着色点
向各方向发射射线,非常慢,但是需要简化(或得知某些空间的数据结构,用来加速),整体过程取决于场景的复杂度。
做法是在着色点处向正半球方向发射射线,但是射线是有最大长度限制的,只考虑这个有最大半径的半球哪里被遮挡。 - 在屏幕空间:在后处理Pass完成,不需要预处理,不依赖场景复杂度,很简单,但不是物理上准确的。
并没有从表面上的点向其他方向发射射线,在每个着色点,取世界空间固定大小的球体(由于当时的图形不得知法线信息,只能取整个球,现在的图形默认获得表面位置的法线,可以在法线半球内采样,获得更加精确的结果),在球体内部有随机的采样点,比较采样点的深度与将该采样点投影到屏幕空间后深度缓冲的深度(代表该点能不能被相机看到),得到是否被遮挡的信息,如果没有被遮挡的采样点的个数大于总个数的一半,则认为此处不考虑SSAO问题,否则需要SSAO。
需要注意计算可见性的时候需要:还要将cosθ和可见性加权,由于当时的SSAO不知道法线信息,无法做到与cosθ加权,只能做简单平均。
值得注意的是,这样的近似将在一些情况下产生错误,比如下图红色虚线右边的红点,本来应该对着色点的遮蔽产生贡献,但由于屏幕空间的问题,被错误判断为了遮挡。但这种程度的误差可以接受。
SSAO由于屏幕空间的原因,还会出现伪影的问题,本来不是两个物体交界处,但是由于屏幕空间的深度判断,使其认为该处有遮挡,从而产生错误的环境光遮蔽。
Screen Space Directional Occlusion(屏幕空间方向遮挡,SSDO)
SSDO是SSAO的改进,更多考虑了事实的间接光照。因为SSAO假设了间接光照都是固定值,但是我们参考RSM可以得知屏幕空间的间接光照信息是可以得到的,所以不需要假设间接光照都是统一的。在SSAO的基础上,通过较小的代价在屏幕空间中将之前不可见、视作AO贡献的采样点当作虚拟点光源光源来进行一次漫反射的计算,相对于SSAO增加了不大的开销却拥有了更好的全局光照效果。
SSDO方法与path tracing非常相似:从点向各个方向发射随机光线,如果没有命中遮挡,那么就是直接光照;如果有遮挡就是间接光照。
SSDO假设间接光照来自近处,所以击中的遮挡为它提供了间接光照,没有击中的遮挡提供了直接光照;而SSAO假设间接光照来自远处,是通过间接光照范围在计算照亮率,也就是阴影区。
SSDO由直接光照和间接光照组成,我们关注间接光照的部分
所以我们需要找到哪些表面对p点间接光照有贡献。
方法:对于着色点
可以发现,SSDO中的间接光照光源是离着色点距离较近的位置的反射,而远处的间接光照不考虑,这与SSAO正好相反,也不需要像SSAO一样将环境光视为固定值,而可以通过反射计算来估计。
所以,由上可知
其中,
SSDO的问题:
- 只能表示小范围的全局光照,因为需要在p点半球范围的采样的原因,无法判断远处的物体的间接光照
- p点对其他点的可见性是通过相机到这些点的可见性判断的。比如上图的右侧的A点。
-
只有屏幕空间信息,所以会丢失信息。如下图,随着物体旋转,左侧黄色平面逐渐不可见,随之地面上也失去了黄色平面反射的间接光照。
SSDO丢失信息.png
Screen Space Reflection(屏幕空间反射, SSR)
SSR可以理解为屏幕光线追踪,因为反射其实解决的就是全局光照的事情,SSR仍然是一种全局光照方式,只不过是通过屏幕空间光线追踪的方法。
SSR的假设:被反射的物体与反射表面都在屏幕空间内。
SSR需要解决两个事情:1.求光线和屏幕空间表面的交点 2.有了交点,求出每个交点对着色点的间接光照贡献
为此,需要找到相机与点的连线的反射光线与哪里相交(也就是要反射到哪里的问题):采用光线步进作为模拟光线追踪的方式。只需要从相机向像素追踪出一条光线,然后查询这条光线经过一次反射击中了这个几何外壳的哪里即可。
值得注意的是,光线步进中,步长过短会导致性能下降,步长过长又会导致误差过大,为了实现每次合理的步进步长,采用了Hi-Z Buffer(层级深度缓冲)进行辅助判断。
-
Hi-Z Buffer:对场景的深度缓冲做MipMap,但是不是平均MipMap而是最小池化MipMap。因为,如果光线不与上层MipMap相交(上层更近),那么更不可能与下层MipMap相交。使用尝试步长的方式,先用最低level的MipMap,如果不相交,level+1再进行判断,以此类推,如果相交了,level-1去确定到底与哪块相交。
SSR层级tracing.png
这样就找到了反射的像素(次级光源),我们假设次级光源是diffuse的。然后可以计算着色了,针对着色点的不同BRDF,如果着色点BRDF是glossy的,则在BRDF的反射方向追踪光线,计算反射;如果着色点BRDF是diffuse的,则要采用多条光线进行蒙特卡洛采样,但这样的消耗较高。
对于各种类型的光滑表面,SSR均可以对其进行处理,只需在代入反射方程着色时采用不同的BRDF即可。对于需要追踪多条光线的着色点(粗糙程度高),可以考虑空间上的复用,采用临近像素中着色点追踪到的反射像素来源当作该着色点追踪到的像素,这样可以利用较高的效率得到较好的效果。
优势:对于glossy和镜面反射性能非常好(diffuse比较慢),质量很高,没有遮蔽问题
劣势:屏幕空间的丢失信息问题,漫反射并不方便