# SRPAmbientOcclusion **Repository Path**: alienity/SRPAmbientOcclusion ## Basic Information - **Project Name**: SRPAmbientOcclusion - **Description**: 在Unity上实现了SSAO和HBAO - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-03-28 - **Last Updated**: 2022-07-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简介 环境光遮蔽是计算场景中每一点是如何接受环境光的。而实时渲染中的环境光遮蔽则是对此的模拟。 我们这里实现比较常见的SSAO和HBAO,一个表面点的AO的计算则是对其法线所在半球面的可见性的积分 $$ \displaystyle A_{\bar {p}}={\frac {1}{\pi }}\int _{\Omega }V_{{\bar {p}},{\hat {\omega }}}({\hat {n}}\cdot {\hat {\omega }})\,\operatorname {d} \omega $$ 实时计算的时候离散的积分就好了 ## SSAO SSAO的原理比较简单,其流程比较适合DeferredLighting,一个点的 其实现步骤如下 1. 获取到当前帧的深度图Depth,能获取到法线贴图Normal更好,不能的话直接用ddx和ddy计算也行 2. 对深度图上的每个像素,计算出其ViewSpace的坐标P0,在其法线所在半球面上采样点N个点,计算出这些点的ViewSpace坐标P1 ~ PN,并再次计算出P1 ~ PN的NDC,并采样得到这些点的真实深度值,并重建出真实的ViewSpace坐标V1 ~ VN 3. 对于P1 ~ PN与V1 ~ VN的大小,当V大于P的时候,该点被遮挡,可以根据遮挡的距离给出遮蔽的遮挡积分的权重 4. 最后对所有的采样点都做了遮挡计算后平均,得到当前点的AO值 实现上我们是直接抄的Unity实现版本,从中比较有意思的点是GPU上的伪随机,因为我们需要在单位半球面上随机采样 ```c //From Next Generation Post Processing in Call of Duty: Advanced Warfare [Jimenez 2014] // http://advances.realtimerendering.com/s2014/index.html float InterleavedGradientNoise(float2 pixCoord, int frameCount) { const float3 magic = float3(0.06711056f, 0.00583715f, 52.9829189f); float2 frameMagicScale = float2(2.083f, 4.867f); pixCoord += frameCount * frameMagicScale; return frac(magic.z * frac(dot(pixCoord, magic.xy))); } // Pseudo random number generator with 2D coordinates float UVRandom(float u, float v) { float f = dot(float2(12.9898, 78.233), float2(u, v)); return frac(43758.5453 * sin(f)); } ``` 这基本是在GPU上能找到的比较好用的随机数生成方法了 ![SSAO实现参数没调](https://img-blog.csdnimg.cn/20210408015720807.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Rlbmd5aWJpbmc=,size_16,color_FFFFFF,t_70#pic_center) ## HBAO 相比于SSAO,HBAO则是扩展了采样点的启发性,参考[2],具体代码实现则是参考[3] 环境光遮蔽的半径定义在ViewSpace,我们实现中也是 1. 获取到当前帧的深度图Depth,能获取到法线贴图Normal更好,不能的话直接用ddx和ddy计算也行 2. 对深度图上的每个像素,计算出其ViewSpace的坐标P0,在其法线所在半球面上采样点 1. 当前点Tangent-Bitagent平面上随机朝着比如4个方向采样 2. 当前点Tagent-Normal的随机一个方向上做类似RayMatching的操作,第一步还做一个随机的Offset,每个步进,都获取到对应深度的ViewSpace上的点,计算每个采样 $S_{1}$ 的权重,总权重 WAO=0 - $AO(S_{1})=\sin\Theta(S_{1})-\sin t$ - $WAO+=W(S_{1})AO(S_{1})$ 3. 最后把所有采样得到的权重跟采样次数平均一下就可以得到最终的AO效果 ![HBAO实现有噪声没弄明白](https://img-blog.csdnimg.cn/20210408015755975.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Rlbmd5aWJpbmc=,size_16,color_FFFFFF,t_70) 具体详细的流程还是把PPT上的内容抄一遍比较好。 实现中我们采用了跟SSAO一样的伪随机函数 一般AO的实现后都使用Horizontal和Vertical的Blur进行降噪。 还没有尝试过Temporal的方式去降噪,因为AO本来就是低频的信息,所以非常适合这种Temporal的方法 # 引用 [1] [https://zhuanlan.zhihu.com/p/46633896](https://zhuanlan.zhihu.com/p/46633896) [2] [https://developer.download.nvidia.cn/presentations/2008/SIGGRAPH/HBAO_SIG08b.pdf](https://developer.download.nvidia.cn/presentations/2008/SIGGRAPH/HBAO_SIG08b.pdf) [3] [https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf](https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf)