C++ 模型中顶点重复的消除
当导入.obj格式的模型时,许多多边形共享顶点,从而徒劳地消耗内存,我想做的是通过只保存唯一的顶点来删除重复项 顶点散列C++ 模型中顶点重复的消除,c++,opengl,hash,C++,Opengl,Hash,当导入.obj格式的模型时,许多多边形共享顶点,从而徒劳地消耗内存,我想做的是通过只保存唯一的顶点来删除重复项 顶点散列 /// Template specialization for hashing of a Vec3 namespace std { template<typename T> struct hash<Vec3<T>> { void hash_combine(size_t &seed, const size_t &has
/// Template specialization for hashing of a Vec3
namespace std {
template<typename T>
struct hash<Vec3<T>> {
void hash_combine(size_t &seed, const size_t &hash) const {
seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
size_t operator() (const Vec3<T> &vec) const {
auto hasher = hash<float>{};
auto hashed_x = hasher(vertex.position.x);
auto hashed_y = hasher(vertex.position.y);
auto hashed_z = hasher(vertex.position.z);
auto hashed_color_r = hasher(vertex.color.r);
auto hashed_color_g = hasher(vertex.color.g);
auto hashed_color_b = hasher(vertex.color.b);
auto hashed_color_a = hasher(vertex.color.a);
auto hashed_texcoord_x = hasher(vertex.texCoord.x);
auto hashed_texcoord_y = hasher(vertex.texCoord.y);
auto hashed_normal_x = hasher(vertex.normal.x);
auto hashed_normal_y = hasher(vertex.normal.y);
auto hashed_normal_z = hasher(vertex.normal.z);
size_t seed = 0;
hash_combine(seed, hashed_x);
hash_combine(seed, hashed_y);
hash_combine(seed, hashed_z);
hash_combine(seed, hashed_texcoord_x);
hash_combine(seed, hashed_texcoord_y);
hash_combine(seed, hashed_normal_x);
hash_combine(seed, hashed_normal_y);
hash_combine(seed, hashed_normal_z);
return seed;
}
};
}
///Vec3散列的模板专门化
名称空间标准{
模板
结构散列{
无效哈希组合(大小和种子、常量大小和哈希)常量{
seed^=hash+0x9e3779b9+(seed>2);
}
size_t运算符()(常量Vec3和vec)常量{
自动哈希器=哈希{};
自动散列_x=散列器(vertex.position.x);
自动散列_y=散列器(vertex.position.y);
自动散列_z=散列器(vertex.position.z);
自动散列\u color\u r=hasher(vertex.color.r);
自动散列\u color\u g=hasher(vertex.color.g);
自动散列\u color\u b=散列器(vertex.color.b);
自动散列\u color\u a=hasher(vertex.color.a);
自动哈希_texcoord_x=hasher(vertex.texcoord.x);
自动哈希_texcoord_y=hasher(vertex.texcoord.y);
自动散列_normal_x=散列器(vertex.normal.x);
自动散列_normal_y=hasher(vertex.normal.y);
自动散列_normal_z=hasher(vertex.normal.z);
种子大小=0;
散列组合(种子、散列组合);
散列组合(种子,散列);
散列组合(种子,散列);
散列联合(种子、散列联合);
散列联合(种子、散列的texcoord);
散列合并(种子、散列正常值);
散列组合(种子,散列正常);
散列合并(种子、散列正常值);
返回种子;
}
};
}
使用tinyobjcloader导入网格
Mesh Renderer::load_mesh_from_file(std::string filepath) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
auto success = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filepath.c_str());
if (!success) { SDL_Log("Failed loading mesh %s: %s", filepath.c_str(), err.c_str()); return Mesh(); }
std::unordered_map<Vertex<float>, size_t> unique_vertices{};
Mesh mesh{};
for (auto shape : shapes) { // Shapes
size_t index_offset = 0;
for (auto face : shape.mesh.num_face_vertices) { // Faces (polygon)
for (auto v = 0; v < face; v++) {
tinyobj::index_t idx = shape.mesh.indices[index_offset + v];
Vertex<float> vertex{};
float vx = attrib.vertices[3 * idx.vertex_index + 0];
float vy = attrib.vertices[3 * idx.vertex_index + 1];
float vz = attrib.vertices[3 * idx.vertex_index + 2];
vertex.position = {vx, vy, vz};
float tx = attrib.vertices[3 * idx.texcoord_index + 0];
float ty = attrib.vertices[3 * idx.texcoord_index + 1];
vertex.texCoord = {tx, ty};
float nx = attrib.normals[3 * idx.normal_index + 0];
float ny = attrib.normals[3 * idx.normal_index + 1];
float nz = attrib.normals[3 * idx.normal_index + 2];
vertex.normal = {nx, ny, nz};
// These two lines work just fine (includes all vertices)
// mesh.vertices.push_back(vertex);
// mesh.indices.push_back(mesh.indices.size());
// Check for unique vertices, models will contain duplicates
if (unique_vertices.count(vertex) == 0) {
unique_vertices[vertex] = mesh.indices.size();
mesh.vertices.push_back(vertex);
mesh.indices.push_back(mesh.indices.size());
} else {
mesh.indices.push_back(unique_vertices.at(vertex));
}
}
index_offset += face;
}
}
SDL_Log("Number of vertices: %lu for model %s", mesh.vertices.size(), filepath.c_str());
return mesh;
}
网格渲染器::从网格文件加载网格(std::字符串文件路径){
tinyobj::attrib_t attrib;
向量形状;
载体材料;
std::字符串错误;
auto success=tinyobj::LoadObj(&attrib,&shapes,&materials,&err,filepath.c_str());
如果(!success){SDL_Log(“加载网格%s:%s失败”,filepath.c_str(),err.c_str());返回网格();}
无序映射唯一顶点{};
网格{};
对于(自动形状:形状){//shapes
大小索引偏移量=0;
对于(自动面:shape.mesh.num\u face\u顶点){//面(多边形)
用于(自动v=0;v
第一个图像是包含所有顶点时的图像
这一个是当我只使用唯一的顶点
有什么想法吗?从显示的图像来看,这似乎是一个三角形顶点参考问题
通常,收集一个唯一顶点列表,每个三角形只是对应于其三个顶点的三个索引的集合。让我们假设,由于某种原因,您确实有重复的顶点a和顶点B,并且您决定删除顶点B。在这种情况下,您需要修改包含B的所有三角形的引用,并将其替换为a。删除重复的坐标不是一个好主意。例如,坐标将在网格之间的缝合区域重复,以形成闭合的三维网格结构。在3D游戏中,采用低多边形网格结构以允许快速渲染,但除非另有说明,否则处理这些点云不再是一个大问题,因为强大的GPU和多核CPU系统正在使逼真的动画成为可能。if(unique_vertexs.count(vertex)==0){ 唯一顶点[顶点]=网格。顶点.size() mesh.index.push_back(mesh.顶点.size()) 网格。顶点。推回(顶点) }
说明:索引是指向顶点位置的“指针”。为此,您需要在写入顶点数据的位置获取索引,而不是索引数据的索引。Hmm。。我在规范中找不到任何要求顶点唯一的内容:/它们不是,但明智的网格创建者不会放置重复(如果有重复,冗余会占用更多的空间)。但是,这可能会发生,例如,如果您的网格是从al STL文件转换而来的,而al STL文件确实有重复。@Entalpi您的网格可能具有相同的顶点xyz,但具有不同的texCoord。某些纹理可能在顶点中有坐标跳跃,需要使用不同的纹理坐标重复相同的坐标。但这应该是罕见的,它不应该解释你得到的渲染的差异。我能看到的唯一编码错误是当您读取纹理v坐标时,它是从u坐标复制粘贴的。@PauliNieminen感谢您发现了这个错误