GLTF(GL Transmission Format)是一种专为高效传输和渲染 3D 模型设计的文件格式。今天,我们将以一次深入解析 GLTF 文件的过程,带你全面了解它的结构、关联方式以及使用方式。
一、初识 GLTF 文件
在开始我的介绍之前,我先放一张官方的图片,上面使用图形化的方式描述了gltf的格式规范(英文的),各位看官可以在官方网站上找到这张图:
GLTF 文件通常以 .gltf
或 .glb
作为后缀名,其中 .gltf
是 JSON 格式文件,可能会附带二进制文件(.bin
)和图片素材;而 .glb
则是将所有数据整合成一个二进制文件。
解析的第一步是加载 JSON 文件,我们以 Node.js 为例:
const fs = require('fs');
const gltfPath = './model.gltf';
const gltfData = JSON.parse(fs.readFileSync(gltfPath, 'utf-8'));
console.log('GLTF 文件内容:', gltfData);
读取后的文件内容是一个包含多个字段的 JSON 结构:asset
描述文件信息,scenes
定义场景结构,nodes
是场景中具体的节点等。
二、核心数据结构与关联
1. scenes
和 nodes
scenes
是 GLTF 的入口,每个场景会引用若干节点(nodes
)。
"scenes": [
{
"nodes": [0]
}
],
"nodes": [
{
"name": "RootNode",
"children": [1, 2]
},
{
"mesh": 0
},
{
"mesh": 1
}
]
关联关系图:
Scene (0) --> Node (0: RootNode)
|--> Node (1: Mesh 0)
|--> Node (2: Mesh 1)
在代码中,我们可以递归解析场景和节点:
function traverseNodes(nodes, currentNodeIndex) {
const node = nodes[currentNodeIndex];
console.log('Node:', node.name);
if (node.children) {
node.children.forEach(childIndex => traverseNodes(nodes, childIndex));
}
}
const nodes = gltfData.nodes;
gltfData.scenes[0].nodes.forEach(nodeIndex => traverseNodes(nodes, nodeIndex));
2. meshes
和 primitives
meshes
定义几何数据,它由一个或多个 primitives
构成,而 primitives
包含顶点属性和材质信息。
"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 0,
"NORMAL": 1
},
"indices": 2,
"material": 0
}
]
}
]
-
POSITION
: 定义顶点位置。 -
NORMAL
: 定义法线方向。 -
indices
: 定义三角形索引。
关联关系图:
Mesh --> Primitive --> Attributes (POSITION, NORMAL, ...) --> Accessors --> BufferViews --> Buffers
3. buffers
和 bufferViews
顶点、索引等数据存储在二进制文件中,通过 buffers
字段引用。
"buffers": [
{
"uri": "model.bin",
"byteLength": 1024
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 512,
"target": 34963
}
]
关联示意图:
Buffer (model.bin) --> BufferView (byteOffset, byteLength)
通过 Node.js 加载并解析 buffer 数据:
const bufferData = fs.readFileSync('./model.bin');
const bufferView = gltfData.bufferViews[0];
const extractedData = bufferData.slice(
bufferView.byteOffset,
bufferView.byteOffset + bufferView.byteLength
);
console.log('提取的二进制数据:', extractedData);
4. materials
和 textures
materials
定义了模型的材质属性,包括金属度、粗糙度等,而 textures
则关联了图片数据。
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
}
}
}
],
"textures": [
{
"source": 0
}
],
"images": [
{
"uri": "texture.png"
}
]
关联流程:
Material --> Texture --> Image (texture.png)
三、构建图形化示意图
以下是关联关系的总体结构图:
-
顶层结构:场景与节点
Scene --> Nodes --> Meshes
-
几何数据路径
Mesh --> Primitive --> Attributes --> Accessors --> BufferViews --> Buffers
-
材质与纹理路径
Material --> Texture --> Image
四、完整解析示例
将以上模块结合起来,我们可以解析完整的 GLTF 模型。
function parseGLTF(gltfPath, binPath) {
const gltfData = JSON.parse(fs.readFileSync(gltfPath, 'utf-8'));
const bufferData = fs.readFileSync(binPath);
gltfData.scenes.forEach(scene => {
console.log('Scene:', scene);
scene.nodes.forEach(nodeIndex => {
traverseNodes(gltfData.nodes, nodeIndex);
});
});
gltfData.meshes.forEach(mesh => {
mesh.primitives.forEach(primitive => {
const positionAccessor = gltfData.accessors[primitive.attributes.POSITION];
const positionBufferView = gltfData.bufferViews[positionAccessor.bufferView];
const positionData = bufferData.slice(
positionBufferView.byteOffset,
positionBufferView.byteOffset + positionBufferView.byteLength
);
console.log('顶点位置数据:', positionData);
});
});
}
parseGLTF('./model.gltf', './model.bin');
通过上述代码,我们完成了从场景到几何数据、再到材质与纹理的完整解析。
五、在线预览GLTF 3D文件
-
NSDT gltfviewer gltf glb文件的在线预览
-
NSDT 3Dconvert是一个可以进行3D模型格式转换的在线工具,支持多种3D模型格式在线预览和格式转换,支持将模型转换为GLTF、OBJ、GLB、PLY、STL、XYZ、OFF、DAE等格式。 NSDT 3Dconvert支持GLB、GLTF、PLY、STL、OBJ、OFF、DAE、FBX、DXF、IFC、XYZ、PCD、 LAS、LAZ、STP、STEP、3DXML、IGES、IGS、SHP、GEOJSON、XAML、PTS、ASC、 BREP、FCSTD、BIM、USDZ和PDB等源格式。 NSDT 3Dconvert提供Revit、MicroStation等设计软件插件,通过插件将RVT、RFA、DGN等设计文件进行在线预览和格式转换。
结语
GLTF 文件格式通过其模块化设计,提供了一种高效传输和渲染 3D 模型的方式。从场景节点到几何数据、从材质到纹理,每个部分都有清晰的关联路径。这种结构化的格式不仅易于解析,也为高性能渲染提供了保障。如果你还没有尝试过 GLTF 文件,不妨用本文的示例从零开始解析一次!
标签:文件,gltfData,const,--,从零开始,文件格式,nodes,GLTF From: https://blog.csdn.net/weixin_35760413/article/details/144830815