glTF格式介绍(8)——解析一个Box的glTF文件

glTF格式介绍总目录:https://blog.youkuaiyun.com/qq_31709249/article/details/86477520

在以前的系列文章中,已经介绍了glTF文件的文件结构,而且拿了一个最简单的例子——三角形的glTF文件简单解析了一下。这一节来解析一个稍微复杂一点的glTF文件——正方形模型。正方形模型来源于glTF官方示例,里面有很多glTF格式的模型,下载下来可以用win10自带的3D模型查看器查看。(注意,里面模型有1.0版本的和2.0版本的,但是目前主流的软件都支持2.0版本的,如Cesium、Win10自带的3D模型查看器和编辑器等,包括前面的一系列教程都是介绍2.0版本的,所以下载模型的时候,下载2.0版本的)。

下载文件后,可以看到glTF文件夹下有两个文件:Box.gltfBox0.bin。根据前面文件结构的知识可以知道,Box.gltf是一个json文件,描述3D模型如何组织;Box0.bin则是二进制数据文件,里面包含顶点坐标、纹理坐标等数据。用VSCode打开Box.gltf文件,可以看到里面的内容如下:

{
    "asset": {
        "generator": "COLLADA2GLTF",
        "version": "2.0"
    },
    "scene": 0,
    "scenes": [
        {
            "nodes": [
                0
            ]
        }
    ],
    "nodes": [
        {
            "children": [
                1
            ],
            "matrix": [
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                1.0,
                0.0,
                0.0,
                0.0,
                0.0,
                0.0,
                1.0
            ]
        },
        {
            "mesh": 0
        }
    ],
    "meshes": [
        {
            "primitives": [
                {
                    "attributes": {
                        "NORMAL": 1,
                        "POSITION": 2
                    },
                    "indices": 0,
                    "mode": 4,
                    "material": 0
                }
            ],
            "name": "Mesh"
        }
    ],
    "accessors": [
        {
            "bufferView": 0,
            "byteOffset": 0,
            "componentType": 5123,
            "count": 36,
            "max": [
                23
            ],
            "min": [
                0
            ],
            "type": "SCALAR"
        },
        {
            "bufferView": 1,
            "byteOffset": 0,
            "componentType": 5126,
            "count": 24,
            "max": [
                1.0,
                1.0,
                1.0
            ],
            "min": [
                -1.0,
                -1.0,
                -1.0
            ],
            "type": "VEC3"
        },
        {
            "bufferView": 1,
            "byteOffset": 288,
            "componentType": 5126,
            "count": 24,
            "max": [
                0.5,
                0.5,
                0.5
            ],
            "min": [
                -0.5,
                -0.5,
                -0.5
            ],
            "type": "VEC3"
        }
    ],
    "materials": [
        {
            "pbrMetallicRoughness": {
                "baseColorFactor": [
                    0.800000011920929,
                    0.0,
                    0.0,
                    1.0
                ],
                "metallicFactor": 0.0
            },
            "name": "Red"
        }
    ],
    "bufferViews": [
        {
            "buffer": 0,
            "byteOffset": 576,
            "byteLength": 72,
            "target": 34963
        },
        {
            "buffer": 0,
            "byteOffset": 0,
            "byteLength": 576,
            "byteStride": 12,
            "target": 34962
        }
    ],
    "buffers": [
        {
            "byteLength": 648,
            "uri": "Box0.bin"
        }
    ]
}

然后逐步解析里面的部分。

首先是scene,根据前面的教程可知,scene是整个场景的入口,可以看到里面只有一个场景0。然后再看scenes,里面有一个nodes数组,说明scene的根节点只有一个。再看nodes数组,前面说过,nodes数组是整个场景结构的组织者,可以看到里面有一个children数组,里面有一个元素值为1,说明对应nodes数组中下标为1(第二个)node。第二个node为:

        {
            "mesh": 0
        }

说明该node对应的mesh为meshes数组中下标为0(第一个)的元素。再看meshes数组,根据meshes教程可知,该meshes中只有一个图元(primitives),里面的属性有NORMAL(法向量),且法向量对应的Accessor下标为1(NORMAL值为1)、POSITION(顶点位置坐标),且位置坐标对应的Accessor下标为2(POSITION值为2),此图元是用索引的方式绘制的(有Indices属性),绘制方式为TRIANGLESmode值为4),对应的材质下标为0。

接着解析数据部分,先看Buffer

前面教程

通过buffers属性可知,只有一个buffer,并且对应的文件为"Box0.bin",文件字节长度为648字节。

再看BufferView

 "bufferViews": [
        {
            "buffer": 0,
            "byteOffset": 576,
            "byteLength": 72,
            "target": 34963
        },
        {
            "buffer": 0,
            "byteOffset": 0,
            "byteLength": 576,
            "byteStride": 12,
            "target": 34962
        }
    ]

通过bufferViews属性可知,buffer[0]有两个分块,第一个分块为对应的字节范围为576——572+72=648,对应的数据是ELEMENT_ARRAY_BUFFER(target值为34963,详情见相关教程),第一个分块对应的字节范围为0——576.对应的数据类型为ARRAY_BUFFER(target值为34962,详情见相关教程),需要注意的是,有一个属性byteStride属性 值为12,根据前面教程中数据交错的知识可知,第一个属性和第二个属性之间的间距为12字节。

继续看Accessor

"accessors": [
        {
            "bufferView": 0,
            "byteOffset": 0,
            "componentType": 5123,
            "count": 36,
            "max": [
                23
            ],
            "min": [
                0
            ],
            "type": "SCALAR"
        },
        {
            "bufferView": 1,
            "byteOffset": 0,
            "componentType": 5126,
            "count": 24,
            "max": [
                1.0,
                1.0,
                1.0
            ],
            "min": [
                -1.0,
                -1.0,
                -1.0
            ],
            "type": "VEC3"
        },
        {
            "bufferView": 1,
            "byteOffset": 288,
            "componentType": 5126,
            "count": 24,
            "max": [
                0.5,
                0.5,
                0.5
            ],
            "min": [
                -0.5,
                -0.5,
                -0.5
            ],
            "type": "VEC3"
        }
    ],

通过accessors属性可知,有三个accessor。我们知道索引对应的是第一个accessor,先看第一个accessori,根据属性我们可知,索引数组对应第一个bufferView,在bufferView中起点为0,数据类型为unsigned int(2个字节),一共有36个值。同理可知,法向量对应第二个bufferView,在bufferView中起点为0,数据类型为vec3,一共有24个值;位置坐标对应第二个bufferView,在bufferView中起点为288,数据类型为vec3,一共有24个值。

ok,知道了这些,我们就可以写程序来读取bin文件中的值,来看看里面的数据长啥样。读取程序的代码如下:

#include "stdafx.h"
#include <fstream>
#include <vector>
#include <iostream>
using namespace std;

int main()
{

	const int n = 48;

	std::ifstream f = ifstream("Box0.bin", std::ios::binary);

	for (int i = 0; i < n; i++)
	{
		float a, b, c;
		f.read((char*)&a, sizeof(float));
		f.read((char*)&b, sizeof(float));
		f.read((char*)&c, sizeof(float));
		std::cout <<"	("<< a << ", " << b << ", " << c <<")"<< std::endl;
	}
	for (int i = 0; i < 36; i++)
	{
		short elements;
		f.read((char*)&elements, sizeof(short));
		cout <<"	"<< elements <<",";
		if ((i + 1) % 4 == 0)
			cout << endl;
	}
	return 0;
}

最终读取的结果如下:

我们可以看到,前面24个为法向量,中间24个为位置坐标,最后的36个为索引坐标。和我们理解的一致。

最后的用OpenGL或者OSG,将这个正方体绘制出来,我用的是OSG,最后结果如下:

OK,大功告成。

 

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MeteorChenBo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值