- 示例工程
- 图形学
【免费下载链接】Vulkan
C++ examples for the Vulkan graphics API
本文以仓库内 external/tinygltf/README.md 为核心,结合该库在 Vulkan 示例工程中的真实集成代码,系统讲解 TinyGLTF 的定位、特性、编译选项、加载/保存流程与测试方法。读完本文,你将掌握:如何在任意 C++11 工程中仅靠拷贝头文件引入 TinyGLTF,如何加载 ASCII glTF(.gltf)与二进制 GLB(.glb)场景、处理内嵌 Base64/DataURI 数据与外部资源,如何通过自定义回调接管图像解码与文件系统访问,以及如何像本仓库的 Vulkan 示例一样把 glTF 场景无缝接入 GPU 渲染管线。
TinyGLTF 是什么:项目定位与技术形态
TinyGLTF 是一个Header-Only(仅头文件)的 C++11 glTF 2.0 加载器与序列化器。它不对应某个图形 API,而是负责把符合 Khronos glTF 2.0 规范的.gltf/.glb文件解析成可供上层渲染引擎直接使用的内存结构(tinygltf::Model)。
- 技术形态:全部实现集中在单个头文件 external/tinygltf/tiny_gltf.h(约 7600 行),并在仓库内随附三个运行时依赖头文件:external/tinygltf/json.hpp、external/tinygltf/stb_image.h(stb_image_write.h 仅在某些编译选项下需要)。
- 编译依赖:依赖 Niels Lohmann 的
json.hpp(即 nlohmann/json)解析 JSON,因此要求C++11 编译器;历史上曾存在面向 C++03 的devel-picojson分支,但当前主线以 C++11 为基线。 - 标准遵循:完整实现 glTF 规范 v2.0.0,同时覆盖 ASCII glTF 与二进制 GLB 两种容器格式的加载与保存。
在 README.md 的自述中,官方明确列出其跨平台验证矩阵:macOS + clang(LLVM)、iOS + clang、Linux + gcc/clang、Windows + MinGW、Windows + Visual Studio 2015 Update 3 及以上(VS2013 因 C++11 支持不完整无法编译json.hpp而不被支持)、Android NDK、Android + CrystaX(NDK 替代品) GCC、Web(Emscripten/LLVM)。从源码结构看,tiny_gltf.h 还为 Android(__ANDROID__)与 OpenHarmony(__OHOS__)预置了平台资源加载的接入点,这与本仓库同时提供android/与openharmony/平台工程的事实相互印证。
版本演进与状态
README 与头文件内的版本注释共同勾勒了 TinyGLTF 的能力演进路线(以仓库内头文件 tiny_gltf.h 注释为准,其版本号已迭代至 v2.4.2):
| 版本 | 关键变更 |
|---|---|
| v2.0.0(2018-08-22) | 正式切换到 glTF 2.0 |
| v2.0.1 | 增加比较(comparison)特性 |
| v2.1.0 | 增加 Draco 网格压缩加载支持 |
| v2.2.0 | 支持 16bit PNG 加载;支持 Sparse accessor(稀疏访问器) |
| v2.3.0 | 依据 glTF 2.0 schema 重构 Material 表示,引入TextureInfo类;Value::IsNumber()语义变化 |
| v2.3.1 | Sampler中minFilter/magFilter默认值改为 -1 |
| v2.4.0 | 实验性 RapidJSON 支持;实验性 C++14 支持(可能带来更好性能) |
| v2.4.1 | 修复部分 glTF 对象类缺少extensions/extras属性的问题 |
| v2.4.2 | 解码百分号编码的 URI |
功能特性总览
README 的功能清单可以归纳为以下五个能力维度,每一条都能在 tiny_gltf.h 的公开接口与结构体定义中得到印证:
- 容器格式:ASCII glTF(加载 ✅ / 保存 ✅)、Binary GLB(加载 ✅ / 保存 ✅,支持将 .bin 内嵌为单一 .glb)。
- 缓冲区(Buffers):解析 Base64 编码的内嵌缓冲区数据(DataURI);加载外部
.bin文件;保存时可写入文件或内嵌。 - 图像(Images,基于 stb_image):解析 Base64 内嵌图像 DataURI;加载外部图像文件;支持 PNG(8bit 与 16bit)、JPEG(仅 8bit)、BMP、GIF;提供自定义图像解码回调(例如用于解码 OpenEXR 等 stb_image 不支持的格式)。
- 几何数据:支持 Morph target;支持 Sparse accessor(稀疏访问器,用于高效表达只有部分元素被修改的顶点数据,见 tiny_gltf.h 中 v2.2.0 变更记录)。
- 扩展与可定制性:支持从内存直接加载 glTF;支持自定义回调(图像加载、图像保存、文件系统访问);支持 Draco 网格解码扩展(✅ 已实现;❌ 编码尚不支持)。
接口层面的能力入口集中在 tiny_gltf.h 的class TinyGLTF:LoadASCIIFromFile/LoadASCIIFromString/LoadBinaryFromFile/LoadBinaryFromMemory四个加载入口,WriteGltfSceneToStream/WriteGltfSceneToFile两个保存入口,外加SetImageLoader/SetImageWriter/SetFsCallbacks三个回调注册入口。
在 Vulkan 示例仓库中的实际集成
本仓库正是 TinyGLTF 的上游文档所列举的"Projects using TinyGLTF"典型场景——用 Vulkan 渲染 glTF 2.0 模型。仓库内的集成方式可作为标准接入范本:
- 通用模型加载器:base/VulkanglTFModel.h 与 base/VulkanglTFModel.cpp 定义了
vkglTF::Model类,它封装了 TinyGLTF 解析、Vulkan 缓冲/纹理上传、场景图与动画驱动等完整流程。其中 VulkanglTFModel.h 在包含tiny_gltf.h前定义了TINYGLTF_NO_STB_IMAGE_WRITE(Android 平台还额外定义TINYGLTF_ANDROID_LOAD_FROM_ASSETS),说明该工程不需要 stb_image_write 的序列化能力。 - 独立的简化加载示例:examples/gltfloading/gltfloading.cpp 用约 700 行代码自建了一个"仅含基础功能"的 glTF 加载类,展示了不依赖
vkglTF::Model的最小集成路径;同类独立封装还出现在 examples/gltfscenerendering/gltfscenerendering.h 与 examples/gltfskinning/gltfskinning.h 中。 - 多目标编译提示:apple/examples.h 的注释说明,除自行封装 tiny_gltf 的示例外,其余示例统一通过编译
base/VulkanglTFModel.cpp引入实现。
自定义图像加载回调的典型用法
base/VulkanglTFModel.cpp 展示了 README 中SetImageLoader回调机制的实战写法:仓库为支持KTX 纹理,自定义了loadImageDataFunc,当图像 URI 以.ktx结尾时直接返回true(把解码交给 Vulkan 侧 KTX 加载代码),其余情况回落到 TinyGLTF 默认的tinygltf::LoadImageData;对于不需要贴图的示例则注册loadImageDataFuncEmpty空实现,配合FileLoadingFlags::DontLoadImages跳过图像解码以加快加载。
对应地,base/VulkanglTFModel.cpp 的vkglTF::Model::loadFromFile是标准接入流程的浓缩:构造tinygltf::Model与tinygltf::TinyGLTF→ 按需SetImageLoader→(Android/OpenHarmony 平台)把应用资源管理器指针赋给tinygltf::asset_manager/tinygltf::rawfile_manager→LoadASCIIFromFile解析 → 成功后依次执行loadImages/loadMaterials/ 场景节点遍历loadNode/loadAnimations/loadSkins。这也印证了 README 中"加载 GLB 只需把LoadASCIIFromFile换成LoadBinaryFromFile"的说法。
快速集成:最小可运行示例
按照 README 的 Build 章节,将stb_image.h、stb_image_write.h(仅当需要图像写出时)、json.hpp与tiny_gltf.h拷贝到你的工程即可,无需链接任何库。由于json.hpp与 stb 系列头文件都已被tiny_gltf.h内部条件包含,实际项目中通常只需手动放置 tiny_gltf.h 与对应依赖文件。
关键规则:实现宏只能在一个.cc(编译单元)中定义一次,否则会造成重复符号/重复实例化:
// 仅在工程中的 *一个* .cc 文件里定义以下宏 #define TINYGLTF_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION // #define TINYGLTF_NOEXCEPTION // 可选。禁用异常处理。 #include "tiny_gltf.h"其余所有包含tiny_gltf.h的文件都不再需要定义TINYGLTF_IMPLEMENTATION,只需#include "tiny_gltf.h"(tiny_gltf.h 中#if defined(TINYGLTF_IMPLEMENTATION)的守卫保证了实现只在你指定的编译单元中展开一次)。本仓库的 examples/gltfloading/gltfloading.cpp 即为这种"单编译单元定义实现"的范例。
加载 glTF 2.0 模型的完整流程
README 给出的加载示例是接入 TinyGLTF 的标准姿势,完整代码如下:
using namespace tinygltf; Model model; TinyGLTF loader; std::string err; std::string warn; bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]); // bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // 用于二进制 glTF(.glb) if (!warn.empty()) { printf("Warn: %s\n", warn.c_str()); } if (!err.empty()) { printf("Err: %s\n", err.c_str()); } if (!ret) { printf("Failed to parse glTF\n"); return -1; }要点拆解:
err与warn语义:warn收集非致命问题(如某些资源加载失败,见 tiny_gltf.h 中"Set warning message towarnfor example it fails to load asserts"的注释),err只在解析真正失败时被填充;ret == false即表示解析失败。LoadASCIIFromString/LoadBinaryFromMemory:README 强调支持"从内存加载",对应 tiny_gltf.h 中两个内存入口,后者额外接受base_dir参数,用于解析内存数据中引用的外部资源相对路径。- 校验级别参数:所有加载入口的最后一个参数
check_sections默认为REQUIRE_VERSION,即加载时会校验 glTF 资产头部的asset.version字段,确保版本与库支持的 2.0 规范一致。 model结构:解析完成后,tinygltf::Model内含scenes/nodes/meshes/accessors/bufferViews/buffers/images/textures/materials/animations/skins等与 glTF 2.0 schema 一一对应的容器,上层渲染代码(如 base/VulkanglTFModel.cpp)从model.scenes[model.defaultScene]出发遍历场景图即可。
编译选项全解析
README 列出了全部 11 个编译期宏开关,它们是 TinyGLTF 可裁剪性的核心。以下逐项说明其作用(并结合 tiny_gltf.h 中的实际条件编译逻辑补充取值语义):
| 宏 | 作用与使用场景 |
|---|---|
TINYGLTF_NOEXCEPTION | 禁用 JSON 解析中的 C++ 异常。可与编译器-fno-exceptions配合,或同时定义符号JSON_NOEXCEPTION与TINYGLTF_NOEXCEPTION以彻底移除异常代码。适用于对二进制体积或异常安全敏感的嵌入式/低延迟场景 |
TINYGLTF_NO_STB_IMAGE | 不通过 stb_image 加载图像,改由TinyGLTF::SetImageLoader(LoadImageDataFunction, void *user_data)注册回调自行解码。从实现看(tiny_gltf.h),未定义此宏时默认图像加载器就是tinygltf::LoadImageData |
TINYGLTF_NO_STB_IMAGE_WRITE | 不通过 stb_image_write 写出图像,改由TinyGLTF::SetImageWriter(WriteImageDataFunction, void *user_data)注册回调。本仓库 VulkanglTFModel.h 即定义了此宏(纯加载用途,无序列化需求) |
TINYGLTF_NO_EXTERNAL_IMAGE | 解析期间不尝试加载外部图像文件,适合"先解析场景结构、后按需加载贴图"的延迟加载架构 |
TINYGLTF_ANDROID_LOAD_FROM_ASSETS | 所有文件改为从打包进 APK 的 assets 读取而非常规文件系统。注意:使用前必须把应用的有效AAssetManager指针赋给tinygltf::asset_manager。仓库的 Android 示例正是通过tinygltf::asset_manager = androidApp->activity->assetManager完成赋值(见 base/VulkanglTFModel.cpp) |
TINYGLTF_ENABLE_DRACO | 启用 Draco 压缩网格解码。需要在工程文件中额外提供 Draco 的 include 路径并链接对应库(tiny_gltf.h 在启用时引入draco/compression/decode.h等头文件) |
TINYGLTF_NO_INCLUDE_JSON | 禁止tiny_gltf.h内部自动#include "json.hpp"——适用于json.hpp已在之前被包含,或希望用自定义路径包含它的情况(tiny_gltf.h) |
TINYGLTF_NO_INCLUDE_STB_IMAGE | 同理,禁止内部自动包含stb_image.h |
TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE | 同理,禁止内部自动包含stb_image_write.h |
TINYGLTF_USE_RAPIDJSON | 使用 RapidJSON 作为 JSON 解析/序列化引擎(实验性)。RapidJSON 文件不随 TinyGLTF 分发,启用时需自行设置 include 路径(tiny_gltf.h 会改为包含document.h、writer.h等 RapidJSON 头文件) |
TINYGLTF_USE_CPP14 | 启用 C++14 特性(需 C++14 编译器),README 说明可能带来优于 C++11 的解析性能 |
除上述宏外,tiny_gltf.h 的实现还预留了TINYGLTF_NO_FS宏(禁用内置文件系统回调),配合TinyGLTF::SetFsCallbacks(FsCallbacks)可完全接管FileExists/ExpandFilePath/ReadWholeFile/WriteWholeFile四个文件系统操作——这与TINYGLTF_ANDROID_LOAD_FROM_ASSETS的定制思路一脉相承,是 TinyGLTF 可移植性的底层机制。
关于扩展属性(ExtensionMap)的取值细节
glTF 规范允许任意对象携带extensions与extras。TinyGLTF 将其解析为ExtensionMap(值为tinygltf::Value的映射)。README 专门给出两个易踩坑的语义说明:
- JSON number 的双重表示:扩展属性中的数值会被解析为 int 或 float 并存入
tinygltf::Value。若想要浮点数值,必须使用GetNumberAsDouble()方法显式获取(例如某个扩展约定scale为 2,底层存储是 INT_TYPE,直接取 double 需要走该方法)。 IsNumber()的判定范围:IsNumber()在底层值是整数或浮点数时均返回true(自 v2.3.0 起该行为被正式确认,见 tiny_gltf.h 版本注释),因此它只能回答"是不是数值",不能区分 int 与 double,需要进一步判断类型时可参考tinygltf::Type枚举(tiny_gltf.h 中INT_TYPE/REAL_TYPE等)。
另外,所有 glTF 对象类(Image、Texture、Node、Material等)都内置Value extras与ExtensionMap extensions字段,且通过TinyGLTF::SetStoreOriginalJSONForExtrasAndExtensions(true)可让库额外保存extras/extensions的原始 JSON 字符串(见 tiny_gltf.h 与Image结构体的extras_json_string字段,tiny_gltf.h),便于上层重建自定义数据结构。SetSerializeDefaultValues(true)则会在保存时强制序列化默认值,用于输出完整的 glTF 描述。
保存 glTF 模型
TinyGLTF 同时提供序列化能力,通过WriteGltfSceneToStream/WriteGltfSceneToFile输出。README 明确了两条保存路径的能力边界:
- 缓冲区(Buffers):✅ 写入外部文件;✅ 内嵌到文件内;❌ Draco 压缩后保存(未实现)。
- 图像(Images):✅ 写入外部文件;✅ 内嵌。
- 二进制(.glb):✅ 支持 .bin 内嵌的单一 .glb;❌ 外部 .bin 形式(未实现)。
WriteGltfSceneToFile的完整签名(tiny_gltf.h)为:
bool WriteGltfSceneToFile(Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary);其中embedImages/embedBuffers控制资源是否内嵌为 DataURI,prettyPrint控制 JSON 是否美化输出,writeBinary控制输出 GLB 还是 ASCII JSON。
附带的示例程序
README 随库分发的三个示例程序(上游 examples 目录)可作为上手参考:
- glview:简单的 glTF 几何查看器,适合理解最小的"加载 + 绘制"闭环。
- validator:基于 JSON schema 的简单 glTF 校验器,适合检查模型文件是否符合规范。
- basic:带纹理支持的基础 glTF 查看器。
需要说明的是:本仓库仅镜像了 TinyGLTF 的运行时头文件(external/tinygltf/ 下仅有tiny_gltf.h、json.hpp、stb_image.h、README.md与LICENSE),示例程序对应的实际工程级范例见仓库自身的 examples/gltfloading/gltfloading.cpp、examples/gltfscenerendering/gltfscenerendering.h、examples/gltfskinning/gltfskinning.h(含动画与骨骼)等 Vulkan 示例,其效果截图位于 screenshots/ 目录(gltfloading.jpg、gltfscenerendering.jpg、gltfskinning.jpg)。
测试与验证
README 给出三层测试体系,可在接入后用于回归验证:
- glTF 解析测试:需 Python 2.6/2.7,将 KhronosGroup 的 glTF-Sample-Models 样例模型集克隆到本地;编译
loader_example后编辑test_runner.py,运行$ python test_runner.py批量验证各种合法/非法模型文件的解析结果。 - 单元测试:
$ cd tests $ make $ ./tester $ ./tester_noexcepttester与tester_noexcept分别验证默认构建与TINYGLTF_NOEXCEPTION构建下的正确性。 - 模糊测试(Fuzzing):详见
tests/fuzzer。README 记录其作者在 Ryzen9 3950X 上连续运行一周模糊测试的结果——LoadASCIIFromString除模糊器自身的 OOM(内存耗尽)外未发现其他崩溃点;同时坦言为更稳妥起见,后续应考虑在解析 glTF 数据时引入有界的(bounded)内存大小检查。
许可证与第三方依赖
TinyGLTF 本体采用MIT 许可证(仓库内见 external/tinygltf/LICENSE)。其依赖的第三方组件许可情况如下:
json.hpp:Copyright (c) 2013-2017 Niels Lohmann,MIT 许可证。base64(base64 编解码):Copyright (C) 2004-2008 René Nyffenegger。stb_image.h:v2.08,public domain(公有领域)图像加载库。stb_image_write.h:v1.09,public domain 图像写出库。catch(单元测试框架):Copyright (c) 2012 Two Blue Cubes Ltd.,Boost Software License 1.0。RapidJSON(可选 JSON 引擎):Copyright (C) 2015 THL A29 Limited(腾讯公司)与 Milo Yip,保留所有权利。dlib(uridecode/uriencode 工具函数):Copyright (C) 2003 Davis E. King,Boost Software License 1.0。
由于 TinyGLTF 本体与主要依赖(json.hpp、stb_image)均为宽松许可,在 Vulkan 这类跨平台图形工程中按 README 建议"拷贝头文件 + 单编译单元定义TINYGLTF_IMPLEMENTATION"即可合规引入,无需额外链接或构建步骤。
- 示例工程
- 图形学
【免费下载链接】Vulkan
C++ examples for the Vulkan graphics API
相关推荐
如何使用STMViewer进行实时变量监控?零基础入门到精通教程
如何使用STMViewer进行实时变量监控?零基础入门到精通教程 STMViewer是一款强大的实时STM32变量与跟踪查看器(Real time STM32
开发工具调试器嵌入式桌面应用Warp C++ CUBIN 集成实战:用 Python 编写 Kernel、在 C++ CUDA 程序中加载与启动
Warp C++ CUBIN 集成实战:用 Python 编写 Kernel、在 C++ CUDA 程序中加载与启动 本指南基于 warp/examples/c
高性能计算物理引擎图形学机器人ncnn Vulkan 驱动加载器(simplevk)完全指南:工作原理、加载顺序与实战用法
ncnn Vulkan 驱动加载器(simplevk)完全指南:工作原理、加载顺序与实战用法 导读 本指南以 docs/developer guide/vulk
人工智能深度学习推理引擎本地部署
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考