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下没有分,是整个 ...
随机推荐
- Codeforces 976F
题意略. 思路:为了保证每个点都有至少k条边覆盖,我们可以让二分图的左半边与源点s相连,连容量为indegree[i] - k的边(如果正着想不好想,我们可以想它的反面, 限制它反面的上限,从而保证我 ...
- zookeeper学习之原理
一.zookeeper 是什么 Zookeeper是一个分布式协调服务,可用于服务发现,分布式锁,分布式领导选举,配置管理等.这一切的基础,都是Zookeeper提供了一个类似于Linux文件系统的树 ...
- Nacos整合Spring Cloud Gateway组件
一.什么是Spring Cloud Gateway Spring Cloud Gateway是Spring Cloud官方推出的网关框架,网关作为流量入口有着非常大的作用,常见的功能有路由转发.权限校 ...
- javascript之操作数组方法
掌握如何操作数组,会让你的开发变得更加高效 1.栈和队列方法(以下四个方法都改变原数组) arr.push() //接受任意类型的参数,逐个添加到数组的末尾,并返回数组的长度 改变原数组 arr.po ...
- python小白短期基础入门
一.编程语言介绍与分类 1.什么是编程语言 编程语言本质就是人类的语言,主要用于沟通交流.我们通过编程语言与计算机进行互动交流,从而使计算机来帮助我们实现一些特定的功能和一些复杂的工作. 2.编程语言 ...
- js多重数组完全展开
有时候项目中会遇到多重数组,需要判断多重数组里面有没有要找的对象,强大的js就可以帮助我们 var arrTest = [1, [2, 3, [4]], 5, 6, [7, 8], [[9, [10, ...
- 一本通 P1486 【黑暗城堡】
题库 :一本通 题号 :1486 题目 :黑暗城堡 link :http://ybt.ssoier.cn:8088/problem_show.php?pid=1486 思路 :这道题既然要求使加入生成 ...
- Codeforces 369 C Valera and Elections
Valera and Elections 题意:现在有n个候选人, 有n-1条路, 如果选择了这个候选人, 这个候选人就会将从自己这个城市到1号城市上所有坏的路都修复一下,现在求最小的候选人数目, 如 ...
- CQ18阶梯赛第二场
H国的身份证号码I HihoCoder - 1558 只要单纯的判断一下前后的乘积就好了, 因为不是很想处理倍数的关系, 所以我这里是用 string去处理. 代码: #include<bits ...
- 拿 C# 搞函数式编程 - 1
最近闲下来了,准备出一个 C# 搞 FP 的合集.本合集所有代码均以 C# 8 为示例. 可能你说,为什么要这么做呢?回答:为了好玩.另外,意义党们请 gun cu ke! C# 有委托,而且有 Fu ...