图形语言传输格式glTF和三维瓦片数据3Dtiles(b3dm、pnts)学习

news/2024/9/17 3:42:05/ 标签: 3d

文章目录

  • 一、3DTiles
  • 二、b3dm
  • 三、glTF
    • 1.glTF 3D模型格式有两种
    • 2.glTF 场景描述结构和坐标系
    • 3.glTF的索引访问与ID
    • 4.glTF asset
    • 5.glTF的JSON结构
      • scenes
        • scene.nodes
      • nodes
        • nodes.children
      • transformations
      • 对外部数据的引用
      • buffers 原始二进制数据块,没有固有的结构或含义
      • bufferViews 索引视图(buffer的数据的切片/缓冲区的子集)
      • accessors 访问器
        • Data type 数据类型
          • type (必需)
          • componentType (必需)
          • count (必需)
          • min 和 max 属性
          • 例子
        • Data alignment 数据对齐
        • Data interleaving 数据交错
          • 单属性bufferView
          • 多属性bufferView—— Array-Of-Structures
      • Sparse accessors 稀疏访问器(glTF 2.0)
      • meshes(primitives数组:必需)
        • primitives.attributes
        • primitives.mode —— default: 4 TRIANGLES
        • primitives.targets (Morph Targets 变形目标,glTF 2.0)
        • 例子:最小 glTF 文件
        • 例子:Simple Meshes
      • Skins(后补
      • Instantiation 实例化——同一网格可以由许多节点使用,这些节点可能具有不同的变换
      • Materials
      • Texture
      • images

一、3DTiles

原文
工具+资料
格式详解
格式详解!
3D Tiles 是一种开源的、优化的文件格式,支持逐级细节(LOD)和空间索引,使得数据可以根据视点距离动态加载和卸载。
3DTiles由tileset.json和tile组成,其中tile可以是.b3dm、.i3dm、.pnts、.vctr和.cmpt中的任一种格式文件。

  • 即并没有统一的数据格式,实际上它定义数据的基本框架是瓦片集,瓦片集包括瓦片集数据(tileset.json)和瓦片数据(tile),其中瓦片数据可以是多种文件类型:
    在这里插入图片描述

3dm_13">二、b3dm

B3DM 是 3D Tiles 格式中的一种子格式,专门用于表示和存储三维模型。它通常包含几何数据、纹理和其他属性,适用于大规模的3D城市模型和建筑物。B3DM 文件通常封装了 glTF 模型,可以通过 3D Tiles 渲染引擎进行高效展示和流式传输。

即b3dm数据是基于 gltf 的 ( 单模型基础上做了批量化处理 )。
解析
读取工具TinyGLTFLoader——picojson库
格式详解参考文章、
工具+格式详解参考文章
在这里插入图片描述

三、glTF

原文
是一种针对GL(WebGL,OpenGL ES以及OpenGL)接口的运行时资产传递格式,是一种开放标准的3D模型文件格式,glTF通过提供高效、可扩展、可互操作的格式来传输和加载三维内容,填补了3D建模工具与现代图形应用程序之间的空白。

1.glTF 3D模型格式有两种

*.gltf: 基于JSON的文本文件,可使用文本编辑器轻松编辑,通常会引用外部文件,例如纹理贴图、二进制网格数据等;

即以JSON 格式描述模型的结构,并可以包含二进制数据(如顶点数据和纹理)来优化加载性能。

*.glb: 是二进制格式,通常文件较小且自包含所有资源,但不容易编辑。

VS Code编辑器,建议安装 glTF Tools 扩展工具,能够非常方便的查看glTF的数据结构、glTF和glb互转等。
可以说glTF是3D 模型的JPEG格式。

2.glTF 场景描述结构和坐标系

格式与规范!!!!!!
官方教程
教程
格式详解参考文章
格式详解参考文章
解析

  • JSON格式的文件(.gltf)

包含完整的场景描述,并通过场景结点引用网格进行定义 。包括:节点层次结构、材质(定义了3D对象的外观)、相机(定义义了渲染程序的视锥体设置 )、mesh(网格)、动画(定义了3D对象的变换操作,比如选择、平移操作)、蒙皮(定义了3D对象如何进行骨骼变换)等;

  • .bin包含几何和动画数据以及其他基于缓冲区的数据的二进制文件
  • 图像文件(.jpg,.png)的纹理
  • GLSL 着色器源代码的 GLSL 文本文件 ().glsl

以其他格式定义的资源(例如图像和 GLSL 着色器源代码)可以存储在通过 URI 引用的外部文件中,也可以使用数据 URI 直接嵌入到 JSON 中。
在这里插入图片描述在这里插入图片描述

  • glTF 使用右手坐标系,和opengl一样的坐标系
    3d52cd6c44da877.png" alt="在这里插入图片描述" />

所有线性距离的单位均为米。
所有角度均以弧度为单位。
正旋转是逆时针旋转。

3.glTF的索引访问与ID

场景对象以数组的形式存储在JSON文件的字典中,可以使用ID+数组中各个对象的索引/ID来访问它们:

  • 例子1:
对象里面还可以接着通过索引/id来引用
"buffers": {"a-buffer-id1": {"byteLength": 1024,"type": "arraybuffer","uri": "path-to.bin"},"a-buffer-id2":{...},
},"bufferViews": {"a-bufferView-id1": {"buffer": "a-buffer-id1",//或 "buffer": 0,"byteLength": 512,"byteOffset": 0},"a-bufferView-id2": {...}
}
  • 例子2:
"meshes": {"FirstExampleMeshId": { ... },"SecondExampleMeshId": { ... },"ThirdExampleMeshId": { ... }
}"nodes:" {"FirstExampleNodeId": {"meshes": ["FirstExampleMeshId"]},...
}
或者
"nodes:" {"FirstExampleNodeId": {"meshes": 01....},...
}

不用id直接用索引也行:

"scenes": [{"name": "singleScene","nodes": [0]}],"scene": 0

glTF 顶级字典对象 (accessors, animations, buffers, bufferViews, cameras, images, materials, meshes, nodes, programs, samplers, scenes, shaders, skins, techniques, and textures)的 ID 位于同一命名空间中,并且是唯一的。

"buffers": {"an-id": {// ...}
},
"bufferViews": {"an-id": { // Not allowed since this ID is already used// ...}
}

非顶级 glTF 字典对象的 ID 都位于其自己的命名空间中,ID 只用在对象中是唯一的,由 JSON 强制执行,例如 不同animation.samplers的id可以重复。
例如,允许执行以下操作:

// gltf1.0
"animations": {"animation-0": {// ..."samplers": {"animation-sampler-id": {// ...}}},"animation-1": {// ..."samplers": {"animation-sampler-id": { // 不会冲突// ...}}}
}

4.glTF asset

顶级必须包含 asset 属性,其中必须包含 version 指明 glTF 的版本,还可包含如下的其他附加信息。

"asset": {"extras": {"author": "EdwinRC (https://sketchfab.com/Edwin3D)","license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)","source": "https://sketchfab.com/3d-models/low-poly-winter-scene-2ffe1ed449414cc184078dd1e2882284","title": "Low Poly Winter Scene"},"generator": "Sketchfab-4.86.2","version": "2.0","copyright": "2017 (c) Khronos Group"
}

5.glTF的JSON结构

glTF格式本质上是一个JSON文件。这一文件描述了整个3D场景的内容。
在这里插入图片描述
glTF属性2.0详解

scenes

通过引用node来定义的场景图,glTF asset包含一个或多个scenes,An object containing a list of root nodes to render.
3dd1.png" alt="> 在这里插入图片描述" />

scene.nodes
  • scene.nodes 数组中列出的所有节点都必须是根节点,即,它们不得在任何节点的 node.children 数组中列出。
  • The same root node MAY appear in multiple scenes。
  • 每个元素都必须是唯一的。
"scene": "defaultScene",
"scenes": {"defaultScene": {"nodes": ["node_1"]}
},
 "nodes": [{"name": "singleNode"//索引0}],"scenes": [{"name": "singleScene","nodes": [0]}],"scene": 0

scene:根级别属性(注意为单数)标识数组中的哪些场景应在加载时显示,glTF中的场景描述的入口点;如果未定义,则客户端实现可以延迟渲染,直到请求特定场景。

nodes

它可以包含 mesh 或应用平移 (translation)、旋转 (rotation)、缩放 (scale) 或任意矩阵变换 (matrix),可以引用其他(子)节点等。另外,它可以引用附加到该节点的 mesh 或者 camera 实例,或者描述网格变形的 skin 。
更多属性见官方glTF属性2.0详解

nodes.children
children :此节点的子节点的索引,非必须。这些children 节点中的每一个都可以有自己的子节点。
"nodes": [{"name": "Car","children": [1, 2, 3, 4]},{"name": "wheel_1"},{"name": "wheel_2"},{"name": "wheel_3"},{"name": "wheel_4"}]

在这里插入图片描述

nodes[0] 包含子节点 nodes[1],并且应用了旋转变换,这里旋转用四元数表示。
而 nodes[1] 包含了 meshes[0] 的网格数据。
"nodes": [{"children": [1],"name": "RootNode (gltf orientation matrix)","rotation": [-0.70710678118654746,-0,-0,0.70710678118654757]},{"mesh": 0,"name": "Cylinder.008_0"},// ...
]

transformations

  • 节点的局部变换:
    每个node都可以有一个变换,此转换将应用于附加到节点本身及其所有子节点的所有元素:
matrix矩阵
"nodes": [{"name": "node-camera","camera": 1,"matrix": [2.0,    0.0,    0.0,    0.0,0.0,    0.866,  0.5,    0.0,0.0,   -0.25,   0.433,  0.0,10.0,   20.0,   30.0,    1.0]}]

在这里插入图片描述
也可以使用translation ,旋转,和scale节点的属性:

TRS 属性
"nodes": [{"name": "Box","translation": [ 10.0, 20.0, 30.0 ],"rotation": [ 0.259, 0.0, 0.0, 0.966 ],"scale": [ 2.0, 1.0, 0.5 ]}]
//伪代码:localTransform = translationMatrix * rotationMatrix * scaleMatrix;必须是这种顺序。

当这三个属性中的任何一个未给定时,将使用单位矩阵。类似地,当一个节点既不包含matrix属性或TRS属性,则其局部变换将成为单位矩阵。

  • 节点的全局变换:
    在这里插入图片描述

接下来是关于二进制数据存储:

对外部数据的引用

在这里插入图片描述

buffers 原始二进制数据块,没有固有的结构或含义

允许高效创建 GL 缓冲区和纹理,因为它们不需要额外的解析。
glTF 可以具有任意数量的buffer,都定义在数组buffers中。

  • 虽然缓冲区的大小没有硬性上限,但 glTF 不应使用大于 2^53 字节的缓冲区,因为某些 JSON 解析器可能无法正确解析它们。
  • 存储为glb 二进制块的缓冲区具有 2byteLength^32-1 字节的隐式限制。
  • 缓冲区数据是 little endian(小端字节顺序)。
    在这里插入图片描述
    "buffers": {"duck": {"byteLength": 102040,//缓冲区文件大小,必须大于等于该缓冲区"type": "arraybuffer",//数据的存储方式,二进制数组缓冲区还是文本"uri": "duck.bin"//指向外部文件,例如binary geometry, animation, or skins}},"buffers": [{"byteLength": 102040,"uri": "duck.bin"}]
  • 数据URI

数据URI它直接在JSON文件中对二进制数据进行编码,参考

"buffers" : [{"uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=","byteLength" : 44}],

bufferViews 索引视图(buffer的数据的切片/缓冲区的子集)

此切片使用偏移量byteOffset和长度byteLength(以字节为单位)定义。
在这里插入图片描述
在这里插入图片描述

"bufferViews" : [{"buffer" : 0,"byteOffset" : 0,"byteLength" : 6,"target" : 34963//表示顶点索引ELEMENT_ARRAY_BUFFER},{"buffer" : 0,"byteOffset" : 8,"byteLength" : 36,"target" : 34962//表示顶点属性ARRAY_BUFFER}],

同一缓冲区视图bufferViews 不得同时用于顶点索引ELEMENT_ARRAY_BUFFER和顶点属性ARRAY_BUFFER。

  • target:渲染器稍后可能会使用此属性对缓冲区视图所引用的数据的类型或性质进行分类。
  • 这允许客户端实现提前将每个缓冲区视图指定给适当的处理步骤,例如,具有顶点索引和属性的缓冲区视图将被复制到相应的 GPU 缓冲区,而带有图像数据的缓冲区视图将传递给特定于格式的图像解码器。

在这里插入图片描述

  • 浅灰色显示的字节是正确对齐访问器所需的填充字节,详见下文数据对齐

accessors 访问器

buffers 和 bufferView 不包含类型信息。它们只是定义要从文件中检索的原始数据。glTF 文件中的对象(网格、皮肤、动画)从不直接访问 buffers 或 bufferView,而是通过访问器accessors 访问。

访问器定义了一种从 bufferView 中检索类型化数组数据的方法。访问器指定component type(例如 FLOAT)和type (例如 VEC3),它们组合在一起时定义每个数组元素的完整数据类型
元素可以是,例如,顶点索引vertex indices、顶点属性vertex attributes、动画关键帧animation keyframes等。
访问器还使用属性 byteOffset(指定引用的缓冲区视图bufferView中第一个数据元素的位置) 和 count (数组元素个数)指定 bufferView 中数据的位置和大小。

Data type 数据类型

The type of an accessor’s data is encoded in the type and the componentType properties.

type (必需)

值是一个字符串,用于指定数据元素是标量、向量还是矩阵,即指定了一个元素的组件个数。
在这里插入图片描述

componentType (必需)

访问器组件的数据类型。
UNSIGNED_INT type MUST NOT be used for any accessor that is not referenced by mesh.primitive.indices.

Allowed values:

  • 5120 BYTE
  • 5121 UNSIGNED_BYTE
  • 5122 SHORT
  • 5123 UNSIGNED_SHORT
  • 5125 UNSIGNED_INT
  • 5126 FLOAT
count (必需)

此访问器引用的元素数,count specifies the number of attributes within the bufferView, not the number of bytes。

min 和 max 属性
  • 代表所有数据元素的组件级最小值和最大值,所以就是一个type+component。
  • 如果是vertex positions的accessor,则the min and max properties thus define the bounding box of an object,且为必需。
例子
对应上文bufferView的访问器accessor"accessors" : [{"bufferView" : 0,// "target" : 34963,表示顶点索引ELEMENT_ARRAY_BUFFER"byteOffset" : 0,//该访问器第一个数据元素在bufferView中的位置,多个accessor可以指向同一个bufferView"componentType" : 5123,// UNSIGNED_SHORT,组件类型"count" : 3,//元素个数"type" : "SCALAR",//数据类型,指定组件个数"max" : [ 2 ],"min" : [ 0 ]},{"bufferView" : 1,//"target" : 34962//表示顶点属性ARRAY_BUFFER,可以拥有多个属性"byteOffset" : 0,"componentType" : 5126,//FLOAT"count" : 3,"type" : "VEC3","max" : [ 1.0, 1.0, 0.0 ],// 分量个数由type决定,每个分量数据类型由componentType决定"min" : [ 0.0, 0.0, 0.0 ]}],
Data alignment 数据对齐

访问器引用的数据可以发送到图形卡进行渲染,也可以在主机端用作动画或蒙皮数据。因此,必须根据数据类型对齐存取器的数据。
对齐要求如下:

  • 访问器的 byteOffset 必须能被其 componentType 的大小整除。
  • 访问器的 byteOffset 和它引用的 bufferView 的 byteOffset 之和必须能被其 componentType 的大小整除。

accessor.byteOffset + bufferView.byteOffset能被componentType 的大小(byte)整除。

  • 定义 byteStride 时,它必须是访问器组件类型componentType大小的倍数。

当两个或多个 vertex 属性访问器使用相同的 bufferView 时,必须定义其 byteStride,参考 数据交错

  • 每个访问器都必须适合其 bufferView,不能越界。
  • 出于性能和兼容性原因,vertex 属性的每个元素都必须与 bufferView 内的 4 字节边界对齐

即 此时accessor.byteOffset 和 bufferView.byteStride必须是 4 的倍数。

  • matrix 类型的访问器按列优先顺序存储数据;每列的 start 必须与 4 字节边界对齐。

具体来说,当 ROWS * SIZE_OF_COMPONENT(其中 ROWS 是矩阵的行数,SIZE_OF_COMPONENT指定了分量的字节数)不是 4 的倍数时, (ROWS * SIZE_OF_COMPONENT) % 4 必须在每列的末尾插入填充字节。
3d6d6320c7044e9d9ae80e5aa8961530.png" alt="在这里插入图片描述" />
3dc7f8c8eb4176b1653b79ea74f1b9.png" alt="在这里插入图片描述" />
在这里插入图片描述
对齐要求仅适用于每列的开头,因此如果没有更多数据,则可以省略尾随字节

"bufferViews" : [{"buffer" : 0,"byteOffset" : 0,"byteLength" : 6,"target" : 34963},{"buffer" : 0,"byteOffset" : 8,"byteLength" : 36,"target" : 34962}],"accessors" : [{"bufferView" : 0,"byteOffset" : 0,"componentType" : 5123,"count" : 3,"type" : "SCALAR","max" : [ 2 ],"min" : [ 0 ]},{"bufferView" : 1,"byteOffset" : 0,"componentType" : 5126,"count" : 3,"type" : "VEC3","max" : [ 1.0, 1.0, 0.0 ],"min" : [ 0.0, 0.0, 0.0 ]}],

“bufferView” : 1的byteOffset为8,加上"accessors":0的byteOffset为0,能被"componentType" : 5126的大小4整除,因此,缓冲区的字节 6 和 7 是不携带相关数据的填充字节。
在这里插入图片描述

Data interleaving 数据交错

当缓冲区视图bufferView用于顶点属性数据(target : ARRAY_BUFFER)时,它可以具有多个属性,并且可以Data interleaving数据交错

  • 此时将针对这多个属性中的每一个属性,都创建一个专属accessor对其访问数据,即多个accessors 使用相同的缓冲区视图bufferView,则必须定义byteStride(可accessor.byteStride,且未定义则数据将紧密打包)。
  • bufferView.byteStride:访问器的一个元素的开头与下一个元素的开头之间的字节数,同类型顶点属性之间的步幅(前一个头到下一个头,以字节为单位)。
单属性bufferView

即accessor.component字节数*accessor.type字节数。

在这里插入图片描述

3d337a.png" alt="在这里插入图片描述" />

"accessor_1": {"bufferView": "bufferView_1",//多个属性/元素"byteOffset": 7032,//该访问器第一个数据元素在bufferView中的位置,多个accessor可以指向同一个bufferView"byteStride": 12,//元素大小/步长(字节)"componentType": 5126, // FLOAT,组件类型"count": 586,//元素个数"type": "VEC3"//组件个数
}

在此访问器中,componentType 为 5126 (FLOAT),因此每个组件为 4 个字节。类型为 “VEC3”,因此有三个组件。属性类型的大小byteStride为 12 字节 (4 * 3)。

多属性bufferView—— Array-Of-Structures

在这里插入图片描述

Sparse accessors 稀疏访问器(glTF 2.0)

当存在包含顶点位置的几何数据时,此几何数据可用于多个对象,这可以通过引用两个对象中的同一访问器来实现。
如果两个对象的顶点位置基本相同,并且只有几个顶点不同,则无需将整个几何数据存储两次。相反,可以只存储一次数据,并使用稀疏访问器仅存储与第二个对象不同的顶点位置(即用稀疏访问器数据替换原来的几何数据,构成新的几何对象)。

  • sparse 对象包含以下 REQUIRED 属性:
  • count: number of displaced elements,需要位移元素的数量,或者需要替换的顶点的数量。
    This number MUST NOT be greater than the number of the base accessor elements.
    此数字不得大于基于的访问器元素的数量。
  • indices: 描述bufferView(Required ),componentType(Required ),byteOffset等。
    索引必须形成一个严格递增的序列。并且The number of indices is equal to count.。
  • values: 描述bufferView(Required),byteOffset等。
  • PS: the referenced buffer view MUST NOT have its target or byteStride properties defined
{"scenes" : [ {"nodes" : [ 0 ]} ],"nodes" : [ {"mesh" : 0} ],"meshes" : [ {"primitives" : [ {"attributes" : {"POSITION" : 1},"indices" : 0} ]} ],"buffers" : [ {"uri" : "data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=","byteLength" : 284} ],"bufferViews" : [ {"buffer" : 0,"byteOffset" : 0,"byteLength" : 72,"target" : 34963// 顶点索引ELEMENT_ARRAY_BUFFER,而下面几个bufferView受sparse引用,// MUST NOT have its target or byteStride properties defined. }, {"buffer" : 0,"byteOffset" : 72,"byteLength" : 168}, {"buffer" : 0,"byteOffset" : 240,"byteLength" : 6}, {"buffer" : 0,"byteOffset" : 248,"byteLength" : 36} ],// 两个访问器,一个用于网格的索引(0);一个用于顶点位置(1),并且附加 accessor.sparse 属性"accessors" : [ {"bufferView" : 0,"byteOffset" : 0,"componentType" : 5123,//2byte"count" : 36,//2*36 = 72,对应"bufferView" : 0中"byteLength" : 72"type" : "SCALAR",//1分量"max" : [ 13 ],"min" : [ 0 ]}, {"bufferView" : 1,"byteOffset" : 0,"componentType" : 5126,//float,4"count" : 14,"type" : "VEC3","max" : [ 6.0, 4.0, 0.0 ],"min" : [ 0.0, 0.0, 0.0 ],"sparse" : {"count" : 3,"indices" : {"bufferView" : 2,"byteOffset" : 0,"componentType" : 5123// unsigned_short,2byte},"values" : {"bufferView" : 3,"byteOffset" : 0}}} ],"asset" : {"version" : "2.0"}
}

用于顶点位置(1),并且附加 accessor.sparse 属性:

 "accessors" : [ ...{"bufferView" : 1,"byteOffset" : 0,"componentType" : 5126,//float,4byte"count" : 14,"type" : "VEC3","max" : [ 6.0, 4.0, 0.0 ],"min" : [ 0.0, 0.0, 0.0 ],"sparse" : {"count" : 3,"indices" : {"bufferView" : 2,//包含索引"byteOffset" : 0,"componentType" : 5123//UNSIGNED_SHORT,2byte},"values" : {"bufferView" : 3,//包含新的顶点坐标位置"byteOffset" : 0}}} ],

1.原始几何数据在bufferView:1中,34 = 12字节,1214 = 168字节,对应bufferView:0中"byteLength",配合"byteLength" : 72,代表从"buffer" : 0的72~72+168字节都是bufferView:1。一个元素为vec3f,有14个,其实又buffer中数据可知对应的是14个坐标点:
在这里插入图片描述
2.接下来是accessor:1中sparse accessor,有3个元素。

  • 首先sparse.indices索引属性对应的bufferView:2是从"buffer" : 0中的72+168=240字节开始(也和bufferView:2中"byteOffset" : 240对应上)。代表了3个用UNSIGNED_SHORT表示的索引,3*2byte = 6(对应bufferView:2中"byteLength" : 6)。
  • 其次是sparse.values值属性对应的bufferView:3是从"buffer" : 0中的240+6+2(为了字节对齐246不能整除4) = 248字节开始。代表了3个vec3f,即3个需要替换的新顶点坐标位置。
    3dd9a81dc3043ddbbf0fec98888774f.png" alt="在这里插入图片描述" />
    在这里插入图片描述

meshes(primitives数组:必需)

Any node MAY contain one mesh

  • meshs被定义为primitives数组,对应GL绘图所需的数据。
  • primitives可以指定一个或多个"attributes" ,对应于绘制调用中使用的顶点属性(类似opengl中顶点属性概念)。
  • Indexed primitives可以定义 “indices”。
  • attributes和indices定义为访问器
  • 每个primitive还可以指定一个材质material 和一个与 GL 基元类型相对应的基元类型(例如,triangle )
  • If material is undefined, then a default material MUST be used.

每个primitive基元对应一个 GL 绘制调用(当然,引擎可以自由地批量绘制调用)。
定义基元的 indices 属性时,它会引用用于索引数据的访问器,并且应使用 GL 的 drawElements 函数。
如果未定义 indices 属性,则应使用 GL 的 drawArrays 函数,其 count 等于 attributes 属性引用的任何访问器accessor 的 count 属性(对于给定的基元,它们都相等)。

primitives.attributes
  • attributes的名称对应于标识 vertex attribute顶点属性的枚举值,此值将映射到网格的 GLSL 着色器中的特定命名属性

有效的attributes的名称有:

  • 1.0:POSITION, NORMAL, TEXCOORD, COLOR, JOINT, and WEIGHT。
  • 2.0:POSITION、NORMAL、TANGENT、TEXCOORD_n、COLOR_n、JOINTS_n 和 WEIGHTS_n。
    可以是 [semantic]_[set_index] 的形式,例如 TEXCOORD_0、TEXCOORD_1 等。
    特定于应用程序的属性语义必须以下划线开头,例如 _TEMPERATURE。特定于应用程序的属性语义不得使用 unsigned int 组件类型。
  • attributes的值是包含数据的访问器accessor 的 ID。
    下面定义了每个attribute 的有效访问器type 类型和component type组件类型:
    在这里插入图片描述
    在这里插入图片描述
    注意:
  • POSITION accessor 必须定义其 min 和 max 属性。
  • 每个 TANGENT accessor 元素的 W 分量必须设置为 1.0 或 -1.0。
  • 当 COLOR_n attribute 使用“VEC3”类型的访问器时,必须假定其 alpha 分量的值为 1.0。
  • 采用 [semantic]_[set_index] 格式时,所有索引必须以 0 开头,并且是连续的正整数:TEXCOORD_0、TEXCOORD_1 等。索引不得使用前导零来填充位数(例如,不允许使用 TEXCOORD_01)。
  • All attribute accessors for a given primitive MUST have the same count.
  • When indices property is not defined, attribute accessors’ count indicates表示 the number of vertices to render。
  • when indices property is defined, it indicates the upper (exclusive) bound on the index values in the indices accessor(它表示 indices 访问器中索引值的上限(不包括)), i.e., all index values MUST be less than attribute accessors’ count(因为索引从0开始)。
  • indices accessors 不得包含所用组件类型的最大可能值(例如,255 表示无符号字节,65535 表示无符号整数,4294967295表示无符号整数)。

最大值会触发某些图形 API 中的基元重启,并且需要客户端实现重新构建索引缓冲区。

  • 当 indices 属性未定义时,要渲染的顶点索引数由attribute accessors的count 定义(使用范围 [0…count) 中的隐含值)。
  • 定义 indices 属性时,要渲染的顶点索引数由 indices 引用的accessors的count 定义。在任何一种情况下,顶点索引的数量都必须对所使用的拓扑类型有效+拓扑类型定义如下:
  • Points :pi = {vi};顶点索引的数量MUST be non-zero。
    在这里插入图片描述
  • Line Strips:pi = {vi, vi+1};顶点索引的数量MUST be 2 or greater。
    3d6339adcdbe.png" alt="在这里插入图片描述" />
  • Line Loops:pi = {vi, vi+1};顶点索引的数量MUST be 2 or greater。
    3da95.png" alt="在这里插入图片描述" />
  • Lines:pi = {v2i, v2i+1};顶点索引的数量MUST be divisible by 2 and non-zero.
    在这里插入图片描述
  • Triangles:pi = {v3i, v3i+1, v3i+2};顶点索引的数量MUST be divisible by 3 and non-zero.
    在这里插入图片描述
  • Triangle Strips:pi = {vi, vi+(1+i%2), vi+(2-i%2)};
    在这里插入图片描述
  • Triangle Fans:pi = {vi+1, vi+2, v0};
    在这里插入图片描述
  • 网格几何图形不应包含退化的线条或三角形,即每个拓扑基元多次使用同一顶点的线条或三角形。
  • 如果未指定切线,则应使用默认MikkTSpace算法计算切线,并使用与法线纹理关联的指定顶点位置、法线和纹理坐标。
  • 如果未指定法线,则客户端实现必须计算flat normals平坦法线,并且必须忽略提供的切线(如果存在)。‘
  • 同一三角形的顶点应具有相同的 tangent.w 值。当同一三角形的顶点具有不同的 tangent.w 值时,其切线空间被视为未定义。
  • bitangent vectors的计算方式是:取法线和切线 XYZ 向量的叉积,并将其乘以切线的 W 分量:
    bitangent = cross(normal.xyz, tangent.xyz) * tangent.w
primitives.mode —— default: 4 TRIANGLES

Allowed values:

0 POINTS

1 LINES

2 LINE_LOOP

3 LINE_STRIP

4 TRIANGLES

5 TRIANGLE_STRIP

6 TRIANGLE_FAN

primitives.targets (Morph Targets 变形目标,glTF 2.0)

教程参考,建议在学完动画animations后观看

  • 从版本 2.0 开始,glTF 支持定义网格的变形目标Morph Targets(可变形的网格,存储某些网格属性attributes的置换或差异),在运行时,这些差异可能会以不同的权重添加到原始网格中,以便为网格的各个部分制作动画。这通常用于角色动画中,例如,对虚拟角色的不同面部表情进行编码。
    例如,索引 i 处的基元的变形目标顶点 POSITION 是按以下方式计算的:
primitives[i].attributes.POSITION +weights[0] * primitives[i].targets[0].POSITION +weights[1] * primitives[i].targets[1].POSITION +weights[2] * primitives[i].targets[2].POSITION + ...
  • All primitives MUST have the same number of morph targets in the same order.
  • targets 支持至少三个属性 — POSITION、NORMAL 和 TANGENT — 用于变形。可以选择支持变形的 TEXCOORD_n 和/或 COLOR_n 属性。
  • targets.attribute 的有效访问器type 类型和component type组件类型:
    在这里插入图片描述
    在这里插入图片描述
  • POSITION 访问器必须定义其 min 和 max 属性
  • POSITION、NORMAL 和 TANGENT 属性的置换必须在影响网格顶点的任何变换矩阵 (如skinning 蒙皮或节点变换) 之前应用。
  • 当COLOR_n delta 使用“VEC3”类型的访问器时,必须假定其 alpha 分量的值为 0.0(注意与primitives.attributes的区别)。
  • 应用颜色增量后,每个 COLOR_0 变形访问器元素的所有组件都必须限制在 [0.0, 1.0] 范围内。
  • morph target accessors必须与the accessors of the original primitive具有相同的count 。
  • 具有targets 的mesh还可以定义一个可选的 mesh.weights 属性来存储默认targets 的权重。当 node.weights 定义时,必须使用这些权重。当 mesh.weights 未定义时,默认目标的权重为零。
  • 变形目标的数量没有限制。但是一般支持至少 8 个 morphed 属性,即一个target有一个attribute,则应支持8个target;一个target有两个attribute,则应支持4个target.
{"primitives": [{"attributes": {"NORMAL": 23,"POSITION": 22,"TANGENT": 24,"TEXCOORD_0": 25},"indices": 21,"material": 3,"targets": [{"NORMAL": 33,"POSITION": 32,"TANGENT": 34},{"NORMAL": 43,"POSITION": 42,"TANGENT": 44}]}],"weights": [0, 0.5]
}
例子:最小 glTF 文件
{"scene": 0,"scenes" : [{"nodes" : [ 0 ]}],"nodes" : [{"mesh" : 0}],"meshes" : [{"primitives" : [ {"attributes" : {"POSITION" : 1// 看accessor:1},"indices" : 0// 看accessor:0} ]}],"buffers" : [{"uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=","byteLength" : 44}],"bufferViews" : [{"buffer" : 0,"byteOffset" : 0,"byteLength" : 6,"target" : 34963// 顶点索引ELEMENT_ARRAY_BUFFER},{"buffer" : 0,"byteOffset" : 8,//字节对齐,因为6不是4的倍数!"byteLength" : 36,"target" : 34962// 顶点属性ARRAY_BUFFER}],"accessors" : [{"bufferView" : 0,"byteOffset" : 0,"componentType" : 5123,//UNSIGNED_SHORT,2byte"count" : 3,"type" : "SCALAR",//1"max" : [ 2 ],"min" : [ 0 ]},{"bufferView" : 1,"byteOffset" : 0,"componentType" : 5126,// float,4byte"count" : 3,"type" : "VEC3",//3"max" : [ 1.0, 1.0, 0.0 ],"min" : [ 0.0, 0.0, 0.0 ]}],"asset" : {"version" : "2.0"}
}

在这里插入图片描述

例子:Simple Meshes
{"scene": 0,"scenes" : [{"nodes" : [ 0, 1]}],"nodes" : [{"mesh" : 0},{"mesh" : 0,//网格被渲染了两次。通过将网格附加到两个不同的节点+节点下的变换来实现"translation" : [ 1.0, 0.0, 0.0 ]}],"meshes" : [{"primitives" : [ {"attributes" : {"POSITION" : 1,"NORMAL" : 2},"indices" : 0} ]}],"buffers" : [{"uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8=","byteLength" : 80}],"bufferViews" : [{"buffer" : 0,"byteOffset" : 0,"byteLength" : 6,"target" : 34963},{"buffer" : 0,"byteOffset" : 8,"byteLength" : 72,"target" : 34962}],"accessors" : [{"bufferView" : 0,"byteOffset" : 0,"componentType" : 5123,"count" : 3,"type" : "SCALAR","max" : [ 2 ],"min" : [ 0 ]},{"bufferView" : 1,"byteOffset" : 0,"componentType" : 5126,"count" : 3,"type" : "VEC3","max" : [ 1.0, 1.0, 0.0 ],"min" : [ 0.0, 0.0, 0.0 ]},{"bufferView" : 1,"byteOffset" : 36,"componentType" : 5126,"count" : 3,"type" : "VEC3","max" : [ 0.0, 0.0, 1.0 ],"min" : [ 0.0, 0.0, 1.0 ]}],"asset" : {"version" : "2.0"}
}

在这里插入图片描述

Skins(后补

glTF 支持顶点蒙皮vertex skinning,这允许根据骨架skeleton的姿势对网格的几何体(顶点)进行变形。
这对于为动画几何体(例如虚拟角色)提供逼真的外观至关重要。
1.0——Skins
教程——019_SimpleSkin
2.0——3.7.3. Skins
属性详解
辅助教程
在这里插入图片描述
在这里插入图片描述

Instantiation 实例化——同一网格可以由许多节点使用,这些节点可能具有不同的变换

Materials

材质定义为着色技术shading technique的实例以及参数化值,例如,光色、镜面反射度或光泽度。着色技术使用 JSON 属性来描述 GLSL 顶点和片段着色器程序的数据类型和语义
以下示例显示了具有环境光颜色、漫反射纹理、自发光颜色、光泽度和镜面反射颜色的 Blinn 着色器

"materials": {"blinn-1": {"technique": "technique1","values": {"ambient": [0,0,0,1],"diffuse": "texture_file2","emission": [0,0,0,1],"shininess": 38.4,"specular": [0,0,0,1]}"name": "blinn1"}
},

Texture

images

"image01": {"uri": "image01.png"// 通常指向PNG或JPG文件
}

http://www.ppmy.cn/news/1522310.html

相关文章

[论文笔记] LLM大模型剪枝篇——1、调研

Attention Is All You Need But You Don’t Need All Of It For Inference of Large Language Models LLaMA2在剪枝时,跳过ffn和跳过full layer的效果差不多。相比跳过ffn/full layer,跳过attention layer的影响会更小。 跳过attention layer:7B/13B从100%参数剪枝到66%,平…

【Linux】Linux常见指令以及权限理解(上)

【Linux】Linux常见指令以及权限理解 🥕个人主页:开敲🍉 🔥所属专栏:Linux🍊 🌼文章目录🌼 1. Linux背景 1.1 Linux发展史 1.1.1 Unix发展历史 1.1.2 Linux发展历史 1.2 开源 1.3 企…

PostgreSQL常用函数用法

在PostgreSQL中,函数是处理和操作数据的强大工具。以下是一些常用函数的用法示例。 1. 字符串函数 字符串函数用于操作和处理文本数据,常见操作包括字符串连接、截取、替换、转换大小写等。 LENGTH: 返回字符串的长度。-- 查询语句 SELECT LENGTH(Post…

【word导出带图片】使用docxtemplater导出word,通知书形式的word

一、demo-导出的的 二、代码操作 1、页面呈现 项目要求,所以页面和导出来的word模版一致 2、js代码【直接展示点击导出的js代码】 使用插件【先下载这五个插件,然后页面引入插件】 import docxtemplater from docxtemplater import PizZip from pizzip …

Linux基础入门 --8 DAY

文件权限管理 设置文件的所有者chown 格式: chown [OPTION]... [OWNER][:[GROUP]] FILE... chown [OPTION]... --referenceRFILE FILE... 示例: chown admin(所有者):admin(所属组)f1.txt cho…

Linux下构建Docker镜像

Docker在Linux构建镜像 Docker是一种轻量级的容器化技术,可以让开发者将应用程序及其所有依赖项打包到一个独立的容器中,从而实现跨平台和快速部署,在Linux系统上,我们可以使用D0cker来构建自己的镜像,并且可以通过简…

Win32函数调用约定(Calling Convention)

平常我们在C#中使用DllImportAttribute引入函数时,不指明函数调用约定(CallingConvention)这个参数,也可以正常调用。如FindWindow函数 [DllImport("user32.dll", EntryPoint"FindWindow", SetLastError true)] public static ext…

SpringBoot实现前后端传输加密设计

在Web应用中,确保前后端之间的数据传输安全是非常重要的。这通常涉及到使用HTTPS协议、数据加密、令牌验证等安全措施。本文通过将前后端之间的传输数据进行加密,用于在Spring Boot应用中实现前后端传输加密设计。 一、数据加密方案 即使使用了HTTPS&…

IP地址中的子网掩码

目录 一、子网掩码的概念 二、引入子网掩码的原因 1. 网络分段(Subnetting) 2. IP地址的组织 3. 有效利用IP地址 4. 减少广播域 5. 支持路由 三、子网掩码的划分 例子1 例子2 1. 子网掩码的二进制表示 2. 网络地址 3. 广播地址 4. 可用主机…

C++语法知识点合集:7.string类

文章目录 一、标准库中的string类1.string类2.auto和范围for3.string类的常用接口说明 二、string类的模拟实现1. 经典的string类问题2.浅拷贝3.深拷贝 一、标准库中的string类 1.string类 string是表示字符串的字符串类该类的接口与常规容器的接口基本相同,再添加…

【微处理器系统原理与应用设计第八讲】程序设计的开发框架包括编程语言、程序的基本要素、汇编程序结构、集成开发环境

一、编程语言 从处理器的角度看:一个指令只是一个操作,那么执行多条指令构成的程序就是完成一个完整功能的操作。 从程序执行的角度看:处理器读取指令后译码执行,完成所有操作。 从程序设计的角度看:用指令来描述所…

F12抓包06-3:浏览器导入/导出请求

使用场景:比如测试过程中通过抓包排查的问题,可以将请求记录导出给开发查看。 使用的是HAR文件,即HTTP归档格式(HTTP Archive Format),是用于记录HTTP会话信息的文件格式。 ① 抓包:鼠标右键打开“检查”工具&#xff…

远心镜头选型公式

在当今的机器视觉领域,远心镜头凭借其独特的远心光路设计以及超低畸变、高远心度和高景深等特点,成为尺寸测量和视觉对位中的得力工具。然而,如何进行快速而准确的选型呢?答案就在于选型公式:倍率 焦距 N.A.Sensor 尺…

Transiting from CUDA to HIP(三)

一、Workarounds 1. memcpyToSymbol 在 HIP (Heterogeneous-compute Interface for Portability) 中,hipMemcpyToSymbol 函数用于将数据从主机内存复制到设备上的全局内存或常量内存中,这样可以在设备端的内核中访问这些数据。这个功能特别有用&#x…

2024最新Redis面试题含答案

(3)、队列 Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操…

Shell教程_在Bash_Shell中使用for循环详解

Shell教程_在Bash_Shell中使用for循环详解 在编程语言中, 循环是必不可少的组件, 当您想要一遍又一遍地重复代码直到满足指定条件时使用。 在 Bash 脚本中, 循环扮演着几乎相同的角色, 并用于自动执行重复性任务, 就像在编程语言中一样。 在 Bash 脚本中, 有 3 种类型的循环…

Component name “Find“ should always be multi-word

出现原因 这个是vue的vue文件名校验,意思是你的组件文件名应为多个单词且大驼峰,这样语义性更强, 解决方法 可采用3种方法解决 如果解决不了记得重启vue工程 1.改文件名 比如FindIndex,如此大驼峰 2.给组件添加name属性,注…

坐牢第三十六天(QT)

自定义QQ界面 wedget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> //qt中信息调试类 #include <QIcon> //图标类 #include <QPushButton>//按钮类 #include <QLabel> //标签类 #include <QMovie> //动图类…

真人模特失业?AI虚拟试衣一键成图,IDM-VTON下载介绍

在电商行业竞争尤为激烈的当下&#xff0c;除了打价格战外&#xff0c;如何有效的控制成本&#xff0c;是每个从业者都在思考的问题 IDM-VTON是一个AI虚拟换装工具&#xff0c;旨在帮助服装商家解决约拍模特导致的高昂成本问题&#xff0c;只需一张服装图片&#xff0c;就可以…

手写 Vue Router 中的 Hash 模式和 History 模式

Vue Router 是 Vue.js 的官方路由库&#xff0c;负责管理 Vue应用中的页面导航。它与 Vue.js 核心深度集成&#xff0c;让用 Vue.js 构建单页应用变得轻而易举。Vue Router 提供两种常见的模式&#xff1a;Hash 模式和 History 模式。这两种模式的主要区别在于它们如何管理 URL…