【C++】tinygltf基本使用方法

一、前言       

        网上的教程均为搭配opengl使用,如果单纯想读取模型数据,资料就比较少了。先放出相关链接:

1、gltf规范文档:glTF™ 2.0 Specification (khronos.org)

2、gltf在线模型查看器 :glTF Viewer (donmccurdy.com)

3、tinygltf:GitHub - syoyo/tinygltf: Header only C++11 tiny glTF 2.0 library

4、vulkan使用tinygltf官方示例:GitHub - SaschaWillems/Vulkan-glTF-PBR: Physical based rendering with Vulkan using glTF 2.0 models

 二、使用

1、初始化

        首先引入头文件:

#define TINYGLTF_IMPLEMENTATION
#include <tiny_gltf.h>

        再加载文件,注意glb格式和gltf格式调用的函数不同: 

tinygltf::Model model;
tinygltf::TinyGLTF loader;
std::string err;
std::string warn;

bool load_ok;
if(is_glb)
	load_ok = loader.LoadBinaryFromFile(&model, &err, &warn, std::string{ path_name });
else
	load_ok = loader.LoadASCIIFromFile(&model, &err, &warn, std::string{ path_name });


if (!warn.empty()) 
	log_warn("{}", warn);

if (!err.empty())
	log_err("{}", err);

if (!load_ok)
{
	log_err("解析gltf格式失败:{}", err);
	return {};
}

二、读取材质

        其中 mo_material是我们自定义的结构,用于接收读取到的数据。由于之前的光照模型是 Blinn-Phong,而现在更通用的是pbr,以下代码可能不适用你的需求。

        之所以我将纹理导出、因为我要进行二次处理,生成自定义的模型格式。你可以不保存到文件,而是直接使用。

        通过查看 tinygltf::PbrMetallicRoughness类型的定义,可以知道提供哪些纹理和材质数据。

//------------------获取材质-------------------
mo_material.resize(model.materials.size());
for (size_t i = 0; i < model.materials.size(); ++i)
{
	tinygltf::Material& m = model.materials[i];
	log_info("材质:{},{}", i, m.name);
	tinygltf::PbrMetallicRoughness& pbr = m.pbrMetallicRoughness;

	ModelObjectMaterial& material = mo_material[i];
	material._materialName = m.name;

	material._material._diffuseAlbedo = { 
		(float)pbr.baseColorFactor[0],
		(float)pbr.baseColorFactor[1],
		(float)pbr.baseColorFactor[2], 1.0f };
	material._material._fresnelR0 = { 
		(float)pbr.metallicFactor, (float)pbr.metallicFactor, (float)pbr.metallicFactor };
	material._material._roughness = (float)pbr.roughnessFactor;

	// 纹理导出
	auto fn_texture = [&](int index, std::string_view tag) {
		if (index == -1)
			return std::string{};
		tinygltf::Image& img = model.images[index];
		if (!img.uri.empty())
			return img.uri;

		std::string path_name = fmt::format("{}{}{}#{}.png",
			File::PathTemp(), res_name, tag, i);
		stbi_write_png(CodeCvt::Utf8ToMultiByte(path_name).c_str(),
			img.width, img.height,
			4, img.image.data(), 0);

		return path_name;
		};
	material._pathTexDiffuse = fn_texture(pbr.baseColorTexture.index, "");
	material._pathTexNormal = fn_texture(m.normalTexture.index, "_normal");
}

         我的做法是按材质合并网格(不知道会不会影响骨骼动画,后面再说),你也可以简单处理,按 tinygltf::Primitive为单位为绘制。prim里不直接存数据,而是通过 Accessor访问,然后通过 Accessor取得 BufferView,然后通过 BufferView取得真正的顶点数据。

        注意数据首地址为 buf.data.data() + buf_view.byteOffset + accessor.byteOffset,单个顶点偏移为 int byte_stride = accessor.ByteStride(buf_view)。

        注意我这里没有过多的 assert的检查,这里的代码还需要尝试更多模型,才能具有可靠性。

		for (tinygltf::Mesh& mesh : model.meshes)
		{
			for (tinygltf::Primitive& prim : mesh.primitives)
			{
				PrimData& prim_data = map_material[prim.material];
				size_t vertex_offset = prim_data._allVertex.size();

				// 索引
				{
					tinygltf::Accessor accessor = model.accessors[prim.indices];
					tinygltf::BufferView buf_view = model.bufferViews[accessor.bufferView];
					tinygltf::Buffer& buf = model.buffers[buf_view.buffer];

					int byte_stride = accessor.ByteStride(buf_view);
					unsigned char* p = buf.data.data() + buf_view.byteOffset + accessor.byteOffset;

					assert(byte_stride == 4);

					for (size_t i = 0; i < accessor.count; ++i)
					{
						uint32_t index = *(uint32_t*)p; 
						prim_data._allIndex.push_back(vertex_offset + index);
						p += byte_stride;
					}
				}
				// 位置
				auto iter_pos = prim.attributes.find("POSITION");
				if(iter_pos != prim.attributes.end())
				{
					tinygltf::Accessor accessor = model.accessors[iter_pos->second];
					tinygltf::BufferView buf_view = model.bufferViews[accessor.bufferView];
					tinygltf::Buffer& buf = model.buffers[buf_view.buffer];

					prim_data._allVertex.resize(vertex_offset + accessor.count);

					int byte_stride = accessor.ByteStride(buf_view);
					unsigned char* p = buf.data.data() + buf_view.byteOffset + accessor.byteOffset;

					for (size_t i = 0; i < accessor.count; ++i)
					{
						Vertex& v = prim_data._allVertex[vertex_offset + i];
						v._pos = *(Position3*)p;
						p += byte_stride;
					}
				}
				// uv
				auto iter_uv = prim.attributes.find("TEXCOORD_0");
				if (iter_uv != prim.attributes.end())
				{
					tinygltf::Accessor accessor = model.accessors[iter_uv->second];
					tinygltf::BufferView buf_view = model.bufferViews[accessor.bufferView];
					tinygltf::Buffer& buf = model.buffers[buf_view.buffer];

					int byte_stride = accessor.ByteStride(buf_view);
					unsigned char* p = buf.data.data() + buf_view.byteOffset + accessor.byteOffset;

					for (size_t i = 0; i < accessor.count; ++i)
					{
						Vertex& v = prim_data._allVertex[vertex_offset + i];
						v._uv = *(Position2*)p;
						p += byte_stride;
					}
				}
				// normal
				auto iter_normal = prim.attributes.find("NORMAL");
				if (iter_normal != prim.attributes.end())
				{
					tinygltf::Accessor accessor = model.accessors[iter_normal->second];
					tinygltf::BufferView buf_view = model.bufferViews[accessor.bufferView];
					tinygltf::Buffer& buf = model.buffers[buf_view.buffer];

					int byte_stride = accessor.ByteStride(buf_view);
					unsigned char* p = buf.data.data() + buf_view.byteOffset + accessor.byteOffset;

					for (size_t i = 0; i < accessor.count; ++i)
					{
						Vertex& v = prim_data._allVertex[vertex_offset + i];
						v._normal = *(Position3*)p;
						p += byte_stride;
					}
				}
				// TANGENT
				auto iter_tangent = prim.attributes.find("TANGENT");
				if (iter_tangent != prim.attributes.end())
				{
					tinygltf::Accessor accessor = model.accessors[iter_tangent->second];
					tinygltf::BufferView buf_view = model.bufferViews[accessor.bufferView];
					tinygltf::Buffer& buf = model.buffers[buf_view.buffer];

					int byte_stride = accessor.ByteStride(buf_view);
					unsigned char* p = buf.data.data() + buf_view.byteOffset + accessor.byteOffset;

					for (size_t i = 0; i < accessor.count; ++i)
					{
						Vertex& v = prim_data._allVertex[vertex_offset + i];
						v._tangent = *(Position3*)p;
						p += byte_stride;
					}
				}
			}
		}

三、截图

        这里没有处理骨骼动画和光照,不过对于学习 tinygltf的基本用法,应该是足够了!

        项目地址:dl: C++ drawing library (gitee.com) 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值