You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于tinygltf将自定义三角网格序列化为glTF/GLB格式?

用tinygltf批量导出程序生成三角网格的正确姿势

先给结论:单个三角形单独建缓冲区/访问器不可取

可行是可行,但完全没必要——glTF是为高效传输设计的,每个三角形单独搞一套缓冲区、访问器,会让文件里塞满重复的元数据,体积暴涨,加载时引擎也要反复处理冗余结构,性能拉胯,属于典型的反模式。正确做法是把所有三角形的顶点数据合并到同一个缓冲区,用一套访问器搞定;如果有不同材质,再按材质分组处理。

泛化处理批量三角网格的步骤

假设你已经有批量三角数据:比如一个std::vector<float>,按x1,y1,z1,x2,y2,z2,x3,y3,z3,...的顺序存储所有三角形的顶点坐标。

1. 合并所有顶点数据到单个缓冲区

不用给每个三角形单独分配缓冲区,把所有顶点数据拼进一个大数组:

std::vector<float> all_vertices;
// 遍历所有三角点组,将每个点的x/y/z依次存入数组
for (const auto& triangle : all_triangles) {
    all_vertices.push_back(triangle.p1.x);
    all_vertices.push_back(triangle.p1.y);
    all_vertices.push_back(triangle.p1.z);
    all_vertices.push_back(triangle.p2.x);
    all_vertices.push_back(triangle.p2.y);
    all_vertices.push_back(triangle.p2.z);
    all_vertices.push_back(triangle.p3.x);
    all_vertices.push_back(triangle.p3.y);
    all_vertices.push_back(triangle.p3.z);
}

2. 创建单个缓冲区、BufferView和访问器

将合并后的顶点数据写入tinygltf的缓冲区,再用BufferView和访问器描述数据格式:

tinygltf::Model model;

// 创建缓冲区,写入顶点数据
tinygltf::Buffer& buffer = model.buffers.emplace_back();
buffer.data.resize(all_vertices.size() * sizeof(float));
std::memcpy(buffer.data.data(), all_vertices.data(), buffer.data.size());
buffer.name = "vertex_buffer";

// 创建BufferView,连接缓冲区与访问器
tinygltf::BufferView& bufferView = model.bufferViews.emplace_back();
bufferView.buffer = 0;
bufferView.byteOffset = 0;
bufferView.byteLength = buffer.data.size();
bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
bufferView.name = "vertex_buffer_view";

// 创建访问器,定义顶点数据的类型、数量等
tinygltf::Accessor& accessor = model.accessors.emplace_back();
accessor.bufferView = 0;
accessor.byteOffset = 0;
accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
accessor.count = all_vertices.size() / 3; // 总顶点数(每个顶点3个float)
accessor.type = TINYGLTF_TYPE_VEC3;
// 可选:计算顶点包围盒,填入min/max,方便引擎做优化
accessor.minValues = {std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()};
accessor.maxValues = {std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest()};
for (size_t i = 0; i < all_vertices.size(); i += 3) {
    accessor.minValues[0] = std::min(accessor.minValues[0], all_vertices[i]);
    accessor.minValues[1] = std::min(accessor.minValues[1], all_vertices[i+1]);
    accessor.minValues[2] = std::min(accessor.minValues[2], all_vertices[i+2]);
    accessor.maxValues[0] = std::max(accessor.maxValues[0], all_vertices[i]);
    accessor.maxValues[1] = std::max(accessor.maxValues[1], all_vertices[i+1]);
    accessor.maxValues[2] = std::max(accessor.maxValues[2], all_vertices[i+2]);
}
accessor.name = "vertex_accessor";

3. 创建网格与图元

所有三角形共用同一批顶点数据,只需创建一个图元,模式设为TINYGLTF_MODE_TRIANGLES,引擎会自动按每3个顶点组成一个三角形:

tinygltf::Primitive primitive;
primitive.attributes["POSITION"] = 0; // 指向上面创建的访问器索引
primitive.mode = TINYGLTF_MODE_TRIANGLES;

tinygltf::Mesh mesh;
mesh.primitives.push_back(primitive);
mesh.name = "batch_triangles_mesh";
model.meshes.push_back(mesh);

4. 添加节点并导出

把网格挂到节点上,导出为GLB(二进制格式更紧凑)或glTF:

tinygltf::Node node;
node.mesh = 0; // 指向上面创建的网格索引
model.nodes.push_back(node);
model.scenes[0].nodes.push_back(0); // 将节点加入默认场景

// 导出GLB
tinygltf::Writer writer;
writer.WriteGLB("output.glb", model, true, true);
// 若要导出glTF+bin格式,用下面的代码
// writer.WriteSceneToFile(&model, "output.gltf", tinygltf::WriterOptions());

按材质分组的特殊情况

如果批量三角形有不同材质,就把同材质的三角形顶点合并到同一个缓冲区,每个材质对应一个Primitive,再将这些Primitive放到同一个Mesh中(或不同Mesh,按需选择),既保证效率,又能区分材质渲染。

内容的提问来源于stack exchange,提问作者Lorah Attkins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 09:05:31