从.obj文件中在OpenGL中绘制四边形

时间:2014-12-07 07:44:54

标签: c++ opengl blender

我正在将一个.obj(在Blender中创建,如果有帮助)解析为OpenGL中的C ++程序,它似乎即将结束。它似乎正确地找到了这里需要的顶点。我正在尝试创建一个移动的大写字母。这是我的解析代码:

bool loadObjectFile(const char * filepath, std::vector < glm::vec3 > & out_vertices, std::vector < glm::vec3 > & out_normals)
{
    std::vector< unsigned int > vertexIndices;
    std::vector< glm::vec3 > temp_vertices;
    std::vector< glm::vec3 > temp_normals;
    glm::vec3 tempFaceNormal;

    FILE * file = fopen(filepath, "r");
    if (file == NULL)
    {
        printf("Impossible to open the file !\n");
        return false;
    }

    while (1)
    {
        char lineHeader[128];
        // read the first word of the line
        int res = fscanf(file, "%s", lineHeader);
        if (res == EOF)
            break; // EOF = End Of File. Quit the loop.

        // else : parse lineHeader
        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);
            glm::vec3 zeroface;
            zeroface.x = 0, zeroface.y = 0, zeroface.z = 0;
            temp_normals.push_back(zeroface);
        }
        else if (strcmp(lineHeader, "f") == 0)
        {
            unsigned int vertexIndex[3];
            int matches = fscanf(file, "%d %d %d\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]);

            if (matches != 3)
            {
                printf("File can't be read by our simple parser : ( Try exporting with other options\n");
                return false;
            }

            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);

            tempFaceNormal = computeNormal(temp_vertices[vertexIndex[0] - 1], temp_vertices[vertexIndex[1] - 1], temp_vertices[vertexIndex[2] - 1]);

            temp_normals[vertexIndex[0] - 1] += tempFaceNormal;
            temp_normals[vertexIndex[1] - 1] += tempFaceNormal;
            temp_normals[vertexIndex[2] - 1] += tempFaceNormal;
        }
    }

    // For each vertex of each triangle
    for (unsigned int i = 0; i < vertexIndices.size(); i++)
    {
        unsigned int vertexIndex = vertexIndices[i];
        glm::vec3 vertex = temp_vertices[vertexIndex - 1];
        out_vertices.push_back(vertex);
    }

    //For each vertex normal
    for (unsigned int i = 0; i < temp_normals.size(); i++)
    {
        out_normals.push_back(glm::normalize(temp_normals[i]));
    }
}

glm::vec3 computeNormal(glm::vec3 const & a, glm::vec3 const & b, glm::vec3 const & c)
{
    //Returns the (not normalized) face normal of each triangle
    return glm::cross(c - a, b - a);
}

这是我用来绘制解析顶点的代码:

glBegin(GL_QUADS);
glColor3f(0.2, 0.2, 0);
for (int i = 0; i < vertices.size(); i++)
{
    glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
    glTexCoord3f(vertices[i].x, vertices[i].y, vertices[i].z);
}
for (int i = 0; i < normals.size(); i++)
{
    glNormal3f(normals[i].x, normals[i].y, normals[i].z);
}
glEnd();

这会产生以下(截图):

https://drive.google.com/file/d/0B6qCowcn51DnQk1mT0hSUjZWc00/view?usp=sharing

https://drive.google.com/file/d/0B6qCowcn51DndTBJRFctV1lOQlk/view?usp=sharing

这里,顶点似乎是正确的,但是面部似乎绘制不正确。

2 个答案:

答案 0 :(得分:3)

此代码中存在许多问题。我对其中大部分内容都没有详细介绍,但我想至少指出主要问题。

OBJ解析

只要您自己生成OBJ文件并准确控制它们包含的内容,您所做的主要部分就可能很好。如果您希望能够解析更常见的OBJ文件,则需要更多。例如:

  • &#34; v&#34;以外的其他记录类型和&#34; f&#34;。 OBJ文件可以包含其他数据。其中一部分,如法线(&#34; vn&#34;)和纹理坐标(&#34; vt&#34;)通常对OpenGL渲染至关重要。除此之外,还有很多可能会或可能没有用,这取决于你想要什么。但至少你必须能够跳过你没有解析的记录。
  • 索引值可以是负值,使它们相对于当前位置。
  • 面孔可以有任意数量的顶点。
  • faces的格式每个顶点最多有3个索引(顶点/ texcoord / normal),用斜杠分隔。

原始类型不匹配

这可能是最大的问题。您的解析代码每个面读取3个顶点,这意味着它希望所有面都是三角形:

int matches = fscanf(file, "%d %d %d\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]);
if (matches != 3)
{
    printf("File can't be read by our simple parser : ( Try exporting with other options\n");
    return false;
}

然后渲染代码将面部渲染为四边形:

glBegin(GL_QUADS);

如果文件包含三角形,则需要将其用作渲染的基本类型:

glBegin(GL_TRIANGLES);

如果它包含四边形,则需要为每个面解析4个顶点索引,并相应地更新其余的解析代码。

正常数组

代码包含根据面中顶点索引的顺序重新排列顶点的逻辑:

for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
    unsigned int vertexIndex = vertexIndices[i];
    glm::vec3 vertex = temp_vertices[vertexIndex - 1];
    out_vertices.push_back(vertex);
}

但它只是以顶点的原始顺序复制法线:

for (unsigned int i = 0; i < temp_normals.size(); i++)
{
    out_normals.push_back(glm::normalize(temp_normals[i]));
}

相反,它需要以与顶点相同的方式重新排列法线。这可以作为顶点循环的一部分来完成:

for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
    unsigned int vertexIndex = vertexIndices[i];
    out_vertices.push_back(temp_vertices[vertexIndex - 1]);
    out_normals.push_back(glm::normalize(temp_normals[vertexIndex - 1]));
}

渲染期间法线的规范

渲染代码在循环顶点之后在法线上包含一个单独的循环。以这种方式指定法线绝对值得注意。需要在glNormal3fv()之前调用glVertex3fv()来指定该特定顶点的法线。

渲染循环应该如下所示,而不是有两个独立的循环:

for (int i = 0; i < vertices.size(); i++)
{
    glNormal3f(normals[i].x, normals[i].y, normals[i].z);
    glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
}

glTexCoord3f()来电也会在 glVertex3f()之前进行,但您首先没有纹理坐标。

过时的OpenGL

您正在使用的呈现调用,glBegin() / glEnd()glVertex3f()等是遗留功能。这通常被称为立即模式,不推荐使用,并且在现代版本的OpenGL(OpenGL 3.x / 4.x核心配置文件,OpenGL ES 2.0 / 3.x)中不再可用。

对于现代渲染,首先要了解顶点缓冲区对象(VBO)和顶点阵列对象(VAO)以指定顶点。然后,您可以使用glDrawElements()glDrawArrays()通过一次调用绘制整个对象。

答案 1 :(得分:0)

我最终想出了我能做些什么。因为我已经将Bender的.obj文件作为一组四边形导出,所以我使用的是GL_QUADS和GL_QUADSTRIP。出于某种原因,这两个产生了一些奇怪,丑陋的结果。我刚刚将.obj文件导入Blender,并将网格转换为三角形而不是四边形。然后,我将这些文件提供给此代码,一切正常。谢谢大家!