如何手动读取 GLTF/GLB 文件

推荐:使用NSDT场景编辑器快速搭建3D应用场景

文件类型

GLTF文件有两种不同的主要文件类型:.gltf和.glb。

GLTF文件本质上只是一个重新命名的json文件,它们通常与包含顶点数据等内容的.bin文件相提并论,但这些内容也可以直接包含在json中。

GLB 文件类似于 GLTF 文件,但所有内容都包含在同一个文件中。它分为三个部分,一个小标头、json 字符串和二进制缓冲区。

图片来自官方gltf github

GLTF 格式

在 GLTF 中,与网格、动画和蒙皮相关的一切都存储在缓冲区中,虽然一开始,在没有库的情况下从原始二进制文件中读取似乎令人生畏,但实际上并不太难。我们将逐步进行。

在本文中,我们将介绍如何从 .gltf 和 .glb 文件中从单个网格读取顶点位置数据。

在我们获得实际代码之前,我们需要了解如何使用文件的 json 部分来查找我们想要的内容,因为我们必须跳来跳去才能找到任何东西。你可以从场景级别开始,如果需要,也可以逐步向下工作,但由于我计划只对单个网格使用格式,所以我将从图形的网格节点开始。

假设我们的 GLTF 文件看起来像这样(请注意,实际文件将包含更多数据):

{"accessors" : [{"bufferView": 0,"byteOffset": 0,"componentType": 5126,"count": 197,"max": [ -0.004780198, 0.0003038254, 0.007360002 ],"min": [ -0.008092392, -0.008303153, -0.007400591 ],"type": "VEC3"}],"buffers": [{"byteLength": 2460034,"uri": "example.bin"}],"bufferViews": [{"buffer": 0,"byteLength": 306642,"target": 34963,"byteOffset": 2153392},],"meshes": [{"name": "example mesh","primitives": [{"attributes": {"POSITION": 0,"NORMAL": 1,"TEXCOORD_0": 2,"TANGENT": 3},"indices": 4,"material": 0,"mode": 4}]}]
}

要查找网格的位置数据,我们首先需要访问索引 0 处的 “meshes” 键,然后访问第一个基元。(据我所知,基元本质上只是子网格。然后我们将检索“属性”->“位置”。这将为我们提供访问器的索引。插入它,我们可以从第一个访问器获取“bufferView”值。然后,这为我们提供了缓冲区视图的索引,我们最终可以使用它来获取缓冲区以从中检索数据。在这种情况下,缓冲区存储在外部文件“example.bin”中。打开该文件后,我们将转到访问器中“byteOffset”提供给我们的位置,最后读取缓冲区数据。

下面开始分别介绍如何从gltf/glb文件中读取数据 ,在此过程中你可以用GLTF编辑器对3D模型文件进行编辑和验证模型数据。

从 GLTF 文件读取

我将在我的示例代码中使用 c++ ,但对于任何其他语言,步骤应该大致相同。

// First define our filname, would probbably be better to prompt the user for one
const std::string& gltfFilename = "example.gltf"// open the gltf file
std::ifstream jsonFile(gltfFilename, std::ios::binary);// parse the json so we can use it later
Json::Value json;try{jsonFile >> json;
}catch(const std::exception& e){std::cerr << "Json parsing error: " << e.what() << std::endl;
}
jsonFile.close();// Extract the name of the bin file, for the sake of simplicity I'm assuming there's only one
std::string binFilename = json["buffers"][0]["uri"].asString();// Open it with the cursor at the end of the file so we can determine it's size,
// We could techincally read the filesize from the gltf file, but I trust the file itself more
std::ifstream binFile = std::ifstream(binFilename, std::ios::binary | std::ios::ate);// Read file length and then reset cursor
size_t binLength = binFile.tellg();
binFile.seekg(0);std::vector<char> bin(binLength);
binFile.read(bin.data(), binLength);
binFile.close();// Now that we have the files read out, let's actually do something with them
// This code prints out all the vertex positions for the first primitive// Get the primitve we want to print out: 
Json::Value& primitive = json["meshes"][0]["primitives"][0];// Get the accessor for position: 
Json::Value& positionAccessor = json["accessors"][primitive["attributes"]["POSITION"].asInt()];// Get the bufferView 
Json::Value& bufferView = json["bufferViews"][positionAccessor["bufferView"].asInt()];// Now get the start of the float3 array by adding the bufferView byte offset to the bin pointer
// It's a little sketchy to cast to a raw float array, but hey, it works.
float* buffer = (float*)(bin.data() + bufferView["byteOffset"].asInt());// Print out all the vertex positions 
for (int i = 0; i < positionAccessor["count"].asInt(); ++i)
{std::cout << "(" << buffer[i*3] << ", " << buffer[i*3 + 1] << ", " << buffer[i*3 + 2] << ")" << std::endl;
}// And as a cherry on top, let's print out the total number of verticies
std::cout << "vertices: " << positionAccessor["count"].asInt() << std::endl;

从 GLB 文件读取:

从 .glb 文件中读取有点困难,因为我们不能只是将其放入 JSON 解析器中,但它是可行的。在文件类型部分中参考上图,我们可以找到有关所需文件格式的所有信息:

std::ifstream binFile = std::ifstream(glbFilename, std::ios::binary); binFile.seekg(12); //Skip past the 12 byte header, to the json header
uint32_t jsonLength;
binFile.read((char*)&jsonLength, sizeof(uint32_t)); //Read the length of the json file from it's headerstd::string jsonStr;
jsonStr.resize(jsonLength);
binFile.seekg(20); // Skip the rest of the JSON header to the start of the string
binFile.read(jsonStr.data(), jsonLength); // Read out the json string// Parse the json
Json::Reader reader;
if(!reader.parse(jsonStr, _json))std::cerr << "Problem parsing assetData: " << jsonStr << std::endl;// After reading from the json, the file cusor will automatically be at the start of the binary headeruint32_t binLength;
binFile.read((char*)&binLength, sizeof(binLength)); // Read out the bin length from it's header
binFile.seekg(sizeof(uint32_t), std::ios_base::cur); // skip chunk typestd::vector<char> bin(binLength);
binFile.read(bin.data(), binLength);//Now you're free to use the data the same way we did above

总结

希望这对您有所帮助。我知道它在某些方面有点缺乏细节,所以一旦我了解更多,我可能会回来更新它,提供更多关于动画和皮肤的信息。但在那之前,再见。

原文链接:了解 glTF 2.0 格式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70235.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式实时仿真系统-反射内存的应用

为了使分布式实时仿真系统(一个典型代表就行飞行模拟器)达到逼真的仿真效果&#xff0c;在系统内部&#xff0c;往往不仅需要对各种数据模型进行实时解算&#xff0c;而且需要一个延迟时间极低的确定性网络在系统之间传递数据&#xff0c;这样才能让各个子系统之间协调一致地工…

问道管理:分时高抛低吸策略?

分时高抛低吸是股市中的一种买卖战略&#xff0c;也是投资者经常运用的一种方法。这种战略经过剖析图表、股价和时刻&#xff0c;尽可能减少危险&#xff0c;添加收益。下面从多个视点对十二种分时高抛低吸进行剖析。 视点一&#xff1a;什么是分时高抛低吸&#xff1f; 分时高…

报错:为什么数组明明有内容但打印的length是0

文章目录 一、问题二、分析三、解决1.将异步改为同步2.设置延迟 一、问题 在日常开发中&#xff0c;for 循环遍历调用接口&#xff0c;并将接口返回的值进行拼接&#xff0c;即push到一个新的数组中&#xff0c;但是在for循环内部是可以拿到这个新的数组&#xff0c;而for循环…

人工智能的优势:使用 GPT 和扩散模型生成图像

推荐&#xff1a;使用 NSDT场景编辑器快速搭建3D应用场景 世界被人工智能 &#xff08;AI&#xff09; 所吸引&#xff0c;尤其是自然语言处理 &#xff08;NLP&#xff09; 和生成 AI 的最新进展&#xff0c;这是有充分理由的。这些突破性技术有可能提高各种任务的日常生产力。…

Unity的GPUSkinning进一步介绍

大家好&#xff0c;我是阿赵。   在几年前&#xff0c;我曾经写过一篇介绍GPUSkinning的文章&#xff0c;这么多年之后&#xff0c;还是看到不停有朋友在翻看这篇旧文章。今天上去GitHub看了一下&#xff0c;GPUSkinning这个开源的插件已经很久没有更新过了&#xff0c;还是停…

云原生Kubernetes:kubectl管理命令

目录 一、理论 1.kubectl 管理命令 2.项目的生命周期 二、实验 1.kubectl 管理命令 2.项目的生命周期 三、总结 一、理论 1.kubectl 管理命令 &#xff08;1&#xff09;陈述式资源管理方法 kubernetes集群管理集群资源的唯一入口是通过相应的方法调用apiserver的接口…

复旦-华盛顿EMBA:AI时代掘金,科技进化里的挑战与机遇

如果从去年年底ChatGPT3.5发布算起&#xff0c;AI赛道的热度已经持续飙升了半年有余。      “AI的iPhone时刻”代表什么&#xff1f;AI驱动的商业时代已经到来&#xff1f;      我们能看到担忧、恐惧、憧憬&#xff0c;但唯独不缺狂飙突进、加速进化。人类制造AI&…

WordPress(4)关于网站的背景图片更换

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、更改的位置1. 红色区域是要更换的随机的图片二、替换图片位置三.开启随机数量四.结束前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也…

Hadoop的分布式文件存储系统HDFS组件的使用

Hadoop的第一个核心组件&#xff1a;HDFS&#xff08;分布式文件存储系统&#xff09; 一、HDFS的组成1、NameNode2、DataNode3、SecondaryNameNode4、客户端&#xff1a;命令行/Java API 二、HDFS的基本使用1、命令行操作2、Java API操作 三、HDFS的工作流程问题&#xff08;H…

Direct3D颜色

在Direct3D中颜色用RGB三元组来表示&#xff0c;RGB数据可用俩种不同的结构来保存&#xff0c;第一种是D3DCOLOR&#xff0c;它实际上与DWORD类型完全相同&#xff0c;共有32位&#xff0c;D3DCOLOR类型种的各位被分成四个8位项&#xff0c;每项存储了一种颜色分量的亮度值。 由…

【Hive SQL 每日一题】统计用户连续下单的日期区间

文章目录 测试数据需求说明需求实现 测试数据 create table test(user_id string,order_date string);INSERT INTO test(user_id, order_date) VALUES(101, 2021-09-21),(101, 2021-09-22),(101, 2021-09-23),(101, 2021-09-27),(101, 2021-09-28),(101, 2021-09-29),(101, 20…

C语言sizeof()计算空间大小为8的问题

在练习数据结构过程中&#xff0c;定义指针p&#xff0c;并且申请了10个char类型空间&#xff0c;但在计算p所指空间大小时候&#xff0c;发现了一些奇怪的现象。 #include <stdio.h> #include <stdlib.h>int main(){char s[12];printf("the size of memory …

Java反射:探索对象创建与类信息获取

文章目录 1. 对象的创建2. 类的初始化2.1 类的加载2.2 类的连接2.3 类的初始化 3. 反射是什么&#xff1f;4. 获取Class类对象4.1 使用类名.class4.2 使用对象的getClass()方法4.3 使用Class.forName() 5. 获取构造器对象5.1 使用getConstructors()和getDeclaredConstructors()…

pytorch代码实现之空间通道重组卷积SCConv

空间通道重组卷积SCConv 空间通道重组卷积SCConv&#xff0c;全称Spatial and Channel Reconstruction Convolution&#xff0c;CPR2023年提出&#xff0c;可以即插即用&#xff0c;能够在减少参数的同时提升性能的模块。其核心思想是希望能够实现减少特征冗余从而提高算法的效…

【探索Linux】—— 强大的命令行工具 P.8(进程优先级、环境变量)

阅读导航 前言一、进程优先级1. 优先级概念2. Linux查看系统进程3. PRI&#xff08;Priority&#xff09;和NI&#xff08;Nice&#xff09; 二、环境变量1. 概念2. 查看环境变量方法3. 环境变量的组织方式4.通过代码获取环境变量5. 环境变量的特点 总结温馨提示 前言 前面我们…

C++ - 多态的实现原理

前言 本博客主要介绍C 当中 多态语法的实现原理&#xff0c;如果有对 多态语法 有疑问的&#xff0c;请看下面这篇博客&#xff1a; 探究&#xff0c;为什么多态的条件是那样的&#xff08;虚函数表&#xff09; 首先&#xff0c;调用虚函数必须是 父类的 指针或 引用&#xf…

KT142C-sop16语音芯片ic的功能介绍 支持pwm和dac输出 usb直接更新内置空间

1.1 简介 KT142C是一个提供串口的SOP16语音芯片&#xff0c;完美的集成了MP3的硬解码。内置330KByte的空间&#xff0c;最大支持330秒的语音长度&#xff0c;支持多段语音&#xff0c;支持直驱0.5W的扬声器无需外置功放 软件支持串口通信协议&#xff0c;默认波特率9600.同时…

opencv旋转图像

0 、使用旋转矩阵旋转 import cv2img cv2.imread(img.jpg, 1) (h, w) img.shape[:2] # 获取图像的宽和高# 定义旋转中心坐标 center (w / 2, h / 2)# 定义旋转角度 angle 90# 定义缩放比例 scale 1# 获得旋转矩阵 M cv2.getRotationMatrix2D(center, angle, scale)# 进行…

比亚迪海豹:特斯拉强劲对手,瑞银拆解成本比同级车型低15%~35%

瑞银证券日前对中国电动车产品比亚迪海豹进行了拆解&#xff0c;发现海豹具有强大的成本优势&#xff0c;而这个优势主要来自于中国本土生产和国内完善的电动车供应链以及比亚迪的垂直整合体系和零部件高度集成性。比亚迪的整车成本比同级别竞争车型分别低15%至35%。 瑞银预测&…

【100天精通Python】Day55:Python 数据分析_Pandas数据选取和常用操作

目录 Pandas数据选择和操作 1 选择列和行 2 过滤数据 3 添加、删除和修改数据 4 数据排序 Pandas数据选择和操作 Pandas是一个Python库&#xff0c;用于数据分析和操作&#xff0c;提供了丰富的功能来选择、过滤、添加、删除和修改数据。 1 选择列和行 Pandas 提供了多种…