赞
踩
绑定是实现骨骼动画非常重要的一个技术。尤其在人物,生物上非常常用。只要模型上绑定好骨骼,就能通过一套预置好的动画数据来实现一些诸如行走,奔跑等动作。
要在模型上绑定骨骼。一般是在 maya,blender 一类的 3d 软件中实现的。几乎很少(通常也没必要)在游戏引擎中去做实时绑定。一是耗时长,模型越复杂,顶点需要计算的数量就越多。二是大多数美术资源都是预制好的,从模型,贴图到绑定蒙皮。
个人一直关注 procedural generation。经过实践,在游戏引擎内已经可以做到模型和贴图的实时生成。如若能把绑定环节也解决,就能涵盖更完整的流程。
这是近期一个探索成果。整个视频的生物都是实时用算法生成的。优点是形态多样,不占用储存空间。传统的制作流程上,若要出现1万种生物,就需要1万个模型。会产生非常可怕的数据量。虽然能通过提取模块,在一定程度上进行压缩,但灵活性相当有限。
上面视频中的生物模型生成用到了 marching cube + signed distance functions。这里重点会分享关于实时绑定的实现思路以及解决方法。
解决过程
若直接在google 搜索 procedrual rigging 一类的关键词,几乎没有相关教程。(可见这一技术并不常用)而解决这一问题的突破口,是从一个叫 tail animator 插件中获得启发的。该插件提供了一个能在引擎编辑器内绑定指定模型的功能。经过简单的改写,让它支持能在 runtime 中实时绑定,也实现了 Test 403 的版本。
但插件中的绑定性能相当不理想,上述的 lowPoly 风格的模型,仅仅是几百几千的顶点,生成时就能发现可察觉的延迟。所以当时的想法,就是尽量理解内部源码,并设法在 compute shader 上实现,来加速绑定过程
(Tail Animator 中的部分代码)
经观察发现。略去具体的实现细节,和绑定相关,需要进行计算与赋值操作的相关变量如下
mesh.boneWeights = weights; // BoneWeight[]
mesh.bindposes = bindPoses; // Matrix4x4[]
rend.bones = bones; // Transform[]
rend.sharedMesh = mesh;
按图索骥,从Unity的文档中找到了一段关于Mesh.bindposes的范例
(https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html),
其中创建了一个 quad ,以及两个 bone 来进行绑定。并且生成了一个 AnimationClip 来驱动骨骼进行移动。运行效果如图。
* 若关闭掉 AnimationClip ,就能手动移动骨骼顶点,去影响这一 quad 面片
* 让人欣喜的,这段代码是一个最小可用范例,展现了清晰的结构。经过阅读,里面比较陌生的数据类型是 BoneWeight ,查看后结构如下
(官方一个比较清晰的解释:https://docs.unity3d.com/ScriptReference/BoneWeight.html)
Skinning bone weights of a vertex in the mesh. Each vertex is skinned with up to four bones. All weights should sum up to one. Weights and bone indices should be defined in the order of decreasing weight. If a vertex is affected by less than four bones, the remaining weights should be zeroes.根据这一信息,可以看到每个顶点最多可以受四个骨骼节点影响。我们可以人为地分配权重来决定哪个节点对顶点的偏移影响最大。若理解没有错误,你在引擎内看到的所有细腻的绑定效果,都是基于这一规则去实现的 ,非常巧妙。
例子中 weight 的数量为 4。bone ,bindPoses 的数量为 2。其中 weight 的数量对应的就是顶点数。而 boneIndex 的值表示每个顶点应该绑定哪个骨骼。weight0 设为 1 则表示该顶点只受一个骨骼影响
而表示骨骼的 Transform 数组由于是组件,所以要先创建新的 GameObject 来挂载。同时将它变成渲染物体的子对象。位置数值按需设置,旋转数值设为 Quaternion.identity ,表示“没有旋转“。作为默认值。而 bindPoses 本身是一个矩阵,具体含义可参考
(https://forum.unity.com/threads/some-explanations-on-bindposes.86185/)
(https://gist.github.com/larryhou/5ccb8cec465024fe3239ddd4ffce44e5),
初始化时只需按如下格式操作即可
基于这个理解,后面也先后创建了两个版本(Cpu/Gpu)去针对特定模型自动绑定骨骼。而 GPU 版。经过粗略测试,上万的顶点绑定连同模型的生成耗时不超过30ms,效率惊人。也让实时绑定的技术真正有了生产力。
下图是运行后的结果,以绑定一个触手为例。完整的代码已上传Github
(https://github.com/Wenzy--/Procedural-Rigging-in-Unity)
具体细节不在此展开。有三个要点需要注意。
* 骨骼在组织时,一般要有父子层级的嵌套关系。这样父层级的骨骼改变,也能影响所有子层级的。(例如肩关节旋转或位移,手肘,手掌,手指等各节点也会跟随)
* 计算权重时,会基于mesh 顶点到骨骼的距离做计算。但由于骨骼的坐标与 mesh 的顶点数据不在同一参考系,计算前需要先做转化
* 计算权重的一个原则,距离顶点越近的骨骼,分配到的权重越高,也越易影响模型
下阶段展望
有了目前的基础,下一步计划就是进行人物的实时绑定,有感兴趣的朋友欢迎交流探讨。很久前有关注 @_alexeykalinin 的作品,他也完成了一套程序化绑定的工作流。但由于使用的工具是 Houdini,所以也只能在编辑器中生成。而无法做到实时。但可以预见,实时绑定+实时模型生成在图形创作上有巨大的潜力
(https://twitter.com/i/status/1205907693543788549)
(来自alexeykalinin)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。