上个月,某策划突发奇想,要在手机上做个全战。要求:在手机上实现上千人同屏作战,而且不许卡

       一般情况下,这种无理取闹的需求我是直接要怼回去的,但是引擎的老大发话,这个功能可以试着做一做。我还能说什么呢,我只能用任天堂开发气球大战时坂本贺勇那句话:“我就试着做一做让你死心”。

老大发话其实我很能理解,两个原因,一个是unity有一个Animation-Instancing插件,所以实现骨骼动画实例化肯定是可行的,只不过在UE4上实现工作量未知。另一个就是我所在的公司是一个比较惯着策划的公司,尽最大可能满足策划的各种不合理需求

       既然要做那就先补一些理论吧,然后就真的找到了相关的文章。推荐一下 GPU Gems 3 中的 Part I: Geometry Chapter 2. Animated Crowd Rendering

       接下来搜索一下UE4有没有前人实现过Animation Instancing,仔细搜索的话还真的发现有,就是这个项目。这个项目基于4.15的改版引擎。拿到这个demo之后,我也改改引擎,改改项目,终于在4.19版本上把demo跑起来了。

       这个东西性能还是很好的。也就是说,理论上,超千人同屏问题不大。那么接下来的步骤,就是在自己的工程使用。

       设计思路: 将模型顶点色的r和b通道填写该顶点所受影响的骨骼索引,在g和a通道存储骨骼的权重。将模型的骨骼变换矩阵烘焙到一张贴图上,在顶点着色器中根据顶点色采样骨骼变换矩阵完成蒙皮。

其实同时还发现一个悲剧,就是无论Unity官方提供的animation instance插件,还是之前的nextRTS demo,都是使用的morph而非skinning,烘焙的贴图是模型中每个顶点的位置变换而非骨骼的变换。这个效率很高,但是动画贴图会非常的大,和模型的定点数成正比。而用骨骼贴图的话,骨骼贴图可以很小,之和骨骼根数相关。

       我首先想到的思路,是在3DS MAX中完成顶点色绘制以及骨骼动画贴图的制作。因为3DS MAX中进行顶点操作非常容易,动画信息也很容易获取。而且导入到UE4的过程中,SkeletalMesh和StaticMesh都能获得无续在UE4中额外转换。

       用了一下午学习3DS MAX的基本操作,用了一天时间学习maxscript的语法。然后磕磕绊绊用了不到一周时间完成了在3DS MAX中将骨骼索引和权重烘焙到顶点色的功能。

       骨骼动画在maxscript中制作的想法在同时被否决,原因:1.3DS MAX制作贴图只能是位图,精度仅有8位。2.3DS MAX中不支持颜色为负值或者大于1的值。所以拿出UE4,开始编写插件吧。精度不止8位,允许负值,允许大于1的值,这个就是HDR贴图嘛。所以在UE4中生成一张有符号HDR贴图,将骨骼动画写到其中。其实这个步骤挺超级复杂简单的。

       之后编写顶点着色器,这个和普通的GPU蒙皮的思路差不多,就是以前是传骨骼矩阵,现在是采样骨骼贴图6次来获得骨骼矩阵(采样一次是一个vec4,三次是一个3x4骨骼矩阵,6次是两个)。

       初次运行,果然如同群魔乱舞。在修正所有细节bug之后,确定了一个问题:UE4中的骨骼索引和3DS MAX中的模型的骨骼索引完全不同,在3DS MAX中将骨骼索引和权重刷到顶点色的方法走不通。所以只能继续编写UE4的插件。扩展SkeletalMesh Editor,使之能导出静态模型的同时将骨骼的索引权重刷到顶点色上。这步本身应该非常复杂,但是UE4提供了MeshUtility工具,可以将SkeletalMesh转换为StaticMesh。将生成的静态模型刷顶点色即可。

UE4提供的MeshUtility有一些小bug,需要修复一下。

       至此运行,播放一个实例的骨骼动画已经没有问题,但是大量实例却有BUG,性能也不是很理想。阅读UE4的VertexFacoty实现逻辑就可以知道原因。

       修改UE4顶点着色器源码(MaterialTemplate,BasePassVertexShader),添加新的宏,在使用skinning instance的时候,让材质中的worldposition节点其实是初始的local position,让world position offset输出变为蒙皮后的local position输入。就可以获得正确的skinning instance。

       完成这个之后,其实还有很多后续优化。比如优化骨骼动画贴图的排列逻辑使之能容纳更多动作,写一个简易动画状态机可以控制多个动作播放,编写接口可以单独控制某个实例的动作。

       这次大概明白了TA的一些任务,真的辛苦,向所有TA致敬。