大家好,我是程序员 kenney,今天给大家介绍
glTF
的基础知识
。
glTF 是什么?
它是 GL Transmission Format 的缩写,是 khronos推 出的一种描述3D模型的格式,目标是使其成为一种通用的3D模型格式。它的官方github是 https://www.khronos.org/gltf/
我们知道3D模型有各种各样的格式,如obj、FBX、dea等, 格式的多种多样带来了一个问题的就是各种软件、渲染引擎、游戏引擎之间的3D模型的不通用 。
例如在3D设计软件中设计好一个3D模型之后,想把它放到一个渲染引擎中渲染,如果没有一个通用的格式,渲染引擎可能也有一套自己的格式,这样就要做格式转换,而有些格式是不开源的,需要官方提供转换工具,非常麻烦。
而如果有一个通用的格式大家都支持,这就好办了,就像图片中的jpeg一样,通常的图片编辑软件都能导出jpeg,通常的图片查看软件也都能打开,这就非常方便,而 glTF就是要成为3D模型界的 jpeg 。
glTF 是一用 json 格式编写的 ,目前现在推出了2.0版本,本篇文章也是讲解2.0版本,glTF格式中可以描述的信息相当丰富,本篇文章先给大家介绍一些基础的字段:
场景和节点描述:scenes、nodes
网格描述:meshes
数据描述:buffers、bufferViews、accessors
材质描述:materials
纹理、图片和采样器描述:textures、images、samplers
摄像机描述:cameras
总览
我们先来从一张图总体上看一下glTF文件的结构,每一种类型的字段,基本是都是用数组来组织,在每个字段中,又可以通过索引去引用其它字段。
它类似一个树状,但又不是严格的树。
最顶层的是场景,场景中包含了一些节点,节点中又可以套子节点,节点中可以有mesh、摄像机,如果是mesh,还可以为它指定材质及mesh网格数据,材质中可以指定纯色及图片纹理及其采样配置,还可以描述蒙皮及动画 。
场景和节点
场景和节点分别用scenes、nodes字段描述,场景就是代表要渲染的一些东西的集合,场景里面包含一些节点,每个节点可以认为是一个物体(也可以是摄像机),节点也可以是空的,节点中可以继续挂子结点。
来看上面这个例子,scene字段指定了使用第0个场景,一般来说,同一时刻只会渲染一个场景。
scenes字段是一个场景数组,描述所有场景,这个例子中场景只有一个,它里面的nodes字段描述这个场景中有哪些节点,这里是用索引为0、1、2的节点,它就对应了下面nodes数组中第0、1、2个节。nodes字段描述了所有节点,节点里可以用children描述子节点,子节点的描述方式也是索引号。这样,通过上图左中的这段json,我们就描述了如上图右中的那样一个场景结构。
可以为每个结点指定它的变换参数,这个变换参数是它相对于父节点的变换,可以用变换矩阵描述,也可以分别指定平移、旋转和缩放。还可以指定它的mesh及摄像机,mesh就描述了这个节点是什么物体,而camera会影响这个节点被观察的样子 。
网格描述
有了节点之后,我们要描述节点的东西是什么,一个东西首先要有形状,比如一个立方体,或者一个圆柱,这就需要meshs来描述:
meshs同样也是一个数组,每个mesh中由primitives描述图元,其中的mode是图元的种类,也就是点、线或者三角形,indices表示accessors中的索引,后面会讲到。
attributes描述的是attribute的结构,里面可以有顶点坐标、纹理坐标、法向量等,同样也是以索引的方式引用accessors。最后的material表示这个mesh的材质,也是用索引来引用 。
数据描述
glTF中用buffers、bufferViews、accessors来描述数据:
buffers是数据块数组,每个buffer由长度和路径描述,bufferViews同样也是数组,其中每个bufferView表示一个buffer的视图。这个怎么理解呢?buffer是一堆纯数据,它本身是不描述这堆数据用来做什么的,而bufferView就可以理解成如何去看待这堆数据。
其中buffer表示它对应的buffers中的索引。
target描述了它是用来干什么的,比如上图中的34963就对应了GL_ELEMENT_ARRAY_BUFFER,这个值是和OpenGL库里定义的值是一致的,这样使用起来就相当友好,glBindBuffer()可以直接传这里解析出来的target值而不用做映射转换。
byteOffset描述的是这块buffer的偏移字节,byteLength是字节长度,bytteStride描述的是一个数据的开始距离下一个数据开始的跨度,如果相同语义的数据不是连续存在放的,bytteStride就会比一份数据的长度大,因为它要跨过其它类型的数据。
再往下是accessors,它同样也是一个数组,其中每个accessor描述了对应bufferView中的数据以什么样的方式进行访问,在上图上,偏移了4个字段,以VEC2的方式取数据,即二维向量,向量每个成份的类型componentType是5126,它对应的是GL_FLOAT,count表示取数据的个数 。
材质描述
materials中每个元素是一个material :
例如上图中的例子通过金属度-粗糙度模型来描述材质,其中有一些材质纹理图,比如基础纹理图、法向图、遮挡纹理图等,每个纹理图通过index来对应纹理图数组中的索引,纹理坐标也是类似的 。
纹理、图片和采样器描述
纹理、图片和采样器分别通过textures、images、samplers描述,看下面的例子 :
textures中每个元素通过source索引到images数组中,从而找到对应的纹理数据,images数组中的元素可以以图片路径的方式提供图片,也可以用bufferView的方式,bufferView最终又对应到一个数据块上。
samplers则描述了采样器的配置,大家一看里面的字段就会很熟悉,同样的,它也是直接对应了OpenGL库里定义的值,解析出来直接使用,非常方便 。
摄像机描述
摄像机用cameras描述 :
熟悉OpenGL的同学一眼就能看出里面定义了一个透视投影相机和正交投影相机,以及视角、近/远平面等参数,这个比较简单,没有太多好说的 。
完整例子
我们来看一下完整的例子,glTF官方提供了很多sample models:
https://github.com/KhronosGroup/glTF-Sample-Models
我选取了一个比较简单的立方体,大家看完文章可以尝试阅读一下它的glTF文件,看看是否能理解其中的描述 :
{
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 36,
"max" : [
35
],
"min" : [
0
],
"type" : "SCALAR"
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000001
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
-0.000000,
-0.000000,
1.000000
],
"min" : [
0.000000,
-0.000000,
-1.000000,
-1.000000
],
"type" : "VEC4"
},
{
"bufferView" : 4,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000
],
"type" : "VEC2"
}
],
"asset" : {
"generator" : "VKTS glTF 2.0 exporter",
"version" : "2.0"
},
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 72,
"byteOffset" : 0,
"target" : 34963
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 72,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 504,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 576,
"byteOffset" : 936,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 1512,
"target" : 34962
}
],
"buffers" : [
{
"byteLength" : 1800,
"uri" : "Cube.bin"
}
],
"images" : [
{
"uri" : "Cube_BaseColor.png"
},
{
"uri" : "Cube_MetallicRoughness.png"
}
],
"materials" : [
{
"name" : "Cube",
"pbrMetallicRoughness" : {
"baseColorTexture" : {
"index" : 0
},
"metallicRoughnessTexture" : {
"index" : 1
}
}
}
],
"meshes" : [
{
"name" : "Cube",
"primitives" : [
{
"attributes" : {
"NORMAL" : 2,
"POSITION" : 1,
"TANGENT" : 3,
"TEXCOORD_0" : 4
},
"indices" : 0,
"material" : 0,
"mode" : 4
}
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "Cube"
}
],
"samplers" : [
{}
],
"scene" : 0,
"scenes" : [
{
"nodes" : [
0
]
}
],
"textures" : [
{
"sampler" : 0,
"source" : 0
},
{
"sampler" : 0,
"source" : 1
}
]
}
glTF有很多方法能方便地查看效果,可以在线看,比如 gltf-viewer.donmccurdy.com ,上面那个glTF例子的渲染效果是这样的 :
谢谢阅读!如有疑问,欢迎在评论区交流~
欢迎关注我的 github:www.github.com/kenneycode
原文链接: https://juejin.cn/post/6897480101177491463
-- END --
进技术交流群, 扫码添加我的微信:Byte-Flow
获取相关资料和源码
学习音视频、OpenGL ES、Vulkan 、Metal、图像滤镜、视频特效及相关渲染技术的付费社群,面试指导,1v1 简历服务,职业规划。
我的付费社群
推荐: