【问题标题】:How do I draw an OBJ file in OpenGL using tinyobjloader?如何使用 tinyobjloader 在 OpenGL 中绘制 OBJ 文件?
【发布时间】:2021-08-10 18:34:07
【问题描述】:

我正在尝试在 OpenGL 中绘制 this free airwing model from Starfox 64。我在 Blender 中将 .fbx 文件转换为 .obj 并使用 tinyobjloader 加载它(我的大学科目的所有要求)。

我几乎将示例代码(使用现代 API)添加到我的程序中,替换了文件名,并抓取了 attrib.verticesattrib.normals 向量来绘制机翼。

我可以用 GL_POINTS 查看顶点:

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_POINTS, 0, vertices.size() / 3);
glDisableClientState(GL_VERTEX_ARRAY);

哪个看起来是正确的(我……认为?):

但我不确定如何渲染实体模型。简单地将 GL_POINTS 替换为 GL_TRIANGLES(如图所示)或 GL_QUADS 不起作用:

我正在使用带 GLUT 的 OpenGL 1.1(再次强调,大学)。我想我只是不知道自己在做什么,真的。帮忙?

【问题讨论】:

    标签: c++ opengl glut wavefront


    【解决方案1】:

    E:当我最初写这个答案时,我只使用了顶点和法线。我已经想出了如何让材质和纹理发挥作用,但目前没有时间写出来。当我有时间时,我会添加它,但如果你想同时在 tinyobj 标题周围戳一下,这在很大程度上是相同的逻辑。 :-)

    我在最后一天学到了很多关于 TinyOBJLoader 的知识,所以我希望这对将来的人有所帮助。 Credit goes to this GitHub repositoryfileloader.cpp 中非常清晰干净地使用 TinyOBJLoader。

    总结一下我在学习该代码时学到了什么:

    形状的类型为shape_t。对于单个模型 OBJ,shapes 的大小为 1。我假设 OBJ 文件可以包含多个对象,但我对文件格式没有太多了解。

    shape_t 有一个成员 mesh,类型为 mesh_t。该成员存储从 OBJ 的人脸行解析的信息。您可以通过检查material_ids 成员的大小来计算对象的面数。

    每个面的顶点、纹理坐标和法线索引都存储在网格的indices 成员中。这是std::vector<index_t> 类型。这是一个扁平化的索引向量。所以对于具有三角面的模型 f1, f2 ... fi,它存储v1, t1, n1, v2, t2, n2 ... vi, ti, ni。请记住,这些索引对应于整个顶点、纹理坐标或法线。就我个人而言,我通过导入 Blender 并在打开三角剖分的情况下导出它来对我的模型进行三角剖分。 TinyOBJ 有自己的三角测量算法,你可以通过设置reader_config.triangulate 标志来开启。

    到目前为止,我只使用了顶点和法线。以下是我如何访问和存储它们以在 OpenGL 中使用:

    1. 将平面顶点和法线数组转换为 3 个一组,即 3D 向量
    for (size_t vec_start = 0; vec_start < attrib.vertices.size(); vec_start += 3) {
        vertices.emplace_back(
            attrib.vertices[vec_start],
            attrib.vertices[vec_start + 1],
            attrib.vertices[vec_start + 2]);
    }
    
    for (size_t norm_start = 0; norm_start < attrib.normals.size(); norm_start += 3) {
        normals.emplace_back(
            attrib.normals[norm_start],
            attrib.normals[norm_start + 1],
            attrib.normals[norm_start + 2]);
    }
    

    这样,顶点和法线容器的索引将与面条目给出的索引相对应。

    1. 循环遍历每个面,并将顶点和法线索引存储在单独的对象中
    for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) {
        const std::vector<tinyobj::index_t>& indices = shape->mesh.indices;
        const std::vector<int>& material_ids = shape->mesh.material_ids;
    
        for (size_t index = 0; index < material_ids.size(); ++index) {
            // offset by 3 because values are grouped as vertex/normal/texture
            triangles.push_back(Triangle(
                { indices[3 * index].vertex_index, indices[3 * index + 1].vertex_index, indices[3 * index + 2].vertex_index },
                { indices[3 * index].normal_index, indices[3 * index + 1].normal_index, indices[3 * index + 2].normal_index })
            );
        }
    }
    

    那么绘图就很容易了:

    glBegin(GL_TRIANGLES);
    for (auto triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
        glNormal3f(normals[triangle->normals[0]].X, normals[triangle->normals[0]].Y, normals[triangle->normals[0]].Z);
        glVertex3f(vertices[triangle->vertices[0]].X, vertices[triangle->vertices[0]].Y, vertices[triangle->vertices[0]].Z);
    
        glNormal3f(normals[triangle->normals[1]].X, normals[triangle->normals[1]].Y, normals[triangle->normals[1]].Z);
        glVertex3f(vertices[triangle->vertices[1]].X, vertices[triangle->vertices[1]].Y, vertices[triangle->vertices[1]].Z);
    
        glNormal3f(normals[triangle->normals[2]].X, normals[triangle->normals[2]].Y, normals[triangle->normals[2]].Z);
        glVertex3f(vertices[triangle->vertices[2]].X, vertices[triangle->vertices[2]].Y, vertices[triangle->vertices[2]].Z);
    }
    glEnd();
    

    【讨论】:

    • 你能展示一下实体模型的样子吗?
    • 当然。我已经用光照模型的图像编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2015-02-05
    • 2014-03-19
    • 2021-04-05
    • 2015-10-12
    • 1970-01-01
    • 2014-08-09
    • 2014-04-06
    • 2015-03-18
    相关资源
    最近更新 更多