基于近似演算的Capsule Shadow(胶囊体阴影)

Last modified date

用近似算法绘制胶囊体阴影(非ray marching),更适合移动端。

Capsule Shadow就是用几根胶囊体挂接到角色骨骼,做出一个近似的人形,再计算投影。可以用来模拟物体在阴影里的微弱投影,配合AO可以让阴影中的光照更加有层次感。

网上搜了一些胶囊体阴影的做法,基本原理就是在后处理阶段重建世界坐标和法线,然后用ray marching绘制阴影形状。

用ray marching的话可能不太适合手机端,用trick一点的近似算法也是可以绘制出胶囊体形状的。

先上动图最终效果。(注意角色背部也被画上了阴影,这要先画个stencil抠掉不需要阴影着色的对象):

基本原理:

用PosWS到光源的向量和PosWS到该向量与胶囊体表面最短距离交点的向量的夹角,来表示阴影的扩散系数,最后模拟绘制出阴影合理的形状。(是不是很拗口)如下图,最终要求的就是向量CP和向量LP的夹角。

以左图为例:

  • 先计算直线LP与胶囊体线段AB的最小距离的交点C1和C2的坐标。线段C1C2就是直线AB和LP的公垂线。
  • 由交点C1C2坐标和胶囊体半径,可求得胶囊体表面切点C3。C3与PosWS的连线也就是胶囊体表面的切线。
  • 最后直线CP与LP的夹角就是我们需要绘制胶囊体的系数。

以上算法其实是有误差的,例如右图:如果交点C1在胶囊体线段AB范围之外的话,就要把交点归到A或B的位置,当作球体来计算。点C3就肯定不在C1和C2的连线上了,需要额外去计算C3准确的坐标。这里为了函数简化和统一,就没有再额外做这一步。最终的效果就是胶囊体两头的球体投影会有点扁,但我们正式用这个效果的时候肯定会降低采样做优化的,这点误差其实无所谓。

具体实现流程就是CPU收集胶囊体的信息(AB两个点的坐标和半径)的数组传递给shader,在后处理阶段一个pass做完阴影的计算。由于这个算法结构比较自由,可以让AB两个的点的半径不相等,中间部分的半径就是A和B半径的Lerp值,这样可以做出“不倒翁”的形状,更加方便美术配置角色形状。

这个夹角系数刚好会受到距离影响,距离越远投影扩散模糊程度越高。再加上一点距离fadeout,效果就比较OK了。出于性能节省和最终效果需要的考虑,应用的时候其实只要画角色下半身就可以了,上半身的投影越来越模糊,不需要画了。

再不规则物体表面的投影形状也是OK的。 可以用场景的法线来控制受光面被投影,背光面不做投影。但这种trick的阴影本身就不必要追求真实物理的投影遮挡关系,所以这方面可以看项目具体需求做优化。

要注意计算多个胶囊体投影的时候,要用min最小值来合并,否则重叠部分的影子就会越叠越深。

最终效果差不多就这样。

最后再做个降采样优化,一般来说1/4或者1/6分辨率都还能接受。

总结

  • 因为再算法上尽量优化过,所以相比ray marching是节省了性能的,在手机端跑毫无压力。
  • 除了做降采样优化,未来还可以先用一个pass再角色背后投影位置抠个stencil作范围剔除。
  • 目前我就用了主光源方向,未来可以在cpu端先算出一个合理的光源位置传给shader,例如找出最近的几盏灯,方向跳转的时候再个timer过度一下。

Share

This site is protected by wp-copyrightpro.com