本篇文章不是特别完整,所以放上在公司做的内部分享的PPT。
SSAO什么是SSAO
什么是AO?
AO(Ambient Occlusion, 环境光遮蔽)可以认为是一种全局光照模型(不同于Phong光照模型等局部光照模型)
AO用来计算表面上任意一点暴露于环境光中的程度,反过来说,就是这一点被其他物体''遮挡的程度'',从而决定这一点的受环境光的光照强度,最终使得渲染结果更具层次感。
老外一句话概括:AO = shadow created by ambient illumination
看两个栗子:
右图没有环境光产生的阴影,感觉很不真实,车像浮在空中。
左图只有直接光照产生的阴影,右图加上环境光产生的阴影,老头的皱纹更明显、更立体。
如何计算AO?
先说结论:很费,知道这一点就足够了,不信看公式。
A({P},\hat{n}) = \frac{1}{\pi}\int_{\Omega}(V(\hat{\omega}, P))max(\hat{\omega} \cdot \hat{n}, 0)d\hat{\omega}
上述公式表示表面上法线为\hat{n}
的一点P,受到周边物体的遮挡值A的计算过程。
其中,\hat{\omega}
表示点P到其切平面正方向的单位半球\Omega
表面的各个方向。V是在\hat{\omega}
方向点P的可见性函数,如果在\hat{\omega}
方向点P被遮挡值就是1,否则就是0(在上图中,可以看到红色虚线被阻挡了,V=1;绿色实线没有被遮挡,V=0)。
In traditional Ray Tracing ambient occlusion is simulated by sampling rays from a certain point, which takes a shape of a hemisphere, and then is checked for intersection with the scene (also called Object Space AO).
所以说,AO不适合在游戏内实时计算。
SSAO应运而生
SSAO(Screen Space Ambient Occlusion, 屏幕空间环境光遮蔽)是用于实时高效的模拟上述AO效果的一种计算机图形学技术。由Vladimir Kajalin在Crytek工作时开发,并在2007年由Crytek开发Electronic Arts发行的电子游戏Crysis(孤岛危机)中第一次使用。
SSAO的基本原理
将深度缓冲(depth buffer)看做是场景的一种近似。
在屏幕空间(2D)中,对每个像素进行处理(片元着色器),对该像素周围的像素进行采样,并通过对比当前像素和采样像素的深度来计算最终的AO。
如何采样?对每个采样点,如何计算其贡献的AO?这是SSAO中最核心的两个问题。先来看几种常见的采样算法:
Crysis中的SSAO
Crysis中的SSAO在一个完整的球体内进行采样:
- 将每个采样点投影到屏幕空间内,以获取在深度缓冲中的坐标。
- 根据上述坐标,对深度缓冲进行采样,获取对应的深度。
- 如果样本位置在采样深度之后(即样本在几何体内),则贡献AO。
这种方法的最终的表现跟采样的数量成正比,但为了性能需要尽量降低采样的数量,采样数低了又会产生“条带状”的感觉。
但是, 对于这种现象我们有一个Trick ---- 可以引入噪声,将每个采样点以原点法线方向为旋转轴旋转随机的角度。 这样的新采样点会变得极其不规则,更加离散化。将低频的条纹转化成高频的噪声。
在Unity中实现SSAO
准备工作:获取像素的深度、法线、坐标
- 首先要让Camera构建屏幕空间的深度和法线纹理。以便在shader中对
_CameraDepthNormalsTexture
进行采样,以获取深度和法线信息。
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
- 获取像素深度、法线(摄像机空间)、坐标(摄像机空间
inline float samplePositionAndNormal(float2 uv, out float3 p, out float3 n) { float depth; DecodeDepthNormal(tex2Dlod(_CameraDepthNormalsTexture, float4(uv, 0, 0)), depth, n); float3 ray = (half3(-0.5f,-0.5f,0) + half3(uv.xy,-1)) * _FarCorner; p = ray * depth; return depth; }
因为上述函数在后续步骤会用在for循环里,所以采样时一定要使用tex2Dlod
而不是tex2D
,具体原因看这里。
根据uv以及depth获取摄像机空间下的坐标的具体解释:根据深度重建像素在摄像机空间的坐标
- 在像素周围进行采样
优化
优化采样算法
- 使用采样纹理进行随机采样
上述采样算法对每个像素都是在水平和垂直方向上采样的
将采样轴的x、y坐标以及采样半径分别存储于纹理的RGB通道中。
static void GenerateAxisPattern() { Texture2D axisPattern = new Texture2D(3,3); Color[] colors = axisPattern.GetPixels(); // 90 degrees / 9 samples = 10 degrees increment. // interleave increments per row and column so that they are evenly spread out. float[] angles = new float[9] { 0, 30, 60, 40, 70, 10, 80, 50, 20 }; float[] lenghts = new float[9] { 0.7f, 0.4f, 0.2f, 0.3f, 0.5f, 0.9f, 0.0f, 0.6f, 0.1f }; for (int i = 0; i < 9; ++i) { Vector2 axis = Quaternion.Euler(0, 0, angles[i]) * Vector2.right; colors[i] = new Color(axis.x, axis.y, lenghts[i], 1); } axisPattern.SetPixels(colors); axisPattern.Apply(); AssetDatabase.CreateAsset(axisPattern, "Assets/SSAOResources/AxisPattern.asset"); }
- 限制采样半径、范围
- 控制采样数量
- 降采样,提升效率
优化AO算法
- 遮挡偏移
- 遮挡方向
- 遮挡强度
- 调制光照
- 距离衰减
进一步优化结果
- 遮挡颜色
- 模糊
附加效果:渗色
参考
https://en.wikipedia.org/wiki/Ambient_occlusion
http://gamedev.stackexchange.com/questions/23/what-is-ambient-occlusion
https://santisanchez28.wordpress.com/2011/07/16/ambient-occlusion-2/
https://zhuanlan.zhihu.com/p/25038820
http://perso.telecom-paristech.fr/~boubek/papers/SAO/
https://forum.unity3d.com/threads/lightning-fast-quality-occlusion-simple-ssao.388820/
http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html
https://developer.valvesoftware.com/wiki/SFM/Ambient_Occlusion