gltf骨骼动画解析笔记
前言
gltf骨骼动画分为两部分,一部分是骨骼动画的结构信息,一部分是buffer数据
|asset | ||accessors|--||animations| ||bufferViews|是一个数组,记录了所有item具体的buffer使用情况|buffers|buffer的长度 buffer的地址url
gltf |meshes||nodes|记录了所有节点信息|scene|含有几个场景|scenes|列出所有场景信息|skins||
---------------------------------------------------------------------------------------------- |byteLength |buffer的字节长度
buffers |uri |buffer二进制文件的地址| ---------- |
buffers文件里存储的是字节流,一个字节一个字节的
这个文件是根据一颗渲染树生成的,这比较简单,就是将各种属性打包成字节流,通过偏移地址和长度来标记他们
--------------------------------------------------------------------------------------------| buffer | 使用的buffer的索引,在buffer数组中,具体使用那段buffer
bufferViews[]| byteLength|当前使用的buffer的字节长度| byteOffset|起始地址字节偏移量|target |在显存中的缓冲类型(顶点缓冲或者索引缓冲)-------------------------------------------------------------------------------------------| bufferView | 在bufferViews数组中的索引| componentType|每一个数据有几个字节组成| count | 一共有多少个单位数据
accessors[] | type |一个单位数据有多少个数据组成| max |在所有的数据中最大值| min |在所有的数据中最小值
-------------------------------------------------------------------------------------------|name | 网格的名字
meshes[] |primitives | 绘制信息|------------||attributes |顶点属性
primitives |indices |存储的值是属性accessors数组索引 索引属性|------------|| JOINTS_0 |存储的值是属性accessors数组索引,存储了骨骼节点| NORMAL |存储的值是属性accessors数组索引,存储了法线| POSITION |存储的值是属性accessors数组索引,存储了位置信息
attributes | TANGENT |存储的值是属性accessors数组索引,存储了切线| TEXCOORD_0|存储的值是属性accessors数组索引,存储了纹理| WEIGHTS_0 |存储的值是属性accessors数组索引,权重数据
网格有三角形组成,而三角形就是简单的三个顶点组成,所以网格数据可以看做就是一堆顶点数据
顶点数据就包含了上面六种:
JOINTS_0:顶点受到哪些骨骼节点的制约
NORMAL : 顶点的法线
POSITION:顶点的具体位置
TANGENT:顶点的切线
TEXCOORD_0:顶点的uv坐标
WEIGHTS_0:顶点受到骨骼节点制约的权重,这个权重和JOINTS_0数组里的骨骼节点是一一对应的
-------------------------------------------------------------------------------------------|children[] | 该节点本身含有那些子节点|name | 该节点的名字
nodes[]--|rotation | 旋转的信息,如果是四位,那就是四元数|translation | 平移信息|scale | 缩放信息|mesh | 网格信息 这里存放的是网格索引,具体查看meshes|skin | 蒙皮信息,这里存放的是蒙皮索引, 具体查看skins
看到这个nodes,是个迷惑点,这个nodes指的是骨骼节点,虽然它也是一个节点,但它是特殊的节点
我们的蒙皮就是使用骨骼节点的空间矩阵来创建的
看一份源文件中nodes的构造{0,1,2,3,4,5,6,7,8,9,10,11}
继承关系如下| 0 | | 1 |
obj| 2 | camera| 3 | light| 5 | sun1 | 4 | 该节点含有skin和mesh信息| 6 | 骨骼节点| 7 | 骨骼节点6 | 8 | 骨骼节点| 9 | 骨骼节点9 | 10 | 骨骼节点10| 11 | 骨骼节点
-----------------------------------------------------------------------------------------
scene |这个文件里一共含有多少个场景
-----------------------------------------------------------------------------------------
scenes[] |name|场景的名字|nodes[]|场景含有那些节点,存储的值是nodes的索引
scenes它表示一个场景数组,表示含有若干个数组
场景scene是一个根节点,nodes是一个节点数组,他们是插入到场景节点的一级节点
----------------------------------------------------------------------------------------|inverseBindMatrices|存储的值是属性accessors数组索引
skins[] |joints[] |存储的值是属性nodes数组索引,骨骼节点|skeleton |
首先skins是一个数组,它代表这一个模型可以有若干个蒙皮,注意这里的皮指的不是uv贴图,它指的是由骨骼节点组成的皮
每一个蒙皮,由若干个骨骼节点组成,每一个骨骼节点根据自身的平移四元数旋转以及缩放这三种数据可以构造一个空间矩阵,
每一个空间矩阵占16个元素,就是蒙皮纹理的一行(RGBA RGBA RGBA RGBA),那若干个骨骼节点就是下面这种
RGBA RGBA RGBA RGBA
RGBA RGBA RGBA RGBA
RGBA RGBA RGBA RGBA
RGBA RGBA RGBA RGBA
......
RGBA RGBA RGBA RGBA
OK,一张蒙皮不就生成了啊,这就是蒙皮,蒙皮就是由骨骼节点的空间坐标系所组成
在上面的参数中,有一个inverseBindMatrices属性,其实它和蒙皮中骨骼是一一对应的,而我们在buffer中存储也是它的值
它是初始骨骼矩阵的逆矩阵,我们每一个骨骼都会在nodes中记录,它有自身的旋转缩放平移,那么就可以动态的生成空间矩阵,
但我们仍然需要一个初始骨骼矩阵的逆矩阵,为啥呢?这个原因就是骨骼节点分布的初始位置不同,你比如说在头位置放置一个
骨骼节点,在同样位置放置一个骨骼矩阵,设这个骨骼节点对头位置影响的权重位1.0,此时头位置距离地面的距离位2,
那这两个矩阵相乘,其实人家骨骼节点动也没动,一下子,头就要距离地面为4了,这显然是不对的,所以我们在放置骨骼节点
的时候,一定要记住当时的位置,因为第一次放置,对周围的顶点是没有任何影响的,只有以后骨骼节点的变换才会影响顶点的位置
,所以我们需要记录第一次骨骼绑定的形态信息,这就是骨骼绑定姿势的逆矩阵,哪些被骨骼节点影响的顶点每一次乘以骨骼矩阵
之前都要乘以这个绑定姿势的逆矩阵,它可以抵消第一次绑定的时候的数据所带来的变化
再举一个形象的例子,假如你老婆告诉你,只要你今后身上多出钱,那就可以留一半在身上,但是截至到今天为止,你身上的钱不作数
,必须全额上缴
矩阵由16个元素组成,每个元素的类型是float,也就是4个字节
---------------------------------------------------------------------------------------
结构信息
属性accessors是一个访问buffer数据的中间件
bufferView:指明了在bufferViews中的索引
componentType:指明了每一个数据有几个字节组成,常见的类型如下
‘5120’: Int8Array, // gl.BYTE
‘5121’: Uint8Array, // gl.UNSIGNED_BYTE
‘5122’: Int16Array, // gl.SHORT
‘5123’: Uint16Array, // gl.UNSIGNED_SHORT
‘5124’: Int32Array, // gl.INT
‘5125’: Uint32Array, // gl.UNSIGNED_INT
‘5126’: Float32Array, // gl.FLOAT
count:指的是一共有多少个单位数据
type:指的是一个单位数据有几个数据组成,常见的类型如下
‘SCALAR’: 1,
‘VEC2’: 2,
‘VEC3’: 3,
‘VEC4’: 4, //
‘MAT2’: 4,
‘MAT3’: 9,
‘MAT4’: 16, //4维矩阵占用16个元素
max:表示最大数据
min:表示最小数据
上面的六个属性,其实就是为了组成一个数组,通过bufferView我们从buffer中取出的数据是一个一个字节的,现在要把它放到一个新的数组中,componentType指明了数组类型,count 乘以 type 指明了数组长度
"accessors" : [{"bufferView" : 0,"componentType" : 5123,"count" : 11994,"max" : [2284],"min" : [0],"type" : "SCALAR"},{"bufferView" : 1,"componentType" : 5126,"count" : 2285,"max" : [2.3011670112609863,2.933537244796753,3.3537864685058594],"min" : [-2.3011670112609863,-0.96541428565979,-6.518695831298828],"type" : "VEC3"},......]
属性bufferViews指明了数据在buffer中的具体位置信息
buffer:指的是在buffer数据数组中的索引,我们取到的 buffer数据是一个数组
byteLength:这段数据的长度
byteOffset:这段数据在指定数据流中的长度
target:要在GPU显存中绑定的数据缓冲类型现在
"bufferViews" : [{"buffer" : 0,"byteLength" : 23988,"byteOffset" : 0,"target" : 34963},{"buffer" : 0,"byteLength" : 27420,"byteOffset" : 23988,"target" : 34962},......]
属性buffers指明了buffer数据的地址,我们可以从这里加载到buffer数据
byteLength:字节流长度
uri:字节流地址
"buffers" : [{"byteLength" : 193428,"uri" : "whale.CYCLES.bin"}],
属性meshes指明了所有网格数据
primitives:绘制信息
JOINTS_0:存储的值是属性accessors数组索引,存储了骨骼节点,
NORMAL:存储的值是属性accessors数组索引,法线数据
POSITION:存储的值是属性accessors数组索引,位置数据
TANGENT:存储的值是属性accessors数组索引,切线数据
TEXCOORD_0:存储的值是属性accessors数组索引,纹理数据
WEIGHTS_0:存储的值是属性accessors数组索引,权重数据
indices:存储的值是属性accessors数组索引,索引数据
总结:上面的意思就是当前这个网格的顶点有哪些,法线有哪些,切线有哪些,纹理uv有哪些,受哪些骨骼影响,以及每个骨骼对这些顶点的权重是多少
"meshes" : [{"name" : "orca","primitives" : [{"attributes" : {"JOINTS_0" : 5,"NORMAL" : 2,"POSITION" : 1,"TANGENT" : 3,"TEXCOORD_0" : 4,"WEIGHTS_0" : 6},"indices" : 0}]}]
属性skins这个记录的是蒙皮数据,蒙皮指的不是皮肤
inverseBindMatrices:存储的值是属性accessors数组索引
joints:这里存的是骨骼节点索引,他们位于属性nodes中
每一个骨骼节点他有一个唯一的节点空间坐标系4x4
一个蒙皮纹理由若干个骨骼节点组成
当外界修改骨骼节点的平移缩放旋转,那就会去修改骨骼节点的空间坐标系,进而影响到受他影响的顶点
我们在顶点shader中进行空间变换的时候,会加一个空间坐标系skinMatrix,通常情况,一个点我们设置能够影响它的骨骼空间坐标系的数量为4,不能设置太多,设置太多数据计算量都会增多
'mat4 getBoneMatrix(float jointNdx) {' +'float v = (jointNdx + 0.5) / u_numJoints;' + //算出行'return mat4(' + //s 'texture2D(u_jointTexture, vec2(((0.5 + 0.0) / 4.), v)),' + //0.125 'texture2D(u_jointTexture, vec2(((0.5 + 1.0) / 4.), v)),' + //0.375 'texture2D(u_jointTexture, vec2(((0.5 + 2.0) / 4.), v)),' + //0.625 'texture2D(u_jointTexture, vec2(((0.5 + 3.0) / 4.), v)));' + //0.875 '}' +
'void main() {' +'mat4 skinMatrix = getBoneMatrix(a_JOINTS_0[0]) * a_WEIGHTS_0[0] +' +'getBoneMatrix(a_JOINTS_0[1]) * a_WEIGHTS_0[1] +' +'getBoneMatrix(a_JOINTS_0[2]) * a_WEIGHTS_0[2] +' +'getBoneMatrix(a_JOINTS_0[3]) * a_WEIGHTS_0[3];' +'mat4 world = u_world * skinMatrix;' +'gl_Position = u_projection * u_view * world * a_POSITION;' +
}
总结:其实它的意思就是骨骼节点来造一张纹理,形如
//一共有6个骨骼矩阵//0 1 2 3 4 5//每个顶点受到4个骨骼矩阵的影响/**RGBA RGBA RGBA RGBA --矩阵1 16 骨骼节点1RGBA RGBA RGBA RGBA --矩阵2 16 骨骼节点2RGBA RGBA RGBA RGBA --矩阵3 16 骨骼节点3RGBA RGBA RGBA RGBA --矩阵4 16 骨骼节点4RGBA RGBA RGBA RGBA --矩阵5 16 骨骼节点5RGBA RGBA RGBA RGBA --矩阵6 16 骨骼节点6*/
所谓的蒙皮指的就是骨骼纹理
"skins" : [{"inverseBindMatrices" : 7,"joints" : [6,7,8,9,10,11],"skeleton" : 1}]
属性nodes这个就是记录用到的节点,可以把显示节点看成一个空间坐标系,所谓的节点继承其实不过就是为了完成空间矩阵的相乘,进而达到空间坐标系相互影响的目的,骨骼节点也是一种节点,这是为什么说骨骼动画是影响顶点动画的原因
name:节点的名称
children:这个节点又包含那些子节点,这里存储的是节点的索引
rotation,scale,translation:这三个是当前节点的旋转缩放平移,这个是构成节点空间坐标系的参数,你也可以理解为这是当前节点的空间坐标系的变化信息
mesh:这里存的是属性meshes索引
skin: 这里存的是数学skins索引
"nodes" : [{"children" : [4,6],"name" : "Armature.001","rotation" : [-0.7193397283554077,0.0,1.5972550578955585e-16,0.6946584582328796],"scale" : [2.6016106605529785,2.6016106605529785,2.6016106605529785],"translation" : [-2.672288363424831e-17,-0.12034916877746582,3.479184865951538]},{"mesh" : 0,"name" : "orca","rotation" : [0.7193397283554077,-9.52618489497488e-24,-1.5972550578955585e-16,0.6946584582328796],"scale" : [0.38437727093696594,0.38437727093696594,0.38437727093696594],"skin" : 0,"translation" : [3.278448433886058e-16,1.3348904848098755,0.09290271252393723]},......
buffer数据
gltf骨骼动画解析笔记相关推荐
- 【CocosBuilder 开发系列之一】cocos2dx使用CocosBuilder(编辑器)完成基础骨骼动画
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/cocosbuilder/1061.html ...
- Three.js加载外部模型骨骼动画
加载外部模型骨骼动画 上节课是通过Threejs程序创建一个骨骼动画然后解析播放,本节课是加载解析一个外部的骨骼动画模型文件. 查看骨骼动画数据 在解析模型骨骼动画之前,先加载外部的三维模型,查看骨骼 ...
- 【CocosBuilder 开发系列之一】cocos2dx使用CocosBuilder完成骨骼动画
关于CocosBuilder 已经成为cocos2d.cocos2dx的官方编辑器,主要作用是场景编辑器.新版本中还已经支持了骨骼动画.[对于CocosBuilder Himi最新书籍中也有讲述] 对 ...
- 关于骨骼动画及微软示例Skinned Mesh的解析
这是我自个写的,第一次发. 没想到这个贴子编辑器极差. 原文是有字体字色的.现在只能清一色了. 版主,发贴的编辑器太难用! 你有必要向上反映一下. 下面的字体是我敲html标记加上的,大家凑和看 ...
- Three.js - 通过 AnimationMixer混合器解析骨骼动画
1.骨骼动画原理 骨骼包括骨架和骨头,在three.js中,骨骼模型是SkinnedMesh就是具有骨架Skeleton和骨头bones的网格Mesh,骨骼网格可以控制几何体Geometry的顶点生成 ...
- Unity学习笔记(7) Unity2D骨骼动画制作流程
用骨骼动画最大的好处就是方便之后做换装系统. 无论对3Dor2D游戏都是这样的. 这篇博文记录一下2D骨骼动画的制作流程,参考b站麦扣的系列教程: https://space.bilibili.com ...
- Direct-X学习笔记--骨骼动画
学了几个月DX了,终于到了骨骼动画这一步了,好激动!之前导入过一些静态的模型,还是挺帅的.不过不能动,实在是太遗憾了.今天学习了骨骼动画,终于让偶的模型动起来啦!!! 一.简介 说到动画,其实本人又想 ...
- dae模型如何合并_FBX、DAE模型的格式、导入与骨骼动画
FBX和DAE分别是Autodesk和Khronos旗下核心的可交换型3D模型格式,也算是当前主流的支持顶点蒙皮骨骼动画的格式,在实时渲染领域基本是无人不识了.本文主要以笔记形式记录一下读取这两种模型 ...
- CSharpGL(50)使用Assimp加载骨骼动画
CSharpGL(50)使用Assimp加载骨骼动画 在(http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html)介绍了C++用Asism ...
最新文章
- C语言 数据结构与算法 一
- QT中写一个求QVector容器中数据均值的函数
- 【Raspberry Pi】webpy+mysql+GPIO 实现手机控制
- mysql列连接_连接来自MySQL中不同表的列
- 第二季3:海思MPP模块与视频缓冲池
- 跟一个大佬前辈交流了一下
- 双目密集匹配的一般过程
- Tooltip工具提示控件的使用
- 如何有效地遍历Java Map中的每个条目?
- 利用console控制台调试php代码
- 小米造富神话虚实:平均年薪16万 470亿元期权咋分
- 【图文】远程桌面链接:这可能是由于credssp加密oracle修正
- HBase安装配置及测试
- 校园二手市场需求分析
- Mac下载pd虚拟机以及激活
- 几招紧急应对夏天雷雨天机房断电的方法!
- MySQL数据库如何备份、恢复、导出与导入
- linux mmc 读写,这个mmc读取linux的原因是什么
- WRF模型模拟时所遇到的问题及解决方法
- 如何像我这样创建一个酷炫且能赚钱的网站(使用宝塔安装WordPress搭建子比主题)