c++ - 带有 OpenGL 的 OBJ 文件解析器

标签 c++ parsing opengl

我正在编写一个简单的 OBJ 解析器。现在我不知道如何处理#texture_coordinates > #vertices 的情况。 我的 OBJ 文件如下所示:

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8

我知道为什么我们可以拥有比顶点更多的纹理坐标,我只是不知道如何处理它们,尤其是在使用 OpenGL 渲染时(我这样做)。当我解析 obj 时,我的索引缓冲区看起来像这样:

4 0 3
4 3 7
2 6 7
2 7 3
1 5 2
5 6 2
0 4 1
4 5 1
4 7 5
7 6 5
0 1 2
0 2 3

请注意,我从所有索引中减去 1,因为 OpenGL 需要这样。对象绘制得很好,但顶点数据当然是错误的。顺便说一句,我将顶点数据存储在专用的VBO中。因此,我的 VAO 总共有 2 个 VBO(顶点、纹理)和一个 EBO,用于存储顶点形成三角形的信息。现在如何处理纹理坐标?

根据建议进行编辑:

#

所以我解压缩了顶点(这意味着我总共有 36 个顶点)并将顶点、纹理和法线数据放在一个连续的数组中。结果是这样的: enter image description here

我的 VBO 看起来像这样:

1.000000 1.000000 -1.000000 0.748573 0.750412 0.000000 0.000000 -1.000000
1.000000 -1.000000 -1.000000 0.749279 0.501284 0.000000 0.000000 -1.000000
-1.000000 -1.000000 -1.000000 0.999110 0.501077 0.000000 0.000000 -1.000000
1.000000 1.000000 -1.000000 0.748573 0.750412 0.000000 0.000000 -1.000000
-1.000000 -1.000000 -1.000000 0.999110 0.501077 0.000000 0.000000 -1.000000
-1.000000 1.000000 -1.000000 0.999455 0.750380 0.000000 0.000000 -1.000000

等等... 一行恰好是一个顶点:顶点位置、纹理坐标、法 vector 。

所以三行代表一个三角形。

我通过这样的属性指针设置:

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * objModel2.getObjData().size(), &objModel2.getObjData()[0], GL_STATIC_DRAW);
// vertex positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(0);
// texture positions
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(1);
// normals positions
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat) * 5));
glEnableVertexAttribArray(2);

最后像这样绘制立方体:

    glBindVertexArray(VAO_cube_model_2);
    glDrawArrays(GL_TRIANGLES, 0, objModel2.getObjData().size() / 8);
    glBindVertexArray(0);

纹理文件是下面的png文件: enter image description here

只有 3 和 5 被绘制。我想不通为什么...

最佳答案

OpenGL 使用与 obj 格式不同的索引模式。在 obj 中,每个属性(位置、颜色、纹理坐标)都可以有自己的索引,而在 OpenGL 中只有一个可能的索引。为了正确加载 obj,您必须复制一些数据并找到新的索引。

最简单的事情就是在没有任何重复检查的情况下工作。在这种情况下,您只需复制每个顶点的数据。该过程可能如下所示:

load all v lines into v[]
load all vt lines into vt[]
load all vn lines into vn[]

float[] verts;
int[] idx;

foreach f line consisting of (vi[0]/vti[0]/vni[0] vi[1]/vti[1]/vni[1] vi[2]/vti[2]/vni[2])
    for i = 1:3
        verts.append(v[vi[i]], vt[vti[i]], vn[vni[i]]);
        idx.append(verts.size() - 1);

在这种情况下,您甚至可以不使用索引缓冲区。人们可能会注意到,这不是最佳方式,因为会保存大量重复数据。一种优化是确定 vi、vti、vni 的组合是否已经添加到顶点列表中,如果已经是这种情况,则只添加正确的索引。

关于c++ - 带有 OpenGL 的 OBJ 文件解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40107359/

相关文章:

c++ - 可以返回引用吗?

c++ - C++中的伪虚函数是什么?

c++ - 是否可以在 C++ 中创建一个符合开放/封闭原则的工厂?

c++ - 如何在Visual Studio 2017和C++中自动生成注释?

PHP:从数组中解析最旧的日期

parsing - 如何在 Julia 中解析外部命令的输出?

C# - 寻找一种简单的方法来解析两种不同表示形式(数字和英文)之间的字符串值

c++ - 如何设置 2 个不同的 FBO 作为片段着色器输出

java - 使用 OpenGL 将对象放置在网格上的正确位置是什么?

javascript - 计算多边形的法向量 - Newells 法