使用 IBO/EBO 时,程序仅在我调用 glBindBuffer 以在创建 VAO 后绑定 IBO/EBO 时才有效

Posted

技术标签:

【中文标题】使用 IBO/EBO 时,程序仅在我调用 glBindBuffer 以在创建 VAO 后绑定 IBO/EBO 时才有效【英文标题】:When using IBO/EBO, program only works when I call glBindBuffer to bind the IBO/EBO AFTER creation of the VAO 【发布时间】:2015-07-02 05:27:46 【问题描述】:

由于某种原因,该程序仅在我创建 VAO 之后再次绑定 IBO/EBO 时才有效。我在线阅读,和多个 SO 帖子,glBindBuffer 仅绑定当前缓冲区,并且它将其附加到 VAO。我认为 glVertexAttribPointer 是将数据附加到 VAO 的函数。

float points[] = 

   -0.5f,  0.5f, 0.0f, // top left      = 0
    0.5f,  0.5f, 0.0f, // top right     = 1
    0.5f, -0.5f, 0.0f, // bottom right  = 2
   -0.5f, -0.5f, 0.0f, // bottom left   = 3

;

GLuint elements[] = 

    0, 1, 2,
    2, 3, 0,
;

// generate vbo (point buffer)
GLuint pb = 0;
glGenBuffers(1, &pb);
glBindBuffer(GL_ARRAY_BUFFER, pb);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

// generate element buffer object (ibo/ebo)
GLuint ebo = 0;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

// generate vao
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);


glBindBuffer(GL_ARRAY_BUFFER, pb);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);


glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); // when I bind buffer again, it works


glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

如果我没有第二个 glBindBuffer,程序就会崩溃。我只想知道为什么我必须在创建 VAO 之后再次调用 glBindBuffer,当调用 glBindBuffer 时只会使缓冲区成为其他函数的活动缓冲区。

Pastebin (FULL CODE)

【问题讨论】:

【参考方案1】:

问题在于您的初始调用顺序。 GL_ELEMENT_ARRAY_BUFFER 绑定是 VAO 状态的一部分。看这个序列:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

由于GL_ELEMENT_ARRAY_BUFFER 绑定是VAO 状态的一部分,序列末尾的glBindVertexArray() 调用将建立GL_ELEMENT_ARRAY_BUFFER 缓冲区绑定,它是您正在绑定的VAO vao 的一部分。其中,由于您刚刚创建了 vao,因此是 0。所以在这个序列的最后,你没有绑定元素数组缓冲区。

或者,从不同的方向来看,当您在开始时调用 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 时,VAO 0 被绑定。所以这个绑定成为了 VAO 0 状态的一部分。然后,当您绑定不同的 VAO vao 时,您将丢失该绑定,并替换为 VAO vao 状态的一部分的绑定。

要使这项工作如您所愿,您需要在绑定 VAO 之后进行初始 GL_ELEMENT_ARRAY_BUFFER 绑定。然后它成为 VAO 状态的一部分,以后每次绑定 VAO 时都会重新建立:

GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

【讨论】:

【参考方案2】:

如果您查看规范,顶点数组对象状态不包括元素缓冲区对象。

确实,启用 VAO 不会设置元素绑定点。

【讨论】:

抱歉直言不讳,但这是完全错误的。元素缓冲区绑定 VAO 状态的一部分。 也许我需要再次阅读OpenGL规范......最近我无法写出关于OpenGL的正确答案。啜泣。 如果您手边有 OpenGL 3.3 规范文档,请查看第 281 页的表 6.5。该表名为“顶点数组对象状态”,包含 ELEMENT_ARRAY_BUFFER_BINDING

以上是关于使用 IBO/EBO 时,程序仅在我调用 glBindBuffer 以在创建 VAO 后绑定 IBO/EBO 时才有效的主要内容,如果未能解决你的问题,请参考以下文章

iCarousel 上的 carouselCurrentItemIndexDidChange 仅在我滑动时被调用

仅在 firebase 调用完成时加载布局

仅在 Visual Studio 中激活时才调用后台任务

iOS UITableView reloadData 仅在我第二次调用我的 reload 函数时刷新表

为啥 xmlHttpRequest.Open 仅在我输入字符串时才起作用?

如何仅在 android 应用程序中调用 ParsePush.SubscribeInBackground 方法一次?