量子计算9 发表于 2022-12-1 11:37

Unity URP下Capsule AO的实现

效果预览




动态效果

需求背景

人物在非直接光照的区域,缺少明暗对比的效果,导致人物与场景没有较好的融合感。人物表现为悬浮在场景中,沉浸感不足。Capsule AO可以在非直接光照的环境中模拟出动态物体的对场景的遮挡关系,弥补暗处的不足。



开启&关闭ao效果对比

实现原理

Capsule AO也叫Capsule Shadow,是由顽皮狗在SIGGRAPH 2013中提出。它是一种用胶囊体(Capsules)对动态物体(通常为带蒙皮的模型,比如角色等,可以用多个胶囊体来包裹四肢以及躯干等)进行模拟,以实现非直接光照环境下的动态软影模拟的技术。
Capsule AO技术实际上是脱胎于 & 中用球谐函数SH来对球形遮挡体的可见性进行模拟的一项技术。与原技术一样,Capsule AO最开始也是用于表示球形遮挡体对场景的遮挡作用,对于场景中任意一个待计算点而言,其接收到的遮挡信息主要由两个分量组成:



[*]环境光(Ambient Occlusion),这个分量是通过计算球形遮挡体在待计算点上方向半球上的投影面积所占比例来得到。环境光的计算过程非常简单,有现成的公式可以使用。
[*]方向光(Directional Occlusion),这个分量则是通过从待计算点发射的以主光源反方向为方向的射线为轴线以一定角度(此角度由美术同学控制,用于调整阴影软化程度)为锥角的圆锥与球形遮挡体的相交区域所占比例求得。方向光虽然也有现成的公式可以使用,但是由于计算过程稍显复杂,在运行时计算会不太划算。Michal Iwanicki给出了一个预计算方法来降低这一步计算的消耗:通过蒙特卡洛算法在离线时预计算出遮挡体与被渲染点几何关系与遮挡值的函数关系,并将结果存储到一张贴图中,在运行时直接采样使用。



示意图



预计算贴图

虽然经过优化,但原技术在表达长距离软影上仍会产生较高的消耗(至少需要2阶球谐函数进行表达)。因此顽皮狗的Michal Iwanicki在其基础上推陈出行创造了Capsule AO,即用胶囊替代球体来计算的优化处理(这也是为什么这个算法被称作Capsule AO的原因)。
Capsule AO的优化思路大体为:胶囊体实际上可以看成是球体沿着某个方向进行拉伸处理得到的结果(即无数个连续的球体组合而成)。在计算方向光AO分量的时候,可以将前面提到的椎体转换到胶囊体空间,之后沿着拉伸的坐标轴进行缩小(包括角度与轴线方向,这个收缩过程会导致输出的几何体不再是径向对称,从而使得相交检测变得更加的复杂。
不过在给出的算法中,将这个不对称直接忽略,还是按照之前的相交检测算法进行处理,虽然结果不太精确,但是看起来并没有太大的瑕疵,且性能上可以得到更大的提升),从而使得输出的几何体正好对应于之前的球形遮挡体,之后就可以直接复用之前的算法进行计算处理。
总结而言,该技术可以分为三个方面,如下图所示。其中对于Direction term部分,如果项目已经开启了动态阴影,该部分可以使用shadowmap或者volume shadow等技术来表现。



实现拆解


公式推导

在本节中仅涉及Ambient部分。Ambient Occlusion表示以某点为中心的上半球所有方向的光线,其中未能到达该点法线方向所对应的半球的多少,该部分数值与irradiance互为相反数。


我们可以化简环境遮罩等价于在距离d处,有个半径为r的圆球的遮挡系数。其中遮挡系数为球体投射到半球的面积除以半球面积。如果全部被遮挡,遮挡系数即为1。



遮挡系数公式

我们可以假设半球的半径为1,此时立体角和所对的面积在数值上相等(物体投影在球体上的面积是其立体角乘以球体半径的平方)。如下图所示,蓝色球体通过黄色锥体向粉色半球投射阴影,我们此时只需求出对应的立体角,即可获取到遮挡面积。



遮挡示意图

立体角使用下面的公式计算,是对投射阴影物体(本例中为圆球)的表面做积分。其基本思路是先将每一个面元微分(其先与待计算点连线的向量(指向面元)做点乘,后与半径的平方做除法),再在球面上做积分。尽管可以给出对应的立体角计算公式,此时的情况依旧十分复杂。


为了简化计算,我们继续观察。此时球体投射的阴影和球体与圆锥体相交产生的半径为R的圆盘投射的阴影相同,其中D为该圆盘的圆心达到待计算点的距离。


那么首先计算一下R和D的数值: R= \frac{r}{d}\sqrt{d^{2}-r^{2}} , D = \frac{1}{d}(d^{2}-r^{2})
此时引入极坐标进行之后的计算,之前需要计算微分部分,可以写作为 d\vec{A} \cdot \vec{n} = |dA|\cdotcos\beta 。


其中 cos\beta = \frac{D}{r} , |dA| = \lambda\cdot d\varphi\cdot d\lambda 。在带入之前的公式之前,还需要考虑圆盘的角度影响,即cosα。


最终可得到如下等式:


在将R和D的值带入和化简后可以得到最终的计算公式:


最终的公式十分简单,本章中省略了一些推导过程,详细的内容可以参考
部分Shader 代码:
// Sphere occlusion
float sphOcclusion( in vec3 pos, in vec3 nor, in vec4 sph )
{
    vec3di = sph.xyz - pos;
    float l= length(di);
    float nl = dot(nor,di/l);
    float h= l/sph.w;
    float h2 = h*h;
    float k2 = 1.0 - h2*nl*nl;

    // above/below horizon
    // EXACT: Quilez - https://iquilezles.org/articles/sphereao
    float res = max(0.0,nl)/h2;
   
    // intersecting horizon
    if( k2 > 0.001 )
    {
      #if 0
            // EXACT : Lagarde/de Rousiers - https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
            res = nl*acos(-nl*sqrt( (h2-1.0)/(1.0-nl*nl) )) - sqrt(k2*(h2-1.0));
            res = res/h2 + atan( sqrt(k2/(h2-1.0)));
            res /= 3.141593;
      #else
            // APPROXIMATED : Quilez - https://iquilezles.org/articles/sphereao
            res = (nl*h+1.0)/h2;
            res = 0.33*res*res;
      #endif
    }

    return res;
}实现流程

本文着重实现了Ambient部分。首先通过场景深度重建世界坐标,得到需要计算AO的坐标点。其中可以根据当前相机的位置进行剔除优化。最简单的处理就是根据距离进行判断,当距离镜头超过一定的位置,即不计算其遮挡的数据,节省性能。
然后转换到胶囊体空间,遍历每个要计算的胶囊体(在遍历前可以做一些胶囊体的剔除处理,例如仅计算在视锥体内的胶囊体),按照公式计算其遮挡系数。该步骤将所有的计算结果统一输出到目标RT上。
最后,在后处理之前使用特定的shader采样该RT。因此只有使用该shader的物体上(例如只在场景建筑)才会叠加Capsule AO效果。这里可以增加强度参数进行ao效果控制。



实现流程


人物的胶囊体添加效果图如下所示,最好是尽可能的贴近模型,且稍微超出模型一些。



胶囊体示意图

在计算AO时,增加了一个trick的方式。通过控制计算的夹角大小,来调整AO的软硬程度。由于该方法不科学,因此就不展开说了。对比图如下所示,最终效果看起来还可以。



左边为较软的效果,右边的效果锐利

参考


[*]^SIGGRAPH 2013http://miciwan.com/SIGGRAPH2013/Lighting%20Technology%20of%20The%20Last%20Of%20Us.pdf
[*]^Ambient Occlusion技术方案综述https://www.jianshu.com/p/7d0704442306
[*]^UE4中的Capsule AO/Shadowhttps://zhuanlan.zhihu.com/p/368039787
[*]^Capsule AO 的实现https://zhuanlan.zhihu.com/p/460444838
[*]^Sphere AOhttps://iquilezles.org/articles/sphereao/

APSchmidt 发表于 2022-12-1 11:44

这等好文!牛逼!收藏了![赞同][赞同][赞同]

XGundam05 发表于 2022-12-1 11:44

写的好棒,学习学习[赞同][赞同]

pc8888888 发表于 2022-12-1 11:49

其实星球大战绝地:陨落的武士团最早运用了,后来原神也用了

mastertravels77 发表于 2022-12-1 11:54

陨落的武士团是ue4自带的sdf拟合的胶囊体阴影
类似的效果最早是2013年的tlou1,使用的lut做椭球体间接阴影,原神也是lut的方案
页: [1]
查看完整版本: Unity URP下Capsule AO的实现