Search code examples
javaopengllwjglvbovao

OpenGL Vertex Array Buffer


i'm trying to learn LWJGL (OpenGL) and i have to say i'm having a hard time.

I was trying to draw a triangle and a quad on the window and i finally managed to do it.

But i still have a question.
Sorry in advance if the question sounds stupid to you but i haven't been able to find a very detailed tutorial on the web so it's hard to understand since it's the first time i use OpenGL.

That being said, this is the relevant part of code:

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);
}

Let's say that the program is running at 60 fps.
This means that the render method is being called by the game loop 60 times every second.

The render method steps are:

  1. glBindVertexArray(vaoId)
  2. glEnableVertexAttribArray(0)
  3. Draw the quad
  4. glDisableVertexAttribArray(0)
  5. glBindVertexArray(0)

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

And the same question applies to the last line of the init() method (glBindVertexArray(0)).

Sorry for my english, it's not my mother tongue.
Thanks in advance.


Solution

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

    No, it is not. OpenGL is designed as a state machine. You have a GL context which contains global state, and objects which you create (like VAOs, VBOs). The objects itself can contain data and per-object state. What matters is the state which is set at the time of some particular GL function call which somehow depends on some of these state values.

    In the case of glDrawElements(), the vertex array pointers and enable bits as well as the GL_ELEMENT_ARRAY_BUFFER binding is relevant for providing the input data for the draw call. (All other state that is influencing the drawing, like texture bindings, shader programs, depth test setting, ... is relevant as well, but lets not focus on these here.). All that state is actually encapsulated in the Vertex Array Object (VAO).

    With the state machine design of OpenGL, state stays the same unless it is explicitely changed. Since you seem to draw only a single object and never need different attrib pointers or element arrays, you simply can set these up once and reduce your render() method to just the glDrawElements() call. This of course assumes that no other code in your render loop does any influencing state changes.

    One thing worth noting: The VAO does store the enables per attribute array, so your step 2 belongs into the VAO initalization, and step 4 is completely useless in this scheme.

    This also means that when you want to manage different objects, you could create a VAO, VBO and EBO per object, and your render mothod would just loop over the objects, set the appropriate VAO, and issue the draw call:

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

    Binding VAO 0 is actually never strictly required in modern OpenGL. You will always have to have some VAO bound at the time of the draw call, so you eventually have to bind a non-0 VAO later again anyway. The only value such unbinding is providing is that it prevents accidental changes to some objects. Since the tradition OpenGL API always uses the indirection of biding targets to modify an object, one can create situations where objects are bound which are not supposed to be bound at that time, resulting in hard to debug misbehaviors between apparently unrelated code parts.