麻辣土豆模型和动作提取:一、模型和骨骼

系列目录

* “土豆”指的是偶像大师百万现场剧场时光(ミリシタ/MLTD)

在这篇文章里,我讲一下土豆的模型网格(mesh)和骨骼(skeleton)。虽然贴图是我后来才加上去的,不过考虑到叙述顺序,还是放到这里一起讲。


读取模型的网格还是很简单的,Unity Studio 里有比较详细的代码。由于我的应用场景不同,自然要保留它的核心逻辑,调整接口。顶点所包含的信息包括位置、法向量、切向量和 UV。一些额外的信息还有“皮肤”表(见下文)和顶点索引。顶点和顶点索引就不用细讲了,对于会用 3D 图形引擎的这都是基础知识。我绘制选择的是 OpenTK,很快就把白模显示出来了。

麻烦的地方在于,OpenTK 是右手系(Y 轴朝上),而原始数据适用于 Unity(左手系,Y 轴朝上),之后还要放到 MMD(左手系,Y 轴朝上)中。平移的变换还是很容易推导的,X、Z 两次镜像即可。不过旋转,我就没用 OpenTK.Quaternion.FromEulerAngles() 算出过正确的值。后来在 Gist 上有人写了一段代码,我最终采用的是下面评论中的函数。虽然没有仔细去理解它的计算过程,不过既然能用那就先用着吧。

可以看到,每一个网格中一般都有数个子网格(submesh)。每个子网格对应了一种材质(material),每一种材质有一个 shader,同时最多有一个主要贴图(main texture)和次要贴图(sub texture)。子网格和材质的对应顺序就要去检索 MeshRenderer 了。顶点信息中包含了 UV,所以很容易就可以将贴图贴上。通用过程应该是,用子网格索引检索材质,从而检索使用的贴图,并自动导出和链接。在这个过程中,还可以自动除重。不过我为了简单(毕竟这些自动化流程可以以后再加),直接每一个子网格分配一张贴图,剩下的自己去尝试。所以每搞定一个模型的贴图需要花上那么十来分钟。

有意思的是,土豆的位置设置是,模型面朝 +Z 方向,摄像机面朝 -Z 方向。而我在 OpenTK 中想做的是也是如此;到了 MMD 中,就变成了模型朝 -Z 而摄像机朝 +Z。所以在转换到 MMD 坐标系的时候,实际上并不用另一轮变换。

土豆的动画使用了 Unity 的动画系统。可想而知,它的骨骼也使用了 Unity 的骨骼支持。骨骼在 Unity 中的表示形式就是 avatar(翻译成“形象”好像也不对味,就保留原文吧)。如果你用过 MMD4Mecanim,很可能会见过它。MMD4Mecanim 转换生成的骨架使用的是通用(generic)avatar;如果这其实是个人物模型,可以将 avatar 的类别改为类人(humanoid),这样在应用动画和物理计算的时候似乎效果会好那么一点。转换过程和类人骨骼的要求就不多说了,各位可以自己去做实验,同时也可以参考一下这个页面。在改变类别时,Unity 会自动识别对应节点的关节。

通过上次和杨彦君玩的简单 MMD 动画系统(这玩意儿到现在还漏洞百出呢,笑),我才理解,所谓“关节”其实就是一个变换矩阵(transform matrix),“皮肤”就是作用于顶点的矩阵和对应权重。这一切都是线性的,意味着可以进行矩阵加和(权重要先归一化),再进行变换。所以,在实际动画控制中,真正变化(animate)的,是平移、旋转和缩放的值。这个,在 Unity 中就是 Transform,包含平移、旋转和缩放。Transform 是依赖于 GameObject 的,所以每个 GameObject 有它自己的 TransformGameObject.transform 属性)。同样的层级关系实际上保存在了两个地方:avatar 和各个 Transform 实例。骨骼的总体信息也保存在了两个地方,avatar 和 SkinnedMeshRenderer 实例。TransformSkinnedMeshRenderer 都是借助了 asset bundle 内 asset preload data 的序列化,通过 PPtr<T> 的方式保存引用,从而相互链接的。

土豆的每个模型都分为身体和头部两块。(为什么?难道……将来可以随便换头吗?我的意思是,自己的动画系统中确实可以随便换,不过土豆是按照命名规则搜索的资源,所以实际上现在一加载就是正常的一对。)这两块都有自己的骨骼,不过根关节不同。身体的是 MODEL_00,头部的是 KUBI。在动画过程中,二者的运动是同步的。但是在动作数据(下一篇会讲到)中,并没有出现任何头部关节。所以这就是一个暗示,可以将两副骨骼拼接起来。在我的实现中,我改变了骨骼的层次,将 KUBI 设置为 MODEL_00/BODY_SCALE/BASE/MUNE1/MUNE2/KUBI 的子关节,同时将头(KUBI/ATAMA)的运动反馈给身体(MODEL_00/BODY_SCALE/BASE/MUNE1/MUNE2/KUBI/ATAMA)。经过如上的处理,这一套合并的骨骼既适用于土豆的动作,也适用于 VMD 的动作。至于土豆中是怎么实现的,考虑到 KUBI 的初始位置就是在身体的颈部(而不是原点),我猜是先计算身体,然后同步两套 Transform 的值吧。

分享到 评论