Lecture 08 Real-time Global Illumination (screen space)
GI in Screen Space
- 只使用屏幕空间的信息
- 换句话说,在现在的渲染结果上做后处理
Screen Space Ambient Occlusion (SSAO)
为什么要环境光遮蔽
- 容易实现
- 增强场景中的物体和物体间的相对位置(立体感)
什么是SSAO
- AO的产生源于全局光照,SSAO是全局光照的近似
- 在屏幕空间
Key idea
Key idea 1
- 我们不知道shading point的间接光照
- 于是对于所有shading point,假设来自四面八方的间接光照是一个常数
Key idea2 & 3
- 考虑了处于不同shading point,间接光照的Visibility
- 假设物体是diffuse材质
Rendering Equation
\[L_o(p,\omega_o)=\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)V(p,\omega_i)\cos\theta_i\mathrm{d}\omega_i\\ \\ 只用在实时渲染中的近似(数学上不成立),将f(x)g(x)的积分拆成f(x)的积分做归一化乘以g(x)的积分\\ \int_{\Omega}f(x)g(x)\mathrm{d}x\approx\frac{\int_{\Omega_G}f(x)\mathrm{d}x}{\int_{\Omega_G\mathrm{d}x}}\cdot\int_{\Omega}g(x)\mathrm{d}x\\ \\ 将Visibity项拆出去,Vibility上下都乘以一个\cos,原因见更深的理解其二\\ L_o^{indir}(p,\omega_o) \approx \frac{\int_{\Omega^+}V(p,\omega_i)\cos\theta_i\mathrm{d}\omega_i}{\int_{\Omega^+}\cos\theta_i\mathrm{d}\omega_i} \cdot \int_{\Omega^+}L_i^{indir}(p,\omega_i)f_r(p\omega_i,\omega_o)\cos\theta_i\mathrm{d}\omega_i\\ \\ 第一项的分母项,对\cos积分,结果是\pi 在AO中,k_A=\frac{\int_{\Omega^+}V(p,\omega_i)\cos\theta_i\mathrm{d}\omega_i}{\pi}\\ 相当于从shading\ point往四面八方看过去,Visibility的加权平均值,从0到1\\ 另一项中,Lighting项假设了radiance是常数,BRDF项假设了物体是diffuse材质\\ 则等于L_i^{indir}(p)\cdot \frac{\rho}{\pi} \cdot\pi=L_i^{indir}(p)\cdot\rho\\ \rho = albedo \]更深的理解
其一
\[\int_{\Omega}f(x)g(x)dx\approx\frac{\int_{\Omega_G}f(x)\mathrm{d}x}{\int_{\Omega_G\mathrm{d}x}}\cdot\int_{\Omega}g(x)\mathrm{d}x\\ =\overline{f(x)}\cdot\int_{\Omega}g(x)\mathrm{d}x\\ 相当于去f在g的support中的平均值 \]条件
- g(x)的support覆盖范围小
- g(x)是smooth的
- 二者满足其一
对于SSAO,g(x)是一个常数,这么做是准确的而不是近似
其二
推导过程中拆分渲染方程时,第一项的分子分母并不是对原先的$$d\omega_i$$积分,而是相当于对$$\cos\theta_i d\omega_i$$积分
Projected solid angle 投影了的立体角
- 立体角是单位球上的一个面积
我们对立体角乘了一个$$\cos\theta_i$$,正好就是立体角从球上投影到一个单位圆上的面积
Projected solid angle $$\mathrm{d}x_\bot=\cos\theta_i\mathrm{d}\omega_I$$这是一个明确的定义
半个单位球投影到单位圆上的面积当然就是$$\pi$$了
所以实际上是将渲染方程中对$$\mathrm{d}\omega_i$$积分当成了对$$\mathrm{d}x_\bot$$积分
简单理解
- 间接光照是常数
- BRDF是diffuse的,也是常数
直接将光照项和BRDF项提出来
实时计算遮挡值$$k_A(p)$$
在世界空间下完成
对几何直接做Raycasting
- 慢,需要简化或者加速结构
- 依赖于场景的复杂程度
如果Raycast的范围大了,那么反射光必定会打到物体,导致Occlusion处处为0
如果Raycast的范围小了,那么可能会遗漏在范围外的Occlusion
最后结果平均起来
- 对于平面物体trace范围是一个半球,结果如图1
- 对于凹面物体,结果如图2
在屏幕空间下完成
在后处理Pass中完成
- 不需要预计算
- 不依赖于场景复杂程度
- 简单
- 没有物理计算
在屏幕空间上利用Z-buffer,在shading point周围随机撒一些点,判断这些点能不能被shading point直接看到
图中红点在物体内部看不到,绿点在物体外部可以被看到
深度图可以当作场景几何的简单近似,利用深度判断点在物体内部还是外部,深度更深说明在物体内部被挡住了
但是出现了一个错误,第二幅图中有一个物体外部的点变成了红点(几何拐了个弯导致外部点被判断成了内部点),但是这样的错误可以接受。
减小采样半径当然可以减小false occlusion的发生,但是这样同时也减小了true occlusion
上一步多算了半个球,因为在shading point后面的点根本不可能挡住shading point,所以只需判断shading point法线方向上的点即可
上一张图是在屏幕空间还不能同时知道深度和法线的时代,这时只能考虑整个球
-
考虑整个球时的情况:
当红点个数过半的情况下才开始考虑AO问题
- 如第二幅图。过半的红点有2个,绿点有3个,所以AO是40%不可见
- 第三幅图共有10个点,9个红点,红点过半,超出一半的红点有4个,所以有AO是 80%被遮挡
- 如果不知道法线方向的情况下,可以通过采样邻近深度点来近似法线方向,但是现代图形管线已经能直接得到法线方向了
在屏幕空间上无法考虑渲染方程中的$$\cos$$项,所以这样得出的Visibility是一个近似的往半球上不同方向上的平均而不是不同$$\cos$$的加权平均,但是在工业界能够接受
选择采样数
- 越多采用效果越精确
- 一般为了性能16个采样点即可
- 可以先少量采样点采样,得到一张noisy的AO纹理,再denoise
Horizon based ambient occlusion (HBAO)
SSAO的改进
原理同SSAO,不过在现代图形管线可以得到法线信息,所以只需采样一个半球,并且由于有法线方向,就可以在$$\cos$$方向上加权平均,得到的结果更加精确(因此HBAO需要额外存储一张normal map)
并且HBAO不想SSAO只考虑采样点深度是否比shading point小,而是会考虑一个小的范围,就可以减小false occlusion。
Screen Space Directional Occulusion (SSDO)
- SSAO的改进
- 没有必要假设次级光源都一样,但不通过RSM来记录,而是通过camera,做法类似path tracing
- 从shading point出发,随机打出一条光线
- 如果没有打中物体,则是直接光照
- 如果打中一个物体,则是间接光照
SSAO与SSDO对比
-
SSAO是假设物体能够收到间接光照,如果发射一条光线打中物体则被遮挡,无法收到间接光照,打不到说明无遮挡,能收到间接光照
-
SSDO是假设物体收不到间接光照,如果发射光线打中物体,则收到该物体的间接光照,打不到则没有间接光照
-
二者的思路是相反的
-
二者都是考虑一个范围内。
- SSAO认为间接光照来自于远处,所以范围内光线无命中说明未被遮挡
- SSDO认为间接光照来自于范围内的物体,如果范围内能打到其他物体,则其他物体都会反射出间接光照
-
因此二者都不完全正确,正确做法应是将二者一块做
- 来自近处的间接光照部分可以用DO捕捉到
- 来自远处的间接光照部分可以用AO捕捉到
做法
\[对于Visibility可见的项,说明是直接光照\\ L_o^{dir}(p,\omega_o)=\int_{\Omega^+,V=1}L_i^{dir}(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_i\mathrm{d}\omega_i \\\\ 对于Visibility不可见的项,说明是间接光照\\ L_o^{indir}(p,\omega_o)=\int_{\Omega^+,V=0}L_i^{indir}(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_i\mathrm{d}\omega_i \]假设物体都是diffuse的,那么它反射到任何一个shading point的radiance肯定与cameara看到它反射出的radiance是一样的,这种情况就能直接算它的贡献
- 做法类似HBAO,测试采样点的深度
- 关于PA会不会被挡住,并不是真正trace一条光线,而是从camera出发,看向A发现A在最小深度之后,说明PA是看不见的,B、D同理,C则不会被挡住,则从P往C方向差环境光的值
- SSDO除了解决间接光照外,还解决了从环境光来的直接光照
- 当这些采样点的贡献都算好了,则可以通过法线方向,将朝向P点的采样点(B、C、D)贡献加起来
- 图3的结果就出现了错误的结果
- PA并没有被挡住,但是SSDO却认为它被挡住了
- PB实际上被挡住了,但SSDO认为它没被挡住
- 假设camera是Q,QA和PA的Visibility肯定不一样,要解决这个问题得真正trace一条光线,SSR就解决了这个问题
SSDO的问题
- 只能表示小范围的全局光照
- 用camera的可见性判断shading point的可见性,不准确
- 屏幕空间问题:丢失了未被看见表面的信息
Screen Space Reflection (SSR)
可以理解成Screen Space Raytracing
- 在屏幕空间做光线追踪
- 不需要知道整个场景的3维的网格、加速结构等等,只需屏幕空间的信息
两个问题
- 求交:在任何光线和在camera下能看到的场景中(这里的任何:不限于考虑反射光,而是考虑)
- Shading:相交像素对shading point的贡献
Basic SSR algorithm - Mirror Reflection
- 对于每一个像素
- 计算反射光线
- 利用深度缓冲区trace光线方向
- 使用相交点的颜色作为反射颜色
SSR并不只能做镜面反射,镜面反射是specular reflection
SSR也可以做glossy reflection,glossy没有specular那么光滑,但也不是完全不光滑
glossy反射出的光线不是一根,而是一个lobe,在lobe中trace多条光线。给定一个入射方向(观察方向),出射方向由BRDF决定,lobe越细,出射光线越少,lobe越大出射光线越多(蒙特卡洛方法多采样几根)
如果地面不平,反射光往不同方向做也能完成(法线)
不同位置有不同roughness(也能用smoothness表达,与roughness相反)
做法
-
Linear Raymarch
找到相交点
- 每次步进一段距离,检查深度值
- 渲染质量取决于步进值
- 可以被精制
- 这里的步长必须是定值,因为没有SDF
如图中橙色点小于场景深度,说明还未相交,当到达绿点,大于场景深度,说明发生相交
-
Hierarchical ray trace
每次步进一步固然精确,但如果能确定每次能一次步进几步就更了
通过生成深度图mipmap可以拿到这个信息,这里的mipmap和平时说的不一样,这里的mipmap每高一层存储的不是上层4个像素的平均深度值,而是最小深度值
-
为什么是Depth Mipmap
- 类似于3D空间的BVH或KD树
- 允许立刻跳过很多不可能相交的像素
- 使用最小值保证保守的逻辑
- 如果一个pixel和上层最小的texel都不可能相交,那么更不可能于下层子节点相交
- 这样就能快速跳过很多格子了
- 比如图2发生了相交,那么看相交的是高一层中的两个节点的哪一个,来判断要继续看哪个mipmap
-
伪代码
mip = 0; while (level > -1) //有两种情况光线停止往前 //第一种是找到交点 //第二种是找遍了屏幕也没找到交点 step through current cell; if (above Z plane) ++level; if (below Z plane) --level;
试探步:刚开始是步进小段距离,如果没有发生相交,步长逐渐变大(相当于level提一层),如果发生相交,应该进行更精细的查找(level降低一层)。
这里的情况:现在level0走一格,未相交,于是在level1上走一格,这样相当于在level0走了四格。
当发生相交了,后面就不能走这么大步进了,于是在第一层level上步进
- 工业界若找到交点不一定是
--level
- Mipmap必须2次幂对齐
- 做不了起点不在2的K次方上的平均,如图中level1中第1、2格是一起的,第3、4格是一起的,无法计算第2、3格在一起的情况
- 工业界若找到交点不一定是
-
问题
- Hidden Geometry
SSR的问题完全是Screen Space的问题,丢失了camera看不到的信息
-
Edge Cutoff
地板应该反射出完整的窗帘,而由于剩余的窗帘超出屏幕而被截断了
一种解决方法是根据反射光走的距离做一个衰减,将结果虚化掉了,边界也就不明显了
Shading
SSR只是将光线于场景中的物体求交改为了光线于屏幕空间求交,所以原先path tracing中的任何算法都能直接使用
\[L_o(o,\omega_o)=\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_i\mathrm{d}\omega_i \]这里$$L_i(p,\omega_i)$$由$$L_o(q,q\rightarrow p)$$获得,因此我们必须假设反射物的diffuse的,因为反射物的radiance来自屏幕空间中的像素, 是光线经过反射物反射到camera的radiance而不是到任意点的radiance
反射接收物可以是任意材质的
问题
- 这里没有涉及到平方衰减,因为这里做的是BRDF sampling,而不是从某些次级光源对其area进行积分,来计算次元光源对shading point的贡献
- 这里处理好了可见性问题,因为这里在任何的p点往任何方向trace,找到的都是第一个能够看到的物体,保证了没有看不见的物体
- 如果做Light sampling一定会有上述的两个问题
Shading中SSR的好处
因为SSR做的是path tracing,所以只需正确考虑BRDF就能达到
- Sharp and blurry reflections 可以做锐利或模糊的反射
- Contack hardening
- 图中小恶魔手指部分的反射比较清晰,而离手指越远的地方越模糊
- 入射光打到某一个shading point,会有一个锥形,锥形大小由BRDF决定,shading point离反射物远,则传播越来越大,那么就相当于收集了一个大范围内所有的点对shading point的贡献,而反射物离shading point近的话,就收集了小范围点对shading point的贡献
- Specular elongation
- 雨天可以发现红绿灯上下被拉长了拉长了,因为地面是各项同性的,法线方向相同,那么反射出去lobe应该就是一个长条的椭圆,这样就会出现反射物体被上下拉长
- Per-pexel roughness and normal
- 对于任何点normal不一样roughness不一样只导致反射出去的光不一样,对于path tracing无影响
改进
知道观察方向,知道BRDF,那么反射出的方向应该是集中在一个范围内的(BRDF lobe),那么就不需要均匀地往四面八方随机采样光线,只需要用一种PDF使其于lobe形状一样,就可以将光线分布在lobe内
- 如图当两个shading point都打到同一个反射物,可以通过调整BRDF项快速求出贡献(离线渲染中的方法)
类似SplitSum,先将屏幕空间模糊,这种对于glossy的物体,可以只对其specular的方向查询一次
但是屏幕空间的点,有各自明确的深度,不希望前景与背景一起模糊,所以要考虑深度差异filter,且深度不应该被filter(Joint Bilateral Filter, LBF, 联合双边滤波)
总结
- 好处
- 对于glossy和specular的反色和快速
- 质量好
- 没有spikes和occlusion问题(spikes是距离平方的问题)
- 坏处
- 在diffuse情况下效率不好
- 丢失屏幕外的信息