在 OpenGL 中为索引缓冲区对象使用结构会导致段错误

Posted

技术标签:

【中文标题】在 OpenGL 中为索引缓冲区对象使用结构会导致段错误【英文标题】:Using struct for indexed buffer object in OpenGL results in segfault 【发布时间】:2013-08-23 03:29:20 【问题描述】:

我一直在使用std::vector<glm::vec3>'s 来存储顶点属性,一切正常,渲染各种不同的网格。但是在重构之后,我的顶点属性存储在一个结构中,我无法得到最简单的东西来渲染。这是结构(简化):

struct Vertex
  GLfloat x, y, z;        //Vertex
  GLfloat r, g, b, a;     //Color
;

我有两个std::vector,一个用于存储顶点属性,一个用于索引:

std::vector<GLushort> indices;
std::vector<struct Vertex> vertices;

在初始化函数中,我用一个简单的绿色三角形填充这些向量:

struct Vertex vertex1;
vertex1.x=1.0;
vertex1.y=0.0;
vertex1.z=0.0;
vertex1.r=0.0;
vertex1.g=1.0;
vertex1.b=0.0;
vertex1.a=1.0;
vertices.push_back(vertex1);
struct Vertex vertex2;
vertex2.x=0.0;
vertex2.y=1.0;
vertex2.z=0.0;
vertex2.r=0.0;
vertex2.g=1.0;
vertex2.b=0.0;
vertex2.a=1.0;
vertices.push_back(vertex2);
struct Vertex vertex3;
vertex3.x=1.0;
vertex3.y=1.0;
vertex3.z=0.0;
vertex3.r=0.0;
vertex3.g=1.0;
vertex3.b=0.0;
vertex3.a=1.0;
vertices.push_back(vertex3);

indices.push_back(1);
indices.push_back(2);
indices.push_back(3);

然后我绑定缓冲区:

glGenBuffers(1, &ibo_elements);
glBindBuffer(GL_ARRAY_BUFFER, ibo_elements);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(struct Vertex), &vertices[0], GL_STATIC_DRAW);

glGenBuffers(1, &elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], GL_STATIC_DRAW);

然后在设置着色器程序和绑定属性名称后,我使用glutDisplayFunc 来运行这个回调:

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

void onDisplay()

  glClearColor(0.0, 0.0, 0.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

  glUseProgram(program);

  glBindBuffer(GL_ARRAY_BUFFER, ibo_elements);
  glVertexAttribPointer(
                        attribute_v_coord,
                        3,
                        GL_FLOAT,
                        GL_FALSE,
                        sizeof(struct Vertex),
                        BUFFER_OFFSET(0)
                        );
  glEnableVertexAttribArray(attribute_v_coord);

  glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
  glVertexAttribPointer(
                        attribute_v_color,
                        4,
                        GL_FLOAT,
                        GL_FALSE,
                        sizeof(struct Vertex),
                        BUFFER_OFFSET(sizeof(GLfloat)*3)
                        );
  glEnableVertexAttribArray(attribute_v_color);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
  int size; glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
  glDrawElements(GL_TRIANGLES, size/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

  glDisableVertexAttribArray(attribute_v_coord);
  glDisableVertexAttribArray(attribute_v_color);
  glutSwapBuffers();

一切都与我之前的工作非常相似。所以我猜测这与数据结构的变化有关。 Valgrind 显示此错误:

==5382== Invalid read of size 4
==5382==    at 0x404EF6A: ??? (in /tmp/glR69wrn (deleted))
==5382==    by 0x870E8A9: ??? (in /usr/lib/libnvidia-glcore.so.325.15)
==5382==    by 0x200000003: ???
==5382==    by 0x404EEBF: ??? (in /tmp/glR69wrn (deleted))
==5382==    by 0x2: ???
==5382==    by 0xAFFC09F: ???
==5382==    by 0x41314D3: ???
==5382==    by 0x40E6FFF: ??? (in /dev/nvidia0)
==5382==    by 0xFFFFFFFE: ???
==5382==  Address 0x28 is not stack'd, malloc'd or (recently) free'd

我没有正确定义顶点属性指针吗?看起来 OpenGL 正在尝试读取一个从未正确设置的浮点数。

【问题讨论】:

嗨里德。您是否正在测试 OpenGL 错误?这是我为完成这项工作而编写的一个快速宏。只需在每次 OpenGL 调用后调用 glErroCheck() 即可。如果你发现什么,请告诉我。 gist.github.com/AThilenius/6315521 glBindBuffer(GL_ARRAY_BUFFER, ibo_elements); 这行简直令人迷惑……GL_ELEMENT_ARRAY_BUFFER 是唯一合乎逻辑的地方来绑定名为“ibo_elements”的东西。 【参考方案1】:

在这种情况下,您应该为每个顶点属性指针使用一个 VBO。您正在提供具有交错数据的单个数据存储。您需要做的就是更改设置顶点属性指针的调用,以便它们具有正确的步幅和基偏移地址。所以这个“颜色缓冲区”VBO(这是一个糟糕的名称选择,因为 OpenGL 已经有一个叫做颜色缓冲区的东西)很可能是问题的根源。 p>

另一个问题,正如在别处提到的,你的元素索引从 1 开始。在这个例子中你有 3 个顶点,元素缓冲区应该填充 0、1、2 的某种组合。在元素缓冲区中有 3 会导致绘制时出现未定义的行为。很多时候,如果您使用无效的索引,驱动程序不会崩溃,不幸的是,GL 没有索引越界的错误状态。通常你只知道在这种情况下出了问题,因为你的屏幕上出现了垃圾。

我担心如果不从 GL 状态机查询这些信息,您甚至都不知道 IBO 中有多少元素。那是糟糕的应用程序设计,很抱歉。您绝对应该事先知道要绘制多少个元素。无论如何,VBO 都应该包装在数据结构或类中(至少包括分配的元素数量),您不想简单地折腾缓冲区对象句柄而不知道它们代表什么。

此外,将浮点值用于顶点颜色可能会很浪费,您几乎不需要它们(GLubyte 和 0-255 通常效果很好)。 1 GLfloat 占用与 4 GLubytes 一样多的内存,而您正在使用 4 GLfloats... 如果您选择使用 xyz 而不是 xyzw 作为顶点位置,使用 4 GLubytes 也有助于对齐。

在较旧的硬件上,4x GLubyte 颜色是硬件 T&L 的“快速路径”。他们仍然在较新的硬件上占用更少的内存,因此几乎在所有情况下它们都是胜利 :)

【讨论】:

哇,你直接指出了我所有的问题。我将glBindBuffer 中的colorbuffer 更改为ibo_elements,并修复了indices,它成功了!我对 OpenGL 很陌生,并且犯了很多菜鸟错误。首先,我从 1 开始索引,因为这就是它在 .obj 格式文件中的工作方式。其次,我对缓冲区对象和顶点属性感到困惑。问题是教程所说的内容之间存在很多不一致之处,并且永远无法确定这些天的最佳实践是什么。单个 VBO 比每个属性都使用一个 VBO 要容易得多,但这不是一些资料所教导的。 单个 VBO 实现可以更简洁。对我来说,在连续的内存块中拥有单个顶点的所有属性更有意义。从逻辑上讲,我喜欢考虑 vtx [n].pos 和 vtx [n].color 而不是 vtx_pos [n] 和 vtx_color [n]。我只能想象教程介绍了多个 VBO 的使用,因为它们打算随着时间的推移添加额外的数组(即从 pos 开始,下一个教程添加颜色,然后是法线,然后是纹理坐标)。如果每个缓冲区仅包含一个属性,则不需要跨步和指针偏移的概念。【参考方案2】:
indices.push_back(0);
indices.push_back(1);
indices.push_back(2);

另外,当你打印出尺寸时你会得到什么?

我也习惯在不需要时解绑缓冲区:

glBindBuffer(GL_ARRAY_BUFFER, 0); //etc

最后……

glBindBuffer(GL_ARRAY_BUFFER, colorbuffer); //what's colorbuffer?

(由于您从同一个 ibo_elements 缓冲区交错,所以这条线应该在这里吗?)

【讨论】:

以上是关于在 OpenGL 中为索引缓冲区对象使用结构会导致段错误的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 顶点数组对象是存储顶点缓冲区名称和索引,还是只存储索引?

带有附加数据的 OpenGL 索引缓冲区对象

OpenGL索引缓冲区对象元素顺序绘制不正确

opengl绘制三角形

在OpenGL ES(Android)中使用带纹理的索引缓冲区有什么意义吗?

OpenGL 帧缓冲区缓慢且自发停止。广泛使用时甚至会导致系统崩溃