Unity骨骼动画优化

发表于2019-03-22
评论1 6.2k浏览
用Unity做游戏经常会用到3D角色,也就会用到骨骼动画。骨骼动画对性能的影响其实非常大的,在说这个问题之前,先来说说骨骼动画的原理。

我之前做过多种骨骼动画,包括写过2D的骨骼动画系统(类似于现在比较流行的龙骨系统),也在flash的stage3D里面写过3D的骨骼模型动画。骨骼动画的原理实际上都是一样的:

首先你需要有一个模型,2D或者3D的,这些模型是由顶点组成的,2d模型的顶点就是一个个四边形的四个顶点,3D模型的顶点就是每个Mesh网格的三角面顶点。

然后,你需要搭建一套骨骼,这些骨骼是树形结构的,也就是有父子连接关系的,父级骨骼在做运动的时候,子级骨骼是跟随父级骨骼运动,在这个基础上然后子级也可以自己运动而不影响父级骨骼。

接下来,你需要把模型的顶点和骨骼做一个对应关系,这就是所谓的蒙皮权重。蒙皮要做的事情,是指定某个顶点受到多少根骨骼的影响,然后在骨骼运动的时候,顶点根据权重的百分比来跟随骨骼运动。比如一个顶点是受到了2跟骨骼的影响,第一根骨骼的权重是30%,第二根骨骼的权重是70%。在两根骨骼同时移动的时候,第一根骨骼向左移动了10米,第二根骨骼向右移动了10米,假若向右是正方向,那么这个顶点实际移动的位置应该就是-10*0.3+10*0.7 = 4,也就是向右移动了4米。在实际的计算中,我们不会这么简单的乘以百分比,是会用矩阵来运算,分别算出正常受到每一根骨骼矩阵影响之后该顶点的最终坐标,然后再乘以百分比相加。

最后再来说说骨骼父子关系。每一个子级的骨骼,需要先获取到它的父级,通过矩阵来转换局部坐标系,算出子级相对父级的局部位移旋转缩放,再将坐标系转换到世界坐标系,得到子级相对于父级的位移旋转缩放在世界坐标的实际位置,得到最终在动画中这根子骨骼的实际坐标。如果一个角色的骨骼数量越多,嵌套的父子关系越复杂,那么这个转换坐标系计算的过程就越复杂,消耗的cpu运算就越多。

针对这个骨骼消耗的问题,历代的游戏引擎都有自己的解决办法。比如很早期的魔兽争霸3的模型动画,是使用了顶点动画的方式。这个做法具体是这样的,首先在3d软件里面先做好模型的骨骼动画,然后在导出的时候,逐帧或者间隔多少帧的计算每个顶点在当前帧的实际坐标,最后只导出顶点的每帧坐标信息而不导出骨骼信息。

这个做法的优点是在游戏运行的时候完全不需要计算骨骼的坐标关系了,直接把所有顶点的当前帧坐标读取一遍,然后设置顶点坐标就解决了。所以在当年模型的顶点数很少(一个角色模型300个三角面左右),角色是固定形态(不需要局部换装),帧率要求不高的情况下,这样做极大的保证了游戏的运行效率。

但这个做法的缺点也是很明显的。随着角色模型面数的增大,帧率的提高,导出的动画文件就增大很多倍,然后由于没有导出骨骼信息,所以也不能在骨骼动画播放的过程中修改某个骨骼的信息做到特殊的动画效果(比如在挥拳的时候手可以根据目标的距离伸长之类),最后也是最严重的,失去了骨骼动画一个基本的作用,就是动态蒙皮换装,每套骨骼动画信息只能针对一个模型的。

之后我在做stage3D的骨骼动画的时候,参考了一部分这个做法,进行了一下改进,改为导入运行的时候,先计算出该骨骼动画的每帧所有骨骼实际坐标,然后存起来。这样就不需要每帧计算父子矩阵关系,只需要直接蒙皮计算。这样做,保证了导出文件的容量(毕竟flash页游还是很讲究文件大小),也能动态的蒙皮换装,因为固定的只是骨骼,和模型没有关系。不过这样做还是不能在播放过程改变骨骼信息做特殊效果。

说了这么多,估计对骨骼动画的原理已经有了一定的了解了。那么接下来自然就想到了影响骨骼动画播放的一些效率问题:

1、模型本身的顶点数

从蒙皮权重的角度可以看出,如果顶点越多,在播放时需要计算每个顶点最终坐标的次数就越多。

2、模型骨骼的数量和复杂程度

从骨骼父子关系的计算可以知道,骨骼越多,计算矩阵坐标系的次数就越多。

为了让骨骼动画流畅,一般来说,我们在游戏里面会规定角色模型的面数和骨骼数量。比如效果稍微好点的游戏,主角3000面左右、小怪1000面左右之类的。然后骨骼数人形角色一般在30根左右。

在Unity引擎里面,对于骨骼有一个优化的功能。下面举一个实际例子说明:

我们导入一个怪物模型,然后在Hierarchy面板,可以看到这个模型上面的所有骨骼。

在project面板选择刚才那个模型的原始fbx文件,可以在Inspector面板看到模型导入的设置参数,在Rig里面有一个Optimize Game Objects的选项,把它勾上。

再把模型拖到场景里面。现在可以看到,模型下面只显示了mesh网格,那些骨骼结构都没有了。这时候播放,可以看到角色的动画是可以正常播放的。

这时候你会想到一个问题,如果我在某些骨骼上面加了一些控制的节点,比如我在手的骨骼上面绑了一个物体,用于武器绑定,那么现在看不到骨骼了,不就不能这样绑定了?其实还是有办法的,再回到fbx导入设置里面:

点右下角的加号,可以选择一些你指定的骨骼排除在外,这些骨骼将不会被隐藏掉。

这时候再把fbx拖到场景里面,可以看到上面出现了我们刚才选择的排除在外的骨骼。

这样处理了之后,首先场景里面不需要生成这么多骨骼,场景物体数量就减少了很多了,然后Unity会帮你精简骨骼计算的矩阵转换(具体处理方式官方没有说明,估计类似于我在Stage3D时的处理),这样可以把骨骼动画播放时的消耗降低。

通过解决影响骨骼动画播放的效率问题,我们就达到实现骨骼动画优化的目的了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引