OpenGL 顶点数组缓冲区

OpenGL Vertex Array Buffer

我正在尝试学习 LWJGL (OpenGL),但我不得不说我遇到了困难。

我试图在 window 上画一个三角形和一个四边形,我终于成功了。

但是我还有一个问题。
如果这个问题对你来说听起来很愚蠢,提前抱歉,但我无法在网上找到非常详细的教程,所以很难理解,因为这是我第一次使用 OpenGL。

也就是说,这是代码的相关部分:

public void init() {
    vertexCount = indices.length;

    vaoId = GL30.glGenVertexArrays();
    GL30.glBindVertexArray(vaoId);

    vboId = GL15.glGenBuffers();
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, coords, GL15.GL_STATIC_DRAW);
    GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

    idxVboId = GL15.glGenBuffers();
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, idxVboId);
    GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);

    GL30.glBindVertexArray(0);
}

public void render() {
    GL30.glBindVertexArray(vaoId);
    GL20.glEnableVertexAttribArray(0);
    GL11.glDrawElements(GL11.GL_TRIANGLES, vertexCount, GL11.GL_UNSIGNED_INT, 0);

    GL20.glDisableVertexAttribArray(0);
    GL30.glBindVertexArray(0);
}

假设程序 运行 为 60 fps。
这意味着渲染方法每秒被游戏循环调用 60 次。

渲染方法步骤为:

  1. glBindVertexArray(vaoId)
  2. glEnableVertexAttribArray(0)
  3. 绘制四边形
  4. glDisableVertexAttribArray(0)
  5. glBindVertexArray(0)

我的问题是:是否需要每次都调用第1、2、4、5步?如果是,为什么?

同样的问题也适用于 init() 方法的最后一行 (glBindVertexArray(0))。

对不起我的英语,这不是我的母语。
提前致谢。

My question is: Is it necessary to call steps 1, 2, 4 and 5 every time? If yes why?

不,不是。 OpenGL 被设计为状态机。您有一个包含全局状态和您创建的对象(如 VAO、VBO)的 GL 上下文。对象本身可以包含数据和每个对象的状态。重要的是在某些特定的 GL 函数调用时设置的状态,它以某种方式取决于其中一些状态值。

glDrawElements() 的情况下,顶点数组指针和启用位以及 GL_ELEMENT_ARRAY_BUFFER 绑定与为绘制调用提供输入数据相关。 (影响绘图的所有其他状态,如纹理绑定、着色器程序、深度测试设置……也相关,但我们不在这里关注这些。)所有这些状态实际上都封装在顶点数组对象 (VAO) 中。

使用 OpenGL 的状态机设计,除非明确更改,否则状态保持不变。由于您似乎只绘制一个对象并且从不需要不同的属性指针或元素数组,您只需设置一次并将您的 render() 方法减少为仅 glDrawElements() 调用。这当然假设您的渲染循环中没有其他代码会影响状态更改。

值得注意的一点:VAO确实存储了每个属性数组的启用,所以你的第2步属于VAO初始化,第4步在这个方案中完全没有用。

这也意味着当你想管理不同的对象时,你可以为每个对象创建一个 VAO、VBO 和 EBO,你的渲染方法将只循环对象,设置适当的 VAO,并发出绘制调用:

for every object obj
    glBindVertexArray(obj.vao);
    glDrawElements(...values depending on obj...);

绑定 VAO 0 实际上在现代 OpenGL 中从来都不是严格要求的。在绘制调用时,您将始终必须绑定一些 VAO,因此无论如何您最终都必须稍后再次绑定非 0 VAO。 unbinding 提供的唯一价值是它可以防止对某些对象的意外更改。由于传统的 OpenGL API 总是使用 绑定目标 的间接方式来修改对象,因此可以创建当时不应该绑定的对象被绑定的情况,导致难以调试明显不相关的代码部分之间的错误行为。