自定义 OpenGL OBJ 模型加载器索引计算不起作用

Custom OpenGL OBJ model loader indices calculation not working

我按照 this website 中的教程制作了一个简单的模型加载器。

所有的坐标都读得很好,但它没有讨论的是如何计算索引,但是在进一步的教程之一 found here 中称为 VBO 索引。虽然这看起来是个好方法,但它计算的索引是错误的。

我在搅拌机中制作了一个立方体并导出为 obj,我用我以前大学老师制作的 obj 加载器加载它,索引读取为 0,1,2,3,4, (依此类推)...立方体为 35。使用下面的代码,索引变为 0,1,2,3,... 16,17,0,18,3,19,4,6,20,7,9,21,10,12,22,13 ,15,23,16.

这是因为它找到相似的顶点并将其推回索引,而实际上对于这个立方体不应该有任何相似的索引。

函数索引VBO慢速

   void Model::indexVBO_slow(
    std::vector<glm::vec3>& in_vertices,
    std::vector<glm::vec2>& in_uvs,
    std::vector<glm::vec3>& in_normals,

    std::vector<unsigned short>& out_indices,
    std::vector<glm::vec3>& out_vertices,
    std::vector<glm::vec2>& out_uvs,
    std::vector<glm::vec3>& out_normals
) {
    // For each input vertex
    for (unsigned int i = 0; i < in_vertices.size(); i++) {

        // Try to find a similar vertex in out_XXXX
        unsigned short index;
        bool found = getSimilarVertexIndex(in_vertices[i], in_uvs[i], in_normals[i], out_vertices, out_uvs, out_normals, index);

        if (found) { // A similar vertex is already in the VBO, use it instead !
            out_indices.push_back(index);
        }
        else { // If not, it needs to be added in the output data.
            out_vertices.push_back(in_vertices[i]);
            out_uvs.push_back(in_uvs[i]);
            out_normals.push_back(in_normals[i]);
            out_indices.push_back((unsigned short)out_vertices.size() - 1);
        }
    }

我将从文件中读取的顶点、uv 和法线传入 vector3s 和 vector2 的向量中。其他叫出来的_当空容器进去。

函数getSimilarVertexIndex

// Searches through all already-exported vertices
// for a similar one.
// Similar = same position + same UVs + same normal
bool Model::getSimilarVertexIndex(glm::vec3& in_vertex, glm::vec2& in_uv, glm::vec3& in_normal, std::vector<glm::vec3>& out_vertices, std::vector<glm::vec2>& out_uvs, std::vector<glm::vec3>& out_normals, unsigned short& result)
{
    // Lame linear search
    for (unsigned int i = 0; i < out_vertices.size(); i++)
    {
        if (
            is_near(in_vertex.x, out_vertices[i].x) &&
            is_near(in_vertex.y, out_vertices[i].y) &&
            is_near(in_vertex.z, out_vertices[i].z) &&
            is_near(in_uv.x, out_uvs[i].x) &&
            is_near(in_uv.y, out_uvs[i].y) &&
            is_near(in_normal.x, out_normals[i].x) &&
            is_near(in_normal.y, out_normals[i].y) &&
            is_near(in_normal.z, out_normals[i].z)
            )
        {
            result = i;
            return true;
        }
    }

    // No other vertex could be used instead.
    // Needs to be added to VBO
    return false;
}

函数is_near

bool Model::is_near(float v1, float v2) {
return fabs(v1 - v2) < 0.01f;

}

填写EBO

//Fill EBO with indices 
    m_buffer->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); //gl bind buffer
    m_buffer->FillBuffer(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW); //gl buffer data

我 100% 确定这不起作用的原因是我不完全理解从 obj 获取数据并将其转换为 OpenGL 索引之间的逻辑。我理解它检查是否已经找到一些数据的部分,以便它可以重用一些索引数据,但不是 if 语句的细节。也许是因为我将它作为 EBO 发送并且教程正在执行 VBO 索引。

解析器代码加上 VBO 索引

std::vector <glm::vec3> m_vertices, m_normals;
std::vector <glm::vec2> m_uvs;

std::vector <unsigned int> vertexIndices, uvIndices, normalIndices;

std::vector<unsigned short> out_indices;
std::vector<glm::vec3> out_vertices;
std::vector<glm::vec2> out_uvs;
std::vector<glm::vec3> out_normals;

std::vector<glm::vec3> temp_vertices, temp_normals;
std::vector<glm::vec2> temp_uvs;


std::string temp_text = "";
FILE* file = fopen(filepath.c_str(), "r");

//Open File

if (!file)
{
    TheDebug::Log("Impossible to openfile", ALERT);
    return false;
}

//Read file until the end
while (1)
{
    char lineHeader[128];
    // read the first word of the line
    int res = fscanf(file, "%s", lineHeader);

    if (res == EOF)
    {
        break;
    }

    //Parse lineheader
    int x = strcmp(lineHeader, "v");
    if (strcmp(lineHeader, "v") == 0) 
    {
        glm::vec3 vertex;
        fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
        temp_vertices.push_back(vertex);
    }
    //scan for uvs
    else if (strcmp(lineHeader, "vt") == 0) 
    {
        glm::vec2 uv;
        glm::vec2 uv2;
        glm::vec2 uv3;
        fscanf(file, "%f %f %f %f\n", &uv.x, &uv.y, &uv3.x, &uv2.y);
        temp_uvs.push_back(uv);
    }
    //scan for normals
    else if (strcmp(lineHeader, "vn") == 0) {
        glm::vec3 normal;
        fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
        temp_normals.push_back(normal);
    }
    //Scan for faces
    else if (strcmp(lineHeader, "f") == 0) 
    {

        std::string vertex1, vertex2, vertex3;
        unsigned int vertexIndex[4], uvIndex[4], normalIndex[4];
        int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);

        if (matches != 9)
        {
            printf("File can't be read : ( Try exporting with other options\n");
            fclose(file);
            return false;
        }


        //triangulated
        if (matches == 9)
        {
            //Add 3 total vertices
            m_totalVertices += 3;

            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            uvIndices.push_back(uvIndex[0]);
            uvIndices.push_back(uvIndex[1]);
            uvIndices.push_back(uvIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }
    }
}

//Go through each vertex of each triangle
for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
    unsigned int vertexIndex = vertexIndices[i];
    unsigned int uvIndex = uvIndices[i];
    unsigned int normalIndex = normalIndices[i];

    glm::vec3 vertex = temp_vertices[vertexIndex - 1];
    glm::vec2 uv = temp_uvs[uvIndex - 1];
    glm::vec3 normal = temp_normals[normalIndex - 1];
    m_normals.push_back(normal);

    m_vertices.push_back(vertex);
    m_uvs.push_back(uv);
}

fclose(file);
unsigned short result;

std::vector<GLuint> indices;
std::vector<glm::vec3> indexed_vertices;
std::vector<glm::vec2> indexed_uvs;
std::vector<glm::vec3> indexed_normals;

indexVBO(m_vertices, m_uvs, m_normals, indices, indexed_vertices, indexed_uvs, indexed_normals);

//在我用这些索引填充缓冲区后。

加载的Cube图片

从昨天开始我就开始搜索这些信息的来源,但是我能找到的地方似乎非常有限,而且没有任何东西能真正帮助我找到信息,所以我不得不求助于堆栈溢出。

请记住,问题在于计算索引,因为使用另一个模型加载器,索引不同并且同时从 0-35 变化,而我的没有。

希望我没有浪费你的时间,提前谢谢你。

经过长达 4 天的测试,我发现我可以在 for 循环中将 v3s 的向量转换为 GLfloats 的向量以添加所有这些向量,并为每个 i 添加索引。

std::vector<GLfloat> testv;
std::vector<GLfloat> testu;
std::vector<GLfloat> testn;
for (size_t i = 0; i < m_vertices.size(); i++)
{
    testv.push_back(m_vertices[i].x);
    testv.push_back(m_vertices[i].y);
    testv.push_back(m_vertices[i].z);

    testu.push_back(m_uvs[i].x);
    testu.push_back(m_uvs[i].y);

    testn.push_back(m_normals[i].x);
    testn.push_back(m_normals[i].y);
    testn.push_back(m_normals[i].z);
    testindices.push_back(i);
}

这似乎适用于我使用的至少 3 个三角模型,但不知道这是否是最佳方法。