【问题标题】:OpenGL Vertex Array BufferOpenGL顶点数组缓冲区
【发布时间】:2016-05-08 15:39:45
【问题描述】:

我正在尝试学习 LWJGL (OpenGL),我不得不说我很难。

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

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

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

【问题讨论】:

  • 应该有很多以前的问题涵盖了 VAO 的工作原理。例如这里:stackoverflow.com/questions/26552642/…。简而言之:不,你不需要这些。您只需要在 render 方法的开头绑定 VAO。如果你只使用一个 VAO,你可以保持绑定。状态不会自发改变,所以如果你从不绑定不同的 VAO(包括 0),就不需要再次绑定。

标签: java opengl lwjgl vbo vao


【解决方案1】:

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

不,不是。 OpenGL 被设计成一个状态机。您有一个 GL 上下文,其中包含全局状态和您创建的对象(如 VAO、VBO)。对象本身可以包含数据和每个对象的状态。重要的是在某些特定的 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...);

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-01
    相关资源
    最近更新 更多