【技术美术百人计划】SSAO屏幕空间环境光遮蔽
一、SSAO介绍
AO
环境光遮蔽,全称Ambient Occlusion,是计算机图形学中一种着色和渲染技术,模拟光线达到物体的能力的粗略的全局方法,描述光线到达物体表面的能力。
SSAO
屏幕空间环境光遮蔽,全称Screen Space Ambient Occlusion,一种用于计算机图形中实时实现近似环境光遮蔽效果的渲染技术。它不是场景的预处理,而是一种屏幕后处理技术,通过在屏幕像素的位置布置随机个采样点,然后通过采样点遮蔽项所占的百分比来计算阴影遮蔽强度。
SSAO历史
AO这项技术最早实在Siggraph 2002年会上由ILM(工业光魔)的技术主管Hayden Landis所展示,当时就被叫做Ambient Occlusion。
2007年,Crytek公司发布了一款叫做屏幕空间环境光遮蔽的技术,并用在了他们的看家作孤岛危机上。
二、SSAO原理

获取深度、法线缓冲
基于深度计算像素坐标、基于法线计算法向半球随机向量
计算像素随机后的坐标
获取随机后的深度并比较
判断加权AO
后处理
公式:
代表
的法线,
代表点
切平面正方向的任意单位向量,
是可见函数,如果点
在
方向被遮挡则为1,否则为0。
由此可见, 计算AO系数是一个颇为昂贵的操作. 一般离线渲染器都会采用Ray-Tracing(光线追踪)或是简化的Ray-Marching(所谓光线行进)算法, 模拟若干条射线以计算遮蔽百分比. 很明显这种方式不可能应用到实时图形渲染中. 尽管目前有一些实时计算AO的新技术, 但是其性能距离普及还有很长的路要走.

上图为基于Ray-Tracing的AO计算模型. 红色的射线表示V = 1, 绿色的射线表示V = 0.
那么我们能否Trade Off, 用差一点的渲染结果来获得更高的运行效率呢? 答案是肯定的, 而且方法还远不止一种. 本文将重点放在SSAO上.
顾名思义, "Screen Space"意味着SSAO并不是场景的预处理, 而是屏幕后期处理. 其原理是在片元着色器中对于屏幕上的每个像素模拟若干个位置随机的采样点, 用被遮蔽的采样点数量百分比来近似表示光照强度系数.
黑色为需要计算的样本、蓝色表示样本法向量
白色、灰色为采样点,灰色表示被遮挡的采样点,据此判断AO强度
图2为法向球形采样该采样方式会导致平面也会产生AO效果。

参考:游戏后期特效第四发 -- 屏幕空间环境光遮蔽(SSAO) - 知乎 (zhihu.com)
https://zhuanlan.zhihu.com/p/25038820
三、SSAO算法实现


NDC空间
很多人初次学习把剪裁空间(Clip Space)和标准化设备坐标(NDC)混淆成同一个东西,事实上它们是完全不同的。
Clip Space是一个顶点乘以MVP矩阵之后所在的空间,Vertex Shader的输出就是在Clip Space上(划重点),接着由GPU自己做透视除法将顶点转到NDC。
透视除法将Clip Space顶点的4个分量都除以w分量,就从Clip Space转换到了NDC了。
而NDC是一个长宽高取值范围为[-1,1]的立方体,超过这个范围的顶点,会被GPU剪裁。

注:在DirectX里面,NDC的z方向取值范围是[0,1],在Unity中可以用UNITY_NEAR_CLIP_VALUE
获取z方向近平面的值,在openGL环境下是-1.0,DirectX中是0.0。

最后通过从相机经过对应像素到远平面的射线乘以线性深度值得到最后的像素坐标。
构建法向量的正交基

切线的计算原理:
因为法线已经确定,所以需要从切平面中随机计算出一条切线。
先随机一个法向半球的向量r。在求得r在法向法向上的投影a,然后再通过r-a得到切线。
AO采样核心

_SampleKernelArray[i]是在c#计算的采样点的随机位置偏移量,先把这个偏移量从切线空间转到观察空间,然后把得到的加上像素点坐标得到对应的采样点坐标。然后再把采样点从观察空间转到屏幕空间,用得到的屏幕坐标采样深度缓冲获取采样点的深度值randomDepth,然后拿这个randomDepth跟linear01Depth来进行比较,如果randomDepth>linear01Depth,那么进行AO加权。
四、SSAO算法改进
随机正交基

因为之前的构建TBN矩阵中的随机向量是固定的,所以为了让TBN矩阵具有随机性,那么需要把随机的向量存入纹理中(需要设置为repeat模式),然后根据平铺来进行随机采样,这样就能得到随机的向量,进而构建随机的TBN矩阵。
AO累加平滑优化

_RangeStrength是设置的阈值,如果randomDepth和linearDepth的差值的绝对值大于我们设定的阈值,那么很有可能是非常远(比如天空),那么就不需要进行AO加权。

同样的,如果随机点深度值跟自身非常近,那么可能会导致明明再同一平面,但是也会出现AO加权,这个时候设置一个非常小的偏移值来比较判断。只有当前像素深度小于随机点像素深度才能够贡献AO。

对于权重部分,是随机向量跟半球法线向量相似度越高,所占的权重越小,反之所占权重就为1。应该是跟法线相近的向量一般大概率是没有被遮蔽,没有太大的参考性,所以所占的权重才会小吧。
对于循环内AO的贡献公式为:
ao+=range*selfCheck*weight;
AO模糊

跟高斯模糊有些相似,也是通过两个一维卷积核进行卷积运算的,但是不同的是,通过中间像素两边像素的法线的点乘再加上设置的阈值来除去特定频域的信息。

nor1和nor2分别是两边的法线,如果法线之间的夹角过大导致点乘小于设定的阈值_BilaterFilterFactor,那么这次运算的权重为0。
参考:UnityShader-BilateralFilter(双边滤波,磨皮滤镜)_puppet_master的专栏-CSDN博客
https://blog.csdn.net/puppet_master/article/details/83066572
五、对比模型烘焙AO
烘焙方式
三维建模软件烘焙AO方式
通过三维建模软件(如3DMax),设定好渲染参数,对模型(单一选择模型实体),烘焙AO到纹理。
游戏引擎烘焙AO方式(Unity3D Lighting)
通过Unity的Lighting功能(主菜单/Window/Rendering/Lighting Setting)进行场景的烘焙,AO信息包含于此。
建模软件烘焙优缺点
优点:
单一物体可控性强(通过单一物体的材质球上的AO纹理贴图),可以控制单一物体的AO的强弱;
弥补场景烘焙的细节,整体场景的烘焙(包含AO信息),并不能完全包含单一物体细节上的AO,而通过三维建模软件烘焙到纹理的方式,增加物体的AO细节。
不影响其(Unity场景中)静态或者动态。
缺点:
操作较其它方式繁琐,需要对模型进行UW处理,再进行烘焙到纹理。
不利于整体场景的整合(如3DMax烘焙到纹理,只能选择单一物体,针对整体场景的处理工作量巨大);
增加AO纹理贴图,不利于资源优化(后期可通过其它纹理通道整合资源);
只有物体本身具有AO信息,获取物体之间的AO信息工作量巨大。
Unity烘焙优缺点
优点:
操作简易,整体场景的烘焙,包含AO的选择。
不受物体本身的UW影响,Unity通过Generate Lightmap UVs生成模型第二个纹理坐标数据。
可以生成物体与物体之间的AO信息。
缺点:
缺少单一物体的细节(可调整参数提高烘焙细节,但换之将增加烘焙纹理数量和尺寸,以及烘焙时间);
受物体是否静态影响,动态物体无法进行烘焙而获得AO信息。
SSAO优缺点
优点:
不依赖场景的复杂度,其效果质量依赖于最终图片像素大小。
实时计算,可用于动态场景。
可控性强,灵活性强,操作简单。
缺点:
性能消耗较之上述2种方式更多,计算非常昂贵。
AO质量上要比离线渲染烘焙(上述2种)不佳(理论上)。
六、SSAO性能消耗
性能消耗的主要方面

AO核心采样消耗说明

使用For循环进行半球随机向量的采用,IF、For等对于GPU计算性能上并不友好。
采用数的数量(上图中的_SampleKernelCount,针对For循环的次数),过低的采用数得不到好的结果;以64为例,1334x750的分辨率,每个像素计算循环64次,合计1334*750*64次AO核心运算;
循环体内部的采样逻辑,每个像素中的迭代需要采样64次的屏幕深度值法线值。
滤波采样消耗说明
本案例采样的是双边滤波(Bilateral Filter),为了保证边缘不被模糊,采样基于法线的双边滤波。

c#后期脚本中,Bilt两次(横向和纵向),合计调用两次滤波渲染Pass;
单一滤波渲染Pass中,多重采样;包括7次主纹理的采用和7次屏幕的法线信息的采用,屏幕中每个像素合计14次纹理采用。
七、拓展链接
【环境遮罩原理】环境遮罩之SSAO原理 - 知乎 (zhihu.com)
https://zhuanlan.zhihu.com/p/46633896
【游戏后期特效第四发--屏幕空间环境光遮蔽 (SSAO)】游戏后期特效第四发 -- 屏幕空间环境光遮蔽(SSAO) - 知乎 (zhihu.com)
https://zhuanlan.zhihu.com/p/25038820
【屏幕空间环境光遮蔽(SSAO)算法的实现】屏幕空间环境光遮蔽(SSAO)算法的实现_小龙虾的博客-CSDN博客_环境光遮蔽
https://blog.csdn.net/qq_39300235/article/details/102460405
【SSAO 与深度重构】第四十六课 SSAO 与深度重构 - 现代 OpenGL 教程(连载) - 极客学院Wiki (jikexueyuan.com)
【Ambient Occlusion(AO)使用指南】Ambient Occlusion(AO)使用指南 - 知乎 (zhihu.com)
https://zhuanlan.zhihu.com/p/150431414
【图形和滤波】图像与滤波 - 阮一峰的网络日志 (ruanyifeng.com)
https://www.ruanyifeng.com/blog/2017/12/image-and-wave-filters.html
【双边滤波】UnityShader-BilateralFilter(双边滤波,磨皮滤镜)_puppet_master的专栏-CSDN博客
https://blog.csdn.net/puppet_master/article/details/83066572