Skeleton with Assimp 骨骼动画解析

Skeleton with Assimp 骨骼动画解析

骨骼动画是图形学中十分常见应用很广泛的一个技术,也是比较基础的内容,作为图形学的工程师需要将这一部分内容梳理清晰,主要关键在于几点:第一,分清楚骨骼节点两个概念;第二,熟悉使用 Assimp(或者其他的)的解析方式,并编程实现骨骼的解析和动画的播放。

理解骨骼

首先,为什么会有骨骼动画这么一种东西的存在呢?如果我们从我们自己的身体上观察,就可以发现,我们全身可以活动的部分,其内部基本都有一根主要的骨头,比如小臂的挥动,小臂上所有的肌肉皮肤都一起和骨骼运动。回过头来看 3D 绘制,通常我们需要绘制的是一个 mesh, 也就是物体的表面部分,可以认为是一张皮。我们希望绘制的对象也可以像人体一样做一些动作。那么同样的,我们将这张皮上面的每一个最小单位,如顶点(vertex)都绑定一根骨头上去,骨头怎么动,皮就怎么动。这个部分,叫做蒙皮(skin rigging),是由艺术家完成的[1](真实情况中,一个点可能受多个骨骼影响,需要确定具体的权重)。这样,我们只需要考虑有限的几个骨头的运动就可以描述人体整体的运动,简化了很多,同时也是对自然规律的模拟。

节点又是什么?

节点就是一个点,在骨骼的语境中,可能称为“关节”,但是关节不同与骨骼。Keep in mind: 骨骼是有长度的线段,仅有空间中一个点的位置无法描述骨骼。只有用两个点,才能组成一条线段,只有线段才能代表骨骼。即使在 Unity 或其他软件中,用 node, 节点,关节 代表骨骼,但是心里要清楚这一点。如下图,RA 是一个骨骼,RB, BC 都分别是骨骼,但是我们不能说 A 是一个骨骼,单独提 A 是没有意义的,这只能是一个点。

艺术家的工作,将所有的顶点,都和骨骼绑定起来,显然,下图这个骨骼的配套的 mesh 的上面部分的顶点,都与 RA 骨骼绑定起来了,而在左下方和右下方的顶点,基本绑定在 BC 和 DE 两个骨骼上。在这几个节点处的顶点,会与多个骨骼绑定,每个骨骼有一定的权重。同时,艺术家会提供一个动画的关键帧的骨骼的姿态,即在关键帧时,每一个骨骼的位置。


  A -
                    B---- R ----- D
  |             |
  |             |
  |             |
  C             E

------------------

R -> A
R -> B -> C
R -> D -> E

根据艺术家提供的数据,究竟我们怎么确定每一个点的位置呢? 首先骨骼之间存在父子结构关系,上图的 RB 骨骼是 BC 骨骼的父亲。我们也可以用节点来描述,那么就是 R 点是 B 点的 父亲, B 点是 C 点的父亲(如上图箭头所示)。在父子层级关系上我们用骨骼或者用节点都是可以描述的,其本质上描述的是同一个骨骼,就是图中画的那样。Assimp 用来帮助解析 FBX 文件,我们从 Assimp 中获取所有的信息。对于任意时刻的骨骼的位置,Assimp 提供每个骨骼相对上一级的变换,以 transform matrix 表示,从根节点开始遍历,就可以得到每一个骨骼相对根节点的变换,如果认为根节点就是在世界坐标系的中心,这就是从 bone space -> world space 的变换了。bone space 就是以这个骨骼当中的某个点作为坐标系的原点,具体是哪一个点,其实我们也是无法得知的,这个信息对于计算和理解都不重要,只要知道 bone space 就是骨骼的局部坐标系,知道绑定了这个骨骼的每一个点,在这个局部坐标系当中的位置即可。对于绑定了这个骨骼的每一个点,设其在 bone space 中的位置为 bone_pos, 那么,其在世界坐标系中的位置就是 bone_pos 乘上计算出来的变换矩阵。

Assimp 解析指南

使用 Assimp 加载 FBX 文件获得 aiScene 这是所有数据的入口。

Bone

aiBone *bone = aiScene->mMeshes[]->mBones[];

mBones 数组里面存储了所有的骨骼,每个骨骼存储对应绑定的顶点和该顶点的权重,以及一个 mOffsetMatrix 这个矩阵十分有用,后文提及。

Node

aiNode *root_node = aiScene->mRootNode  // root node
aiNode *child_node = root_node->mChildren[i]  // get child node

aiNode 中除了存储父子关系相关信息外,最重要的属性就是 mTransformation 这就是相对于上一级 node 的变换矩阵。

这里我们又见到了 node 和 bone 两个说法,在 Assimp 的规定中,每一个 bone 必定会有一个相同名称的 node 与其对应,反过来不成立。每一个骨骼的变换矩阵就是同名节点的变换矩阵。

Bind Pose

Bind Pose 是根据 mesh 的顶点信息,不考虑骨骼,直接绘制得到的结果,也就是绘制对象初始的状态。另外,也可以用上 Assimp 读取出来的数据来验证。[2]

前文提到,bone space 的顶点位置是计算的前提,但是实际上,我们读取的到的 mesh 的顶点位置,是以 model space 也即模型空间来描述的,正因为如此,我们可以直接绘制出初始状态的模型来。那么如何得到 bone space 的位置呢?从理论上来说,逐级遍历得到 BoneToWorld transform matrix, 这个矩阵的逆矩阵就是 WorldToBone transform matrix, 即:

final_pos = (transform_matrix) * (transform_matrix)^(-1) * world_pos;  // means: final_pos = world_pos;

看起来无意义吧,因为逐级计算矩阵再求逆这个操作实在复杂,Assimp 直接提供了这个逆矩阵,就是 mOffsetMatrix, 上式可以写作:

final_pos = (transform_matrix) * (offset_matrix) * world_pos; // means: final_pos = world_pos;

可以利用这个方法验证 FBX 读取和计算是否正确。正常情况下应该和直接绘制的结果一样,如果不一样,就是某个地方出错了(最有可能出错的地方是逐级遍历节点计算变换矩阵)。

Animation

得到 bone space position 以后,计算每一帧的姿态就很简单了。aiScene 包含了若干个 aiAnimation,每个代表一组动画,每个 aiAnimation 包含一个 aiNodeAnim 的数组,称之为 mChannels, 根据 aiNodeAnim->mNodeName 找到对应的 node,那么这个 node 在某个特定时刻的 transform matrix 就可以通过对 Position, Rotation and Scaling 的插值计算出来,该矩阵仍然只代表相对父节点的变换,仍然通过逐级遍历得到每个节点的在特定时刻的,AnimationBoneToWorldTransformMatrix. 最后的计算:

final_pos = (animation_transform_matrix) * (offset_matrix) * world_pos;

Reference

[1] Skeletal Animation With Assimp

[2] can‘t get bones/skinning to work

Asset-Importer-Lib Documentation

Bones Animation - Matrices and calculations

原文地址:https://www.cnblogs.com/psklf/p/11613676.html

时间: 2024-10-09 17:30:05

Skeleton with Assimp 骨骼动画解析的相关文章

Spine Skeleton Animation 2D骨骼动画 For Game 介绍

首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这种条件下应运而生.比较著名的帧动画格式是Quake2所采用的MD2.到今天为止,帧动画依然存在,只不过帧动画更多地是来描述小且动作相对少些的物体. GPU出现后,CPU的问题早已不像以前那么突出,一些新的手段和技术也可以被应用进来了.骨骼动画相对于帧动画而言,更加灵活多变,但同时,骨骼动画需要更多的计算量,因

Unity Spine Skeleton Animation 2D骨骼动画 For Game 介绍

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,我们致力于打造业内unity3d培训.学习第一品牌. 首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这种条件下应运而生.比较著名的帧动画格式是Quake2所采用的MD2.到今天为止,帧动画依然存在,只不过帧动

CSharpGL(50)使用Assimp加载骨骼动画

在(http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html)介绍了C++用Asismp库加载骨骼动画的原理和流程. 在(http://wiki.jikexueyuan.com/project/modern-opengl-tutorial/tutorial45.html)是其中文版译文. 本文用CSharpGL借助Assimp库实现加载和渲染骨骼动画的功能. 下载 CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入

2015元宵节礼物——《刀塔传奇》骨骼动画查看器,开源啦!

首先声明,我不是第一个做出类似查看器的首作者,我所知道的首作者是johance,以下是它在cocoschina发布的贴子. 分享 刀塔查看器Flash導出版本(帶詳細解析說明)和Flash導入導出批處理文件      http://www.cocoachina.com/bbs/read.php?tid-219173.html 贴子里面描述了<刀塔传奇>骨骼动画的文件格式,我是参考这个文件格式写出来的.我的实现思路是将*.fca文件转换成DragonBonesCPP所需要的格式,然后就可以通过D

【转】将3DMAX中的动画通过OGREMAX导入OGRE中,并生成相应的骨骼动画

原创内容转载请注明:http://weibo.com/gdexqin 程序代码的的基础在http://blog.sina.com.cn/s/blog_7c03dc6f01012um2.html中 先看导入后的效果. 动画模型是用别人传到网上的(好像这个人物叫真名法典的样子,不太认识,但是感谢上传者),之前我们已经完成了通过OGREMAX导入静态的场景的效果,导入骨骼动画我们需要对OGREMAX的导出选项进行一些修改. 下面先了解一下OGREMAX的一些基本选项(转自网络): ----------

cocos2dx骨骼动画Armature源码分析(三)

代码目录结构 cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下.(mac下没有分,是整个一坨) armature(目录): animation(目录):动画控制相关. CCProcessBase(文件): ProcessBase(类):CCTween和ArmatureAnimation的基类. CCTWeen(文件): Tween(类):控制flash里一个layer的动画. CCArmatur

cocos2dx骨骼动画Armature源码分析(二)

flash中数据与xml中数据关系 上篇博文从总体上介绍了cocos2dx自带的骨骼动画,这篇介绍一下导出的配置数据各个字段的含义(也解释了DragonBone导出的xml数据每个字段的含义). skeleton节点 1 <skeleton name="Dragon" frameRate="24" version="2.2"> name:flash文件名字. frameRate:flash帧率. version:dragonbones

引擎设计跟踪(九.14) 更新记录和骨骼动画导出

骨骼动画是去年打算写的部分, 但是中间因为工作太忙, 已经拖了一年了. 期间也加了其他东西, 比如对UI做了部分完善.UI对toolbar button添加了drop down 支持, 一种是dropdown menu, 一种是dropdown property sheet 实现这些控件不难, 但是要做抽象和复用, 接口设计稍微有点复杂. 现在可以把一个IConfig对象绑定到toolbar的button里了. 这样保存这些配置的时候,直接使用IConfig接口就可以了.贴一个编辑器的配置文件,

关于Cocos Creator用js脚本代码播放骨骼动画的步骤和注意事项

1.用cc.find()方法找到相应的骨骼动画节点,并把这个对象赋值给一个var出来的新对象. 具体代码:var spineboy_anim = cc.find("UI_Root/anchor_lb/spineboy"); 2.用getComponent()方法找到相应骨骼动画节点的骨骼动画组件,并把这个对象赋值给一个var出来的新对象. 具体代码:var ske_anim_comp = spineboy_anim.getComponent(sp.Skeleton); 3.把第二点的新