骨骼动画原理学习笔记

  • 什么是骨骼?
  • 如何利用骨骼驱动顶点?
    • 单关节骨骼如何驱动顶点?
    • 多关节骨骼如何驱动顶点?
  • 有了骨骼,如何动画?
    • 动画控制
    • 动画数据加载流程
  • 遗留问题

骨骼动画可以看作一种高效的动画数据压缩技术。

最有弹性的动画系统,可想像成动画师能控制物体表面上无穷多的点。当然,用这种方法制作动画,其结果会是无穷大量的数据!此理想的简化版本是控制三角形网格的顶点,那么实际上,我们是把描述动画的信息加以压缩,限制了只能移动顶点…骨骼动画也是另一种通过加入约束来压缩顶点动画的方式。在此方法中,相对大量的顶点只能跟随相对少量的骨骼关节移动。
——《游戏引擎架构》11.1.5小节

因此,骨骼动画中骨骼最终驱动的是顶点,顶点着色器(Vertex Shader)中利用蒙皮矩阵(Skinning Matrix)对顶点做变换,使网格顶点变换到骨骼的当前姿势。

// position
#ifdef POSITION_LOCATION
layout(location = POSITION_LOCATION) in vec3 in_position;
vec4 getPosition()
{vec4 pos = vec4(in_position, 1.0);#ifdef USE_SKINNINGpos = getSkinningMatrix() * pos;
#endifreturn pos;
}
#endif

大量顶点的变换信息压缩在“骨骼”中,能够这样压缩是因为骨骼动画规定了一种压缩范式,即“如何利用骨骼驱动顶点”的范式。因此为了弄清楚骨骼动画的原理,提出如下三个问题:

  • 什么是骨骼?
  • 如何利用骨骼驱动顶点?
  • 有了骨骼,如何动画?

什么是骨骼?


Q:什么是骨骼?
A:骨骼就是一组关节(Joints)。
Q:什么是关节?
A:一个关节可以理解为一个局部坐标系。关节中存储的数据记录了控制这个坐标系的坐标系变换信息——变换矩阵 or SQT数据(缩放/Scale、四元数旋转/Quaternion、平移/Translation)
关节≈关节坐标系关节 \approx 关节坐标系 关节≈关节坐标系
Q:关节如何组织成骨骼?
A:骨骼的关节呈现层级结构,即树结构。一副骨骼有一个根关节点,其余关节点是根关节点的子节点,或者子节点的子节点,或者子节点的子节点的子节点…

因此一副骨骼可以理解为一个树节点为坐标系的树结构。子关节点的坐标系依赖父关节点的坐标系,即,子关节点存储的变换信息能将子关节空间中的顶点变换到父关节空间中。根关节点中存储的是从根关键点变换到模型空间中的变换信息。

如何利用骨骼驱动顶点?

单关节骨骼如何驱动顶点?

如果一副骨骼只有一个关节,那么可以理解为只是在一系列顶点变换(如下图)前又多了一个关节空间到模型空间的变换。顶点从关节坐标系到模型坐标系的变化,与渲染管线其他部分的坐标系变化(e.g. 模型坐标系到世界坐标系到相机坐标系)原理是一样的。

在关节坐标系内,受这个关节影响的所有顶点的位置是不变的。同样可以类比:不管模型变换到世界坐标系时发生了怎样的旋转、位移和形变,在模型空间中mesh的顶点坐标永远是不变的。
关节空间中的不变性并不像模型空间中的不变性那样直观。因为模型在呈现出来时(比如直接用一些3D Viewer打开模型文件时)就已经具有某种姿势(Pose)。此时的顶点位置,是已经经过关节坐标系到模型坐标系的某种变换后的位置。
因此在利用骨骼控制顶点位置时,需要利用默认姿势的变换矩阵的逆矩阵(Inverse Bind Matrix)将顶点恢复到关节空间,再计算新的关节空间到模型空间的变换,这样两阶段的矩阵变化合并计算得到的就是关节矩阵(Joint Matrix)。关节矩阵(Joint Matrix)一般在Shader外计算完成后以uniform的形式传入Shader。

多关节骨骼如何驱动顶点?

如果一个顶点只绑定了一个关节,那么这个顶点只需要关节矩阵(Joint Matrix)就可以完成新姿势的加载。如果绑定了多个关节,就需要利用多个关节矩阵(Joint Matrix)和每个关节的影响权重(Weights)加权平均得到蒙皮矩阵(Skinning Matrix),所有权重的和为1。

每个顶点会存储影响这个顶点的关节索引(Joints)和对应的影响权重(Weights),这两组参数和位置(Position)、法相(Normal)、颜色(Color)、纹理坐标(UV)一样存储在顶点属性(Vertex Attribute)中。

每个顶点一般最多被4个关节影响,因此shader中用2个vec4变量分别表示顶点的关节索引数组和权重数组。

通常游戏引擎会限制每个顶点能绑定的关节数目。典型的限制为每顶点4个关节,原因如下。首先,4个8位关节索引能方便地包裹为一个32位字。此外,每顶点使用2个、3个及4个关节所产生的质量很容易区分,但多数人并不能分辨出每顶点4个关节以上的质量差别。
——《游戏引擎架构》11.5.1小节

// joins
#ifdef JOINTS_0_LOCATION
layout(location = JOINTS_0_LOCATION) in vec4 in_joints_0;
#endif
#ifdef WEIGHTS_0_LOCATION
layout(location = WEIGHTS_0_LOCATION) in vec4 in_weights_0;
#endif#ifdef USE_SKINNING
uniform mat4 u_jointMatrix[JOINT_COUNT];
#endif#ifdef USE_SKINNING
mat4 getSkinningMatrix()
{mat4 skin = mat4(0);#ifdef JOINTS_0_LOCATIONskin +=in_weights_0.x * u_jointMatrix[int(in_joints_0.x)] +in_weights_0.y * u_jointMatrix[int(in_joints_0.y)] +in_weights_0.z * u_jointMatrix[int(in_joints_0.z)] +in_weights_0.w * u_jointMatrix[int(in_joints_0.w)];
#endifreturn skin;
}

有了骨骼,如何动画?

动画控制

动画控制有2个主要的部件:动画通道(Channel)和计时器(Timer)。

  • 动画通道中存储了不同的动画序列,e.g. 某个通道中存储了人的右手的动作,另一个通道中存储了人的左手的动作。每个通道中会存储一组动作的信息,一般为一组时刻序列和一组动作序列,时间和动作一一对应。动作一般是针对某一骨骼关节的变换。
  • 计时器用来控制协调不同通道的时间一致性。通过计时器得到全局的时间ttt,利用全局时间ttt得到每个通道中的局部时间t0,t1,...,tnt_0, t_1, ..., t_nt0​,t1​,...,tn​,再利用局部时间通过插值前后帧计算出每个通道中的动作,加载到骨骼中。

动画数据加载流程

  • 计时器得到下一帧的全局时间ttt
  • 动画控制器计算出每个通道的局部时间,插值得到关节变换(动作),加载到关节的SQT信息中
  • 利用更新后的SQT信息,更新计算每个关节点的局部变换矩阵(Local Transform Matrix)
  • 从骨骼根关节点,层级遍历更新计算每个关节点的全局变化矩阵(World Transform Matrix)
  • 计算每个关节点的关节矩阵(Joint Matrix)
  • 在Shader中计算蒙皮矩阵(Skinning Matrix)
  • 在Shader中对顶点应用蒙皮矩阵,得到变换后的顶点位置。

遗留问题

那么目前还没解决的问题是:

  • Joint Matrix 如何计算?
  • 这些复杂的动画控制数据,如何在模型文件中组织?

这些问题会在下一篇笔记中解答:利用glTF文件加载骨骼动画。
下一篇笔记施工中!

骨骼动画原理学习笔记相关推荐

  1. MOOC人工智能原理学习笔记1

    人工智能原理学习笔记1 The Foundations of AI: Philosophy Mathematics Economics Neuroscience Psychology Computer ...

  2. 自控原理学习笔记-反馈控制系统的动态模型(4)-频率特性函数Nyquist图及Bode图

    自控原理学习笔记 自控原理学习笔记专栏 文章目录 1.频率特性函数 1.1 图形表示方法: 1.2 零极点位置和暂态增益图 1.2.1 复轨迹曲线 1.2.3 例子 1.3 计算系统响应 2.开环频率 ...

  3. 自控原理学习笔记-系统稳定性分析(2)-环路分析及Nyquist-Bode判据

    自控原理学习笔记 自控原理学习笔记专栏 文章目录 3. 环路分析 3.1环路分析基本思想: 3.2 稳定程度的性能指标(相对稳定) 3.3 环路整形 4.Nyquist判据 4.1 与幅角原理关系 4 ...

  4. Golang底层原理学习笔记(一)

    LCY~~Golang底层原理学习笔记 1 源码调试 go源代码地址:GitHub - golang/go: The Go programming language 1.1 源码编译 现在的go语言大 ...

  5. [编译原理学习笔记2-2] 程序语言的语法描述

    [编译原理学习笔记2-2] 程序语言的语法描述 文章目录 [编译原理学习笔记2-2] 程序语言的语法描述 [2.3.1] 上下文无关文法 [2.3.2] 语法分析树与二义性 [2.3.3] 形式语言鸟 ...

  6. 自控原理学习笔记-反馈控制系统的动态模型(1)

    自控原理学习笔记 1.导论 2.反馈控制系统的动态模型(1) 3.反馈控制系统的动态模型(2) 3.反馈控制系统的动态模型(3) 4.反馈控制系统的动态模型(4) 5.反馈控制系统的动态模型(5) 文 ...

  7. Spring5底层原理 学习笔记(二)AOP篇

    文章目录 AOP实现之ajc编译器 AOP实现之agent类加载 AOP实现之动态代理 jdk动态代理 演示 模拟实现动态代理 动态生成代理类需要使用到asm的api,这里就不展开了 Jdk对于反射调 ...

  8. Unity动画状态机学习笔记

    Unity动画状态机学习笔记 一.建平面,拖人物模型.建状态机.动画导入.拖组件--实现Game时人物动画为等待状态. 二.拖WAIT01.WAIT02.WAIT03.WAIT04--实现按数字1切换 ...

  9. 编译原理学习笔记20——符号表

    编译原理学习笔记20--符号表 20.1 符号表的组织与操作 20.2 符号表的内容 20.3 利用符号表分析名字的作用域 20.1 符号表的组织与操作 符号表 符号表的作用与组织 符号表的整理和查找 ...

  10. GPU Skinning 一:骨骼动画原理

    最近在为引擎升级64位的过程中GPU蒙皮也出现了异常,平常骨骼动画和网格蒙皮用的还是非常多的,但是底层的原理并没有深究过,想着还是有必要好好整理下这部分内容. 骨骼蒙皮动画 一般我们称为骨骼动画(Sk ...

最新文章

  1. ps怎么制作流体_ps相框制作教程:ps怎么制作相框效果
  2. CentOS安装Oracle全过程
  3. 没想到!大数据发现微信上使用最多的表情竟是...原谅很多人不知道
  4. (七)日志采集工具sleuth--分布式链路跟踪(zipkin)
  5. Vue.js插槽slot和作用域插槽slot-scope学习小结
  6. 深度解析HashMap
  7. 继承属性public private
  8. 判断一颗二叉树是否是平衡二叉树
  9. Error loading WebappClassLoader
  10. 【Linux】Ubuntu下进行C语言编程
  11. 前端学习(2710):重读vue电商网站30之左侧菜单栏图标设计
  12. 孩子学python用什么教材比较好-python大学里用哪本教材比较好?
  13. 计算机与工程建设项目结合,计算机科学与技术在工程建设项目管理中应用.doc...
  14. linux机器的物理内存监控,Linux内存监控工具
  15. json 转换 java odl_opendaylight:如何查看配置数据库
  16. seay svn漏洞利用工具_史上最强的iPhone越狱工具开源了,永久有效,从4s到X都支持:利用了iOS大漏洞...
  17. 简单介绍一下HBase、Cassandra、Voldemort、Redis、VoltDB、MySQL
  18. 如何快速理解TCP协议
  19. 怎么把两个PDF文件合并一起
  20. 计算机编程 计算存款利息,第8周项目5-定期存款利息计算器

热门文章

  1. 京东商城选择地址信息
  2. Linux redhat 5.7 安装 Teamviewer7
  3. php 支付宝退款40004,接入支付宝支付 错误码4000,排查方法——开发记录
  4. 易到网约车许可证到手,终于能卖个好价钱了
  5. python pip install pil_python安装PIL库
  6. 银行利率bps是什么意思,贷款利率bps是什么意思
  7. 汽车软件测试都测试,汽车零部件如何做测试?
  8. 墨尔本大学 SWEN20003 Project2 课业解析
  9. 局域网oracle 速度慢,[转帖]局域网中其他用户感觉上网速度慢、网速卡
  10. nas 微型计算机,商为家用的利器 希捷BS 2- Bay NAS