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

文章目录

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

一、3DTiles

原文
工具+资料
格式详解
格式详解!

3D Tiles 是一种开源的、优化的文件格式,支持逐级细节(LOD)和空间索引,使得数据可以根据视点距离动态加载和卸载。

3DTiles由tileset.json和tile组成,其中tile可以是.b3dm、.i3dm、.pnts、.vctr和.cmpt中的任一种格式文件。

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

二、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一样的坐标系

所有线性距离的单位均为米。

所有角度均以弧度为单位。

正旋转是逆时针旋转。

3.glTF的索引访问与ID

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

  • 例子1:
cpp 复制代码
对象里面还可以接着通过索引/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:
cpp 复制代码
"meshes": {
    "FirstExampleMeshId": { ... },
    "SecondExampleMeshId": { ... },
    "ThirdExampleMeshId": { ... }
}

"nodes:" {
    "FirstExampleNodeId": {
        "meshes": ["FirstExampleMeshId"]
    },
    ...
}
或者
"nodes:" {
    "FirstExampleNodeId": {
        "meshes": 0,1....
    },
    ...
}

不用id直接用索引也行:

cpp 复制代码
"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 位于同一命名空间中,并且是唯一的。

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

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

例如,允许执行以下操作:

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

4.glTF asset

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

cpp 复制代码
"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.

scene.nodes
  • scene.nodes 数组中列出的所有节点都必须是根节点,即,它们不得在任何节点的 node.children 数组中列出。
  • The same root node MAY appear in multiple scenes。
  • 每个元素都必须是唯一的。
cpp 复制代码
"scene": "defaultScene",
"scenes": {
    "defaultScene": {
        "nodes": [
            "node_1"
        ]
    }
},
cpp 复制代码
 "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
cpp 复制代码
children :此节点的子节点的索引,非必须。这些children 节点中的每一个都可以有自己的子节点。
"nodes": [
        {
            "name": "Car",
            "children": [1, 2, 3, 4]
        },
        {
            "name": "wheel_1"
        },
        {
            "name": "wheel_2"
        },
        {
            "name": "wheel_3"
        },
        {
            "name": "wheel_4"
        }
    ]
cpp 复制代码
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都可以有一个变换,此转换将应用于附加到节点本身及其所有子节点的所有元素:
cpp 复制代码
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节点的属性:

cpp 复制代码
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(小端字节顺序)。
cpp 复制代码
    "buffers": {
        "duck": {
            "byteLength": 102040,//缓冲区文件大小,必须大于等于该缓冲区
            "type": "arraybuffer",//数据的存储方式,二进制数组缓冲区还是文本
            "uri": "duck.bin"//指向外部文件,例如binary geometry, animation, or skins
        }
    },
或
 "buffers": [
       {
           "byteLength": 102040,
           "uri": "duck.bin"
       }
   ]
  • 数据URI

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

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

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

此切片使用偏移量byteOffset和长度byteLength(以字节为单位)定义。

cpp 复制代码
"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,且为必需。
例子
cpp 复制代码
对应上文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 必须在每列的末尾插入填充字节。


对齐要求仅适用于每列的开头,因此如果没有更多数据,则可以省略尾随字节

cpp 复制代码
"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字节数。

cpp 复制代码
"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
cpp 复制代码
{
  "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 属性:

cpp 复制代码
 "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字节,12 14 = 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个需要替换的新顶点坐标位置。

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。
  • Line Loops:pi = {vi, vi+1};顶点索引的数量MUST be 2 or greater。
  • 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 是按以下方式计算的:
cpp 复制代码
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.
cpp 复制代码
{
    "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 文件
cpp 复制代码
{
  "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
cpp 复制代码
{
  "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 着色器

cpp 复制代码
"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

cpp 复制代码
"image01": {
    "uri": "image01.png"// 通常指向PNG或JPG文件
}
相关推荐
starsongda11 小时前
VR科技展厅重塑科技展示新风貌,引领未来展示潮流
科技·3d·vr
兔老大的胡萝卜12 小时前
threejs 数字孪生,制作3d炫酷网页
前端·3d
CV-X.WANG15 小时前
【详细 工程向】基于Smart3D的五镜头相机三维重建
数码相机·3d
JoeyKo1 天前
国内版Sketchfab平台 - CG美术之家(3D编辑发布篇)
3d·3d建模·3dsmax·3d渲染·模型·3d模型·cg模型
mirrornan1 天前
3D互动+AR试戴,重塑线上珠宝营销新体验!
3d·ar
DisonTangor2 天前
腾讯混元3D-1.0:文本到三维和图像到三维生成的统一框架
人工智能·3d·aigc
坠金3 天前
【3D】基础概念
3d
小白菜学前端3 天前
ThreeJS创建一个3D物体的基本流程
3d·three.js
梦想的理由3 天前
3D人体建模的前沿探索:细数主流模型与技术进展
3d
吃个糖糖3 天前
Halcon 3D表面匹配
3d