全局光照
Global space
在Global Space中做全局光照,每个shading point都会有一个三维坐标
RSM
Reflective Shadow Mapping是在Shadow Mapping的基础上,做的全局光照(2 bounces)
思路如下
- 每个点光源都有一个Shadow Map,可以找到所有能被光源直接照亮的点,我们把这些点看作次级光源
- 在渲染shading point \(P\)时,计算所有次级光源,对\(P\)的影响

计算影响如下
其中在\(P\)上对立体角的积分,我们转化为在次级光源对面积的积分
我们要做两个重要的假设
- 所有次级光源都是Diffuse的,这样子BRDF容易计算
- 忽略Visibility,假设次级光源不会被遮挡,因为Visibility很难算

再加上如下式子进行转换,可以消除\(dA\)
所以我们需要记录次级光源得到的Flux

其中因为次级光源较多,次级光源的数量和Shadow Map的分辨率一样大,我们会对Shadow Map做一个采样,而不是使用所有的次级光源,尽量找到与\(P\)最近的光源来计算全局光照
LPV
Light Propagation Volumns假设,radiance是沿直线传播,而且不会衰减
算法流程如下
- 用Shadow Map找到次级光源
- 将World Space划分为三维网格,每一个格子中如果有次级光源,则计算该次级光源朝各个方向的radiance,用SH拟合,作为该格子的irradiance
- 进行迭代,每次迭代,一个网格会对其相邻的六个网格进行传播。迭代几次后结束
- 计算每个shading point得到的irradiance

有时环境中存在如下较小的隔板,比格子的尺度还小
如图中P点和隔板对面的点被划入同一个格子,这样这个格子的irradiance计算是有问题的
所以一般格子的大小会动态变化
LPV方法也是不能考虑Visibility的
VXGI
Voxel Global Illumination用了八叉树,把整个world space,做了层级的划分,划分为小voxel和大voxel
算法需要2 passes,想法类似Photon Mapping,思路如下
- 从光源渲染,计算每个最小的voxel,得到的irradiance和法线方向
- 根据层级迭代,大voxel根据其包含的小voxel做插值,得到大voxel的irradiance和法线方向

- 从Camera渲染,对shading point \(P\)发射Camera Ray
如果是Glossy材质,我们用一个圆锥来代表Camera Ray在\(P\)上反射的光线
然后计算圆锥包含的voxel对\(P\)造成的影响,我们会使用到第1,2步计算得到的每个voxel的irradiance和法线方向来计算
其中反射光线越远,圆锥底面积越大,我们就使用更大的voxel来计算影响

对于Diffuse材质,就使用多个圆锥来覆盖shading point球面

VXGI得到的全局光照效果比较准确,但是rendering时效率较低
而且如果场景是动态的,每次移动,都要重新计算voxel的irradiance和法线
Screen Space
在Screen Space中做全局光照,三维空间被压缩成了一张二维纹理,忽略了深度,看不到被遮挡物的信息
SSAO
Screen Space Ambient Occlusion 是在屏幕空间上对图像做后期处理,得到全局光照
基本思想是延续Blinn-Phong Shading的常数全局光照项
但对每个shading point计算一个遮蔽系数,也就是Ambient Occlusion,乘上常数全局光照,就得到了所要的效果
抽象的算法是,从每个shading point,向外随机发出光线,如果光线在一定范围内被挡住了就不会贡献间接光照,否则可以贡献间接光照

数学推导
从The Rendering Equation开始

我们把\(cos\theta\)和\(\mathrm d\omega\)结合一下,看作单位球上立体角的投影
立体角的定义本身是:立体角对应球面面积 \(\div\) 半径平方
单位立体角投影得到的就是单位圆上的单位面积

于是我们把The Rendering Equation中
\(\mathrm d\omega\)和\(cos\theta\)结合成 “d投影面积”放到一块
再利用The Rendering Equation的约等式,转换成

其中蓝色和橙色部分我们都可以化简成如下形式
AO就是蓝色部分,相当于对Visibility在半球上做一个加权平均
橙色部分,在SSAO中我们把物体都当作Diffuse,而且全局光照当作常数,所以非常好算

算法
First Pass先计算出z-buffer
Second Pass在屏幕空间中,每个shading point,取一定大小的圆,在圆内随机取若干测试点
我们这里把 从shading point到测试点的路径是否会被遮蔽 等同于 从camera到测试点的路径是否会被遮蔽
取到的测试点投影到z-buffer上,查看z-buffer对应texel的深度,如果测试点深度 > z-buffer深度,则代表被测试点遮蔽,标为红点,否则没被遮蔽,标为绿点
通过计算绿点的比例,来计算AO

存在问题
1
SSAO中每个测试点对Shading point遮蔽贡献是均等的
而The Rendering Equation转换成的形式中,还需要乘上cos\(\theta\)
2
有时候SSAO会把距离较远的遮蔽关系计算到AO中,这并不是全局光照的本意,产生了一些不自然的阴影

3
Z-buffer在一些测试点的错误测试,因为我们用从camera到测试点的遮蔽关系来代替了从shading point到测试点的关系
SSDO
Screen Space Directional Occlusion的思路和SSAO相反,在每个shading point随机发出光线,如果光线在一定范围内被挡住了,则贡献间接光照,否则不贡献
从抽象的角度来看,SSAO计算了从极远处传来的间接光照,SSDO计算了从近处来的间接光照

算法
- 一定大小圆内随机取点,找到被遮蔽的点对应的shading point,把这些shading point当作次级光源,计算对点P的影响,计算和RSM中一致
![image]()
SSDO能带来Color Bleeding的效果,但是因为是在Screen Space,被遮挡物带来的Reflection效果就看不到了

SSR
Screen Space Reflection实现了在Screen Space上做光线追踪
得到了各种间接反射的效果

算法思路是,我们想要渲染上图中黄点时,从camera向黄点射出一条光线,假设光线反射后击中绿点,则计算绿点带给黄点的影响即可
关键在于找到绿点
算法
我们在黄点时,用Ray Marching的思路来找绿点
每次步进一定长度,使用Depth Map来测试到达的点是否被遮挡
如果被遮挡就停止
我们想要尽量准确地找到反射击中的点而不是点的后方,又不想要ray marching的步长过短(耗时),采用了下述的方法

将场景的Depth Map做一个以最小值来压缩的Mip map
Mip map中大块信息,计算的是它包含的小块中最小的深度
比如说,最低级的层次,一个texel记录了它自己的深度;再高一级的层次,一个texel记录了它包含的4个小texel中深度最小值

接着使用这个Mip map,进行如下的算法(如下是二维情况的演示)
一开始步进只每次跨过一个pixel
每次走完,就判断是否被当前mip map texel遮挡住了。如果没有被遮挡,mip map上升一个层级,否则下降一个层级,两种情况步进长度都变成新的mip map texel的长度
直到确定和最小的texel相交时结束,或者没有和任何texel相交


计算反射击中的点\(Q\)对shading point \(P\)的影响
需要假设\(Q\)是Diffuse的,因为我们只知道从\(Q\)到camera的Radiance,不知道\(Q\)到\(P\)的Radiance,所以假设Diffuse可以解决这个问题
在\(P\)处做shading,就是根据\(P\)的BRDF做Monte Carlo,\(L\)就是\(Q\)传来的Radiance

对于不同的材质,BRDF有所不同,采样也就不一样
比如Diffuse就要向四面八方射出光线,而Specular就只需要一条光线
存在问题
- 和所有Screen Space techiniques一样,对被遮挡物带来的影响无法计算

- 有时候反射击中的点已经在camera FOV之外,则带来缺角
通过将Radiance做一个根据距离的衰减,可以解决这个问题


浙公网安备 33010602011771号