Lec8~9
1 Screen Space Ambient Occlusion(SSAO)
SSAO:屏幕空间环境光遮蔽 —— 一种对全局光照效果的近似
屏幕空间:只使用从相机出发渲染得到的信息,可以理解额为对已经渲染好的图像做 后处理
。注意与RSM这种基于图像的技术相区别。(RSM的思路是先从light对场景先进行处理,得到一些texture存放的三维场景信息,再进行光照计算)
为什么需要算AO?
- 很低的代价就能近似的模拟出全局光照的效果
- 效果提升很大,让接缝处更暗,使整张图片更有层次感
关键思想
- 不知道入射光的一切信息。假设入射的间接光是常数,没错就类似于Phong模型的环境光项
- 虽然假设着色点的环境光(间接光)是常量,但不同的着色点拥有
不同的visibility项
。即考虑不同方向有不同的可见性
。比如从着色点的法线方向生成半球,探测并计算着色点被其他物体或自身mesh的其他部分遮挡的百分比
AO理论基础(依然是渲染方程)
- 借助经典product integral的近似拆分公式(乘积的积分 ≈ 积分的乘积)
- 我们的关注点是Visility项,因此将其单独拆出
- 篮框部分: 分母为半球上对 c o s θ i cos\theta_i cosθi的积分,就是π。则可以写成下面这样,并且给了个新的名字 k A \large k_A kA。这个公式就是 计算着色点p在球面上所有方向的Visibility项的加权( c o s θ i cosθ_i cosθi)平均值
- 红框部分
- L i i n d i r \Large L_i^{indir} Liindir:间接光照的入射强度Radiance,我们已经假设为常数
- f r \Large f_r fr:设为diffuse材质,即 ρ π \Large \frac{ρ}{π} πρ,剩下的又是半球上的cos积分,结果为π。最后的计算结果就是由用户定义的两个常量:环境光强度、Albedo
更深入理解
-
这个公式的左边部分,就是一个标准的连续函数在 Ω G Ω_G ΩG范围内的均值, f ( x ) f(x) f(x)在AO计算中为Visility term,因此就是对半球上的可见性求平均。另外,由于g(x)是常数,所以AO计算中的 这种拆分方式是非常
准确
的
-
注意我们在上面用这个公式的时候,把cos项也跟随这拆出来了,为什么可以这么做,很违规啊?
- 其实并不违规,因为cos其实可以跟dω合并,当成一个 d 某某 d_{某某} d某某,这里要引入一个概念(Projected Solid Angle,投影立体角),立体角就相当于单位球上面积与 1 2 1^2 12的比值,也就是面积。而 c o s θ i d ω cos\theta_id\omega cosθidω相当于把球上的单位面积投影到底面的圆内,而积分区域就从半球变为圆, 也就是说下面这个积分就相当于是求单位圆的面积,即π
- 其实并不违规,因为cos其实可以跟dω合并,当成一个 d 某某 d_{某某} d某某,这里要引入一个概念(Projected Solid Angle,投影立体角),立体角就相当于单位球上面积与 1 2 1^2 12的比值,也就是面积。而 c o s θ i d ω cos\theta_id\omega cosθidω相当于把球上的单位面积投影到底面的圆内,而积分区域就从半球变为圆, 也就是说下面这个积分就相当于是求单位圆的面积,即π
更简单的理解AO
- 环境光、BRDF为常量,直接提到积分外
AO总结:shading = 平均Visility x 自定义常数
前面准确地用公式展示了AO是什么,下面就讨论如何在屏幕空间实现它
屏幕空间实时计算任何一个着色点p的AO值 k A ( p ) k_A(p) kA(p),
- 如果是三维空间中,从着色点向法线所半球的一定半径内发射光线去探测,看遮蔽情况是怎样的,计算出一个可见性百分比,但是太慢
- 屏幕空间中可以利用z-buffer
- 在场景的所有对象经过渲染管线后,在得到相机可视范围内的场景图像时,都会同时生成一张zbuffer,因为光栅管线必须做深度测试,所以zbuffer是白给
- z-buffer可以近似的代表场景的几何信息的,如下图,从下往上看,可以看到物体表面的所有点的深度分布。因此可以选择在以着色点为球心的球内撒一些随机点,然后再把他们挨个做深度测试,如果深度比深度图上的记录更大则表示在物体内部,如图红色标记,绿色则表示没有阻挡,因此可以计算出可见性百分比
- 本来应该考虑是法线所在半球,但是咱们没有法线信息,只能使用整球,因此规定:只有当红点超过一半,才应用AO 。比如下图左1着色点的周围可见性为1,2着色点可见性为0.6, 3号点就0.2
- 因为没有法线信息,因此cos项(当前采样方向与法线夹角的cos)也就忽略了,但是效果也还行
SSAO这种做法,不可避免的会有下面这种的瑕疵。单纯的把采样点投影到相机处进行深度测试的坏处,会认为采样点被石凳所遮挡。
太多的采样点会影响性能,因此传统手艺又来了,少量采样点(比如16个)计算出AO结果后,进行 模糊
其实,整个场景的法线信息是很好得到的(实际上,现在的渲染管线,肯定都会渲一张场景的法线)
考虑G-buffer/延迟渲染等概念,在一个pass中,对场景物体的顶点法线信息放进管线中处理,渲染一张存放整个场景相机所能看见的着色点的法线纹理。之后利用这张纹理,就可以在法线的半球生成采样点,并且可以加入cos项(权重),靠近法线的方向权重更大,使得结果更准确
2 Screen Space Directional Occlusion(SSDO)
SSDO是对SSAO的改良,更多的考虑了 实际的间接光照
,能够有 color bleeding
的现象
如下左图为AO,右图为DO,DO就有漫反射物体的color bleeding现象的发生
关键思想
- 计算AO根本没必要假设环境光是恒定的常量
- 咱应该利用那些很容易就能获取到的间接光照的信息。比如找到场景中的一些次级光源,但DO的计算不是跟RSM一样从光源出发,而是从相机出发。毕竟是screen space嘛
思想其实类似于path tracing
- 从着色点p随机发射光线
- 如果没击中任何阻挡物,算直接光照
- 如果击中遮挡物,算间接光照,即假设击中q点,计算q点的直接光照,然后算p的间接光照
SSAO 与 SSDO 这两者的假设其实是 完全相反的
- AO:红色部分当做间接光照强度,橙色部分为非间接光照
- DO:红色部分是非间接光照,橙色部分是间接光照(这个假设更符合path tracing)
- 造成这两者的不同的原因是
- AO假设间接光是来自
无限远的地方
,因此会被其他部分阻挡。 - DO假设间接光来自
附近
,即附近物体表面反射来的,如果附近没有东西就没有间接光(这个假设也会引入一些瑕疵) - 而理论上来说,来自近处和远处的间接光应该都要考虑。。
- AO假设间接光是来自
将光线被阻挡
和 未被阻挡
分开考虑
- 未被阻挡部分认为是 直接光照,正常解渲染方程即可
- 阻挡部分才是SSDO的关注点,即V项为0的那些光路才是DO部分。这就是之前3D全局光照部分研究的东西啊?不从三维空间进行计算才是SSDO的创新点
SSDO步骤
- 基本与SSAO前面一样,在法线所在半球撒采样点,投影到相机处做深度测试
- 通过测试的点(C)所在的方位给着色点P提供
直接光照
- 不能通过的点(ABD)所在三个方位则给P提供
间接光照
(假设ABD点接受到的直接光照反射到P所在方向的能量已经计算好了) - 判断每个提供间接光照的点分别提供多少能量,然后加起来即可,注意A的法线与AP的点乘为负,因此贡献为0
这里的间接/直接光判断,好像跟之前所说的 从p点做raytracing有点不同,还是用的SSAO的深度测试那一套,这样还是会有一样的问题如下
- 相机看A点被挡住,但是PA这条光线实际上是没被挡住
- 相机看B点没被挡住,但PB实际上被挡住了
尽管如此,SSDO比path tracing快,效果已经能够很接近于离线渲染了,但还是有问题
- 跟之前的SSAO一样,只是
小范围的全局光照
- Visibility项并不是真正的P点与采样点之间的可见性,而是借助相机的深度测试,所以不准,但也还行吧
- 一切基于
屏幕空间
的计算都会有的局限性:从相机看向场景看到的是一个壳
,即距离相机最近的那些表面,对于场景中不能看见的部分的信息缺失。比如渲染下面这个物体,不同的摆放角度下可以看到这种局限性的影响:第一种摆放角度下,黄色方块一侧的地板有color bleeding,还不错,但是当慢慢旋转方块到垂直于相机视角,这种现象消失了,因为他不知道这是一个方块!他只知道他所能看到的表面的信息
3 Screen Space Reflection/Raytracing(SSR)
SSR:就是做屏幕空间的全局光照,可以理解为屏幕空间的光线追踪而不需要知道三维空间的几何拓扑结构
主要工作
- 求交:光线与场景(外壳)
- 着色:求出交点们对着色点的贡献
SSR算法可以做很多类型材质的反射
SSR算法大概思路
- 计算反射光线。镜面反射就反射1条,glossy就向镜面反射方向附近一个lobe范围采样多条,漫反射就整个法线半球采样多条。注意跟前面SSAO不同,这里需要用到法线了
- 朝着反射光线的方向进行跟踪(利用z-buffer计算)
- 使用交点处的颜色作为反射的颜色
G-buffers延迟渲染思路
- 在前面几个pass分别渲染
场景
、场景的法线
、深度
到三张纹理上 - 然后根据
法线纹理
以及深度纹理
做屏幕空间光追(SSR) - 最后把计算结果合成到场景图上
SSR的目标是找交点
,采用的方法是Ray Marching
- 对每个着色点,向反射方向生成一根光线,每前进一个步长,就做一次深度测试,如果该点的深度比场景的
壳
更浅,则不是交点,一直重复直到找到交点为止
- 步长的选择直接影响渲染的质量,如下,交点在距离p比较远的地方,我们根本没有必要用比较小的步长去一步步的采样、测试,如果步长太大,又可能直接错过。因此引入一种
分层技术
动态调节步长的方法来加速求交过程
Hierarchical ray trace(层次结构光追)
- 生成深度图Mip-Map,选取的卷积核不是做平均,而是
取最小值
。比如1层的1个texel记录的是第0层对应的4个texel深度的最小值。这个操作非常类似于BVH、KD-Tree等3D场景划分的层级求交加速算法,而这里是对深度所近似的场景做层次加速结构
,这是屏幕空间嘛。(0层:每个像素对应一个深度 1层:大小变为1/4以此类推)
- 加速的逻辑:如果一根光线跟上层的一个大的节点都不相交,那么它就不可能与其子节点有交点,如下面在一维中的例子
从左到右分别是第1、2、1层。每次前进一个步长,如果没有相交,则步长翻倍到上一层去求交,如果还是没有交点则继续增加步长,一旦在某一层有交点了,则应该下降层级(回退),去下一层重新求交,直找到0层。基本上与BVH等加速结构求交一模一样。以下注意事项:- 注意在回退到下一层的时候,没有必要每个节点都求交一次,因为在上一层知道交点在哪里,降层后与对应的子节点求交即可,前面的一定不会交就不用再去判断了,如图4鼠标处为交点,图5中可以跳过与左边的子节点求交
- 注意代码的递归细节,看图5、6、7、8 这个降级过程,图5,在1层上有交点下降到0层 (这之后就是闫老师提醒需要注意的细节),既然0层 子节点1中没有交点,就应该步长翻倍去下一层求交,而这又出现了查到一个位于很尴尬的位置的像素块,比如0层的 1,2号像素 对应1层的1号像素,而0层的3,4号像素对应1层的2号像素,那0层的2,3号像素对应什么呢?
应该对应到1层的1,2号像素做个均值
- 伪代码
SSR的问题(屏幕空间的通病)
- 相机看不见的地方,信息缺失。如下图不符合实际地缺失了手掌部分的反射
再比如这种情况,屏幕空间只能知道这一部分帘子,那么反射就只能做这一部分,本来屏幕之上还有帘子,但是屏幕上没有这个信息,所以反射不出来。一般来说解决办法就是加上一个fading
实际上SSR与路径追踪的原理一样,因此能达到的效果也是差不多的
SSR往后的改进的相关研究
- BRDF重要性采样(偏离线渲染)
已知BRDF,就能算出反射光线的分布PDF,因此没必要均匀在周围胡乱采样,可以靠近镜面反射方向多采样一些,越远就越少
- 相邻着色点的反射光交点复用
时间上的复用在光追部分会讲,在空间上的复用就如下图所示,着色点比较近的,可以把着色点p1的一些交点直接给着色点p2用,而p1对于p2的贡献为多少,可以根据p2的BRDF来计算出来而这样可以有效地节省大量采样次数
- 类似于之前讲的split sum那一部分的操作,先filter后采样 ≈ 先采样后filter
先对环境光做MIPMAP模糊一遍,然后在镜面反射方向查询一次。但是屏幕空间的深度图就有个问题,前景背景污染问题。这些都是比较麻烦的东西
SSR总结
- 优点:对于glossy 和 specular材质的反射计算快、质量高、没有遮挡问题
- 缺点:diffuse物体速度很慢,因为对每个着色点需要散射出大量光线。还有屏幕空间丢失信息问题