C++ 模型中顶点重复的消除

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

当导入.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 &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感谢您发现了这个错误