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
Skeleton with Assimp 骨骼动画解析的更多相关文章
- Spine Skeleton Animation(2D骨骼动画)
骨骼动画 首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这 ...
- three.js之初探骨骼动画
今后的几篇郭先生主要说说three.js骨骼动画.three.js骨骼动画十分有意思,但是对于初学者来说,学起来要稍微困难一些,官方文档比较少,网上除了用圆柱体的例子就是引用外部模型的,想要熟练使用骨 ...
- CSharpGL(50)使用Assimp加载骨骼动画
CSharpGL(50)使用Assimp加载骨骼动画 在(http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html)介绍了C++用Asism ...
- Unity骨骼动画资源解析与优化
一,背景 最近发现项目的动画文件有点大,不光内存大,而且文件也很大,所以从这2个方面下手处理 二,动画文件大小优化 为了优化动画文件大小,我们可以先分析下文件,Ctrl+D将动画文件从FBX拷贝出来, ...
- cocos2dx骨骼动画Armature源码分析(一)
源码分析一body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-to ...
- MD5骨骼动画模型加载(一)
前面我们分析了静态模型OBJ格式,桢动画模型MD2,这篇主要分析骨骼动画MD5的一些概念并且实现. 混合桢动画有计算简单,容易实现等优点,但是在需要比较细致的效果时,则需要更多的关键桢,每桢都添加相同 ...
- 基于Babylon.js编写简单的骨骼动画生成器
使用骨骼动画技术可以将网格的顶点分配给若干骨头,通过给骨头设定关键帧和父子关系,可以赋予网格高度动态并具有传递性的变形 效果.这里结合之前的相关研究在网页端使用JavaScript实现了一个简单的骨骼 ...
- three.js 自制骨骼动画(一)
上一篇郭先生解析了一下官方的骨骼动画案例,这篇郭先生就要做一个稍微复杂一点的骨骼动画了,就拿一个小人下手吧.在线案例请点击博客原文.话不多说先上大图 骨骼动画在GUI上面都有体现.制作骨骼动画的步骤在 ...
- cocos2dx骨骼动画Armature源码分析(三)
代码目录结构 cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下.(mac下没有分,是整个 ...
随机推荐
- Docker学习总结(一)--Docker简介
什么是虚拟化 在计算机中,虚拟化是一种资源管理技术,是将计算机的各种实体资源,如服务器.网络.内存等,以抽象.转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比之前更好的应用这些资源. 在实 ...
- 使用coding和hexo快速搭建博客
欢迎访问我的个人博客皮皮猪:http://www.zhsh666.xyz 今天教大家怎么用hexo快速搭建自己的博客.我不是专业人士,不懂前端知识,所以我十分讨厌那些专业术语,讲了一大堆,对于技术小白 ...
- unity编辑器扩展_06(给选项添加快捷键,控制菜单是否启用)
代码: [MenuItem("Tools/Delete ", true, 1)] static bool DeleteVadidate() { if (S ...
- lua_在C#中执行lua脚本
方法一:使用DoString 代码为: Lua lua = new Lua(); lua.DoString("a=13"); lua.D ...
- Gym 101510C
题意略. 思路: 由于xi的选取是任意的,所以我们不用去理会题目中的xi数列条件.主要是把关注点放在长度为L的线段覆盖至少k个整数这个条件上. 像这种取到最小的合法解的问题,我们应该要想到使用二分法来 ...
- Python——面向对象编程
面向对象编程是一种程序设计思想,把对象作为程序的基本单元,一个对象里面包含了数据以及操作数据的方法,称为类的属性和方法. 基础-定义.实例化.实例属性 用class来定义对象,类名首字母大写,如果这个 ...
- EF的3种开发模式
那么明显开发模式是三种. 即:DateBase First(数据库优先).Model First(模型优先)和Code First(代码优先). 当然,如果把Code First模式的两种具体方式独立 ...
- Java中指令重排
/** * 指令重排:代码执行顺序与预期不一致 (发生在前后行代码无联系时) * 目的:提高性能 * */ public class HappenBefore { private static int ...
- Linux配置使用SSH Key登录并禁用root密码登录
Linux系统大多数都支持OpenSSH,生成公钥.私钥的最好用ssh-keygen命令,如果用putty自带的PUTTYGEN.EXE生成会不兼容OpenSSH,从而会导致登录时出现server r ...
- CodeForces 812E Sagheer and Apple Tree 树上nim
Sagheer and Apple Tree 题解: 先分析一下, 如果只看叶子层的话. 那么就相当于 经典的石子问题 nim 博弈了. 那我们看非叶子层. 看叶子层的父亲层. 我们可以发现, 如果从 ...