I was going over my render method and I started thinking. Do I need a VAO? All the tutorials I have looked at go over VAOs, but do I need them if my data is dynamic (changing every frame)?
I understand that VAO is a state object that is s supposed to hold the ‘state’ of Vertex Array Pointers for the VBOs I bind and etc. That if I bind a VAO and then use glEnableAttribPointer, bind a VBO, and glVertexAttribPointer to pointer to the VBO data a record is saved into the VAO. Where I can call the VAO bind function in my render method and everything will know how to draw.
But my confusion kind of comes in when the data I’m using is dynamic and changing every frame. If I have my render method as:
public void render()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle); //Can comment out and has no effect; Never bound until this point
glEnableVertexAttribArray(0);
//Bind the Vertex Buffer and indicate where the Vertex are
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
}
Do I really need the VAO? Would my app be faster with out them or with them? Or am I doing a bad ‘thing’ rebinding all the Vertex Attributes and etc every single time my render method is called? Should I only be rebinding the VAO, does this change (The rebinding) if my data is dynamic?
In OGL3+ you can’t render anything without a VAO. Like you said, VAOs only hold state, specifically which buffers should be read and how they should be interpreted, so regardless of what you’re drawing you need one.
You seem to have missed something though. There’s no need to set up your VAOs each time you render. You should bind them and set them up once. Then when you want to render with them you simply bind them and render.
private void initializeVAO()
{
//Create and generate the VAO
IntBuffer vertexArrayBuffer = BufferUtils.createIntBuffer(1);
glGenVertexArrays(vertexArrayBuffer);
vertexArrayHandle = vertexArrayBuffer.get(0);
//Create and generatate the Vertex Buffer and Index BUffer
IntBuffer buffers = BufferUtils.createIntBuffer(2);
glGenBuffers(buffers);
vertexBufferHandle = buffers.get(0);
indexBufferHandle = buffers.get(1);
//Bind the VAO and set up the Vertex Attributes
glBindVertexArray(vertexArrayHandle);
glEnableVertexAttribArray(0);
//Bind the Vertex Buffer and indicate where the Vertex are
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindVertexArray(0);
}
public void Render()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
The index buffer call is that correct? In the sense that it is separate from the VAO
Also if I’m loading in data every frame or so through a batching system, I assume I would need to switch between multiple VAOs every Nth frame correct?
Much better! The index buffer binding is also stored in the VAO though, so you can just bind it when initializing once. You’re right that it’s a bit weird though.
Tip: There are LWJGL-specific overloaded versions of glGenVertexArrays(), glGenBuffers() and all other glGen***() functions that allocate a single VAO, VBO or whatever.
Display.create(new PixelFormat(24, 0, 0, 0, 0), new ContextAttribs(3, 2)); //For version 3.2
With this code you’ll get an exception if the requested context can not be created, but some drivers can be a bit tricky. For example, some Mac versions do not support OGL 3.2 with compatibility mode. You need to explicitly tell it to use the core profile without compatibility:
Display.create(new PixelFormat(24, 0, 0, 0, 0), new ContextAttribs(3, 2).withProfileCompatibility(false).withProfileCore(true));
This will also remove all deprecated functionality (= disable compatility mode), so if your game is relying on such functions it’ll not work as expected. It’s also a good idea to check if the requested context is correct by checking the OpenGL version using [icode]GLContext.getCapabilities().OpenGL32[/icode].
Ok cool! Kinda of sucky about the tricky drivers though
Can we go back to the VAOs, while I under stand I should just be binding my VAO. What if my data is not static or I need to swap between buffers I fill on the fly?
I read that I need to use multiple VAOs if I wanted to implement a multiple VBO system; I assume this applies to a double buffer or Mapping system
So for example lets say we have / want a double buffer system. So we make our VAOs like so
private void initializeVAO()
{
//Bind the first VAO and vertexBuffer handle
glBindVertexArray(vertexArrayHandle[0]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[0]); //Handle to second vertex buffer; Data in there is dynamic and changes each frame
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle); //No need for 2 index buffer handles; Contain the same data and is static
glBindVertexArray(0);
//Bind the second VAO and vertexBuffer handle
glBindVertexArray(vertexArrayHandle[1]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[1]); //Handle to second vertex buffer; Data in there is dynamic and changes each frame
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle); //No need for 2 index buffer handles; Contain the same data and is static
glBindVertexArray(0);
}
Where our render method looks like
public void endRender()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle[useBuffer]);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
useBuffer = 1 - useBuffer;
}
In the above code we call glBindVertexArray(vertexArrayHandle[useBuffer]), to bind the VAO we wish to use. Now I’m wondering when I make this call is it executing the commands set up in the init VAO method?
Maybe I’m thinking about this wrong but if it is calling them in order, replaying those commands. How does one change the Buffer data on the fly?
For example I know this does not work but I’m hoping this will make sense out of what I’m asking
public void endRender()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle[useBuffer]);
//Does not work but the idea of doing this. How?
//Bind the buffer handle and VBO to use
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[useBuffer]);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer[useBuffer], GL_STREAM_DRAW);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
useBuffer = 1 - useBuffer;
}
To some extent, yes. Your approach when having multiple VBOs is correct. It’s supposed to be faster than manually setting up your vertex attributes each time you want to render.
VAOs do not store all commands. Actually, they don’t even store the actual glBindBuffer() commands. (EDIT: Not entirely true. They DO remember which index buffer is bound.) They only store the vertex attributes commands. That’d be glEnableVertexAttribArray() and glVertexAttribPointer(). The first one of course only enables a specific vertex attribute, but the second one is a bit more complicated. glVertexAttribPointer() reads from the currently bound GL_ARRAY_BUFFER buffer. When the VAO is bound again, it doesn’t actually call glVertexAttribPointer() again. It remembers which buffer was bound when the VAO was created and uses that one again regardless of which one is bound when the VAO is bound for rendering. Binding the VAO doesn’t bind any new buffers or anything like that.
public void endRender()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle[useBuffer]);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[useBuffer]);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer[useBuffer], GL_STREAM_DRAW);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
useBuffer = 1 - useBuffer;
}
There’s nothing wrong with this code. Updating the data of a buffer does not affect the VAO in any way. Neither glBindBuffer() or glBufferData() modify the VAO, so you can update the buffer at any time, even when the VAO is not bound. The VAO just knows which buffers should be read and how the data should be interpreted, not the actual data in the buffer.
[quote]Think of it like this. glBindBuffer sets a global variable, then glVertexAttribPointer reads that global variable and stores it in the VAO. Changing that global variable after it’s been read doesn’t affect the VAO. You can think of it that way because that’s exactly how it works.
This is also why GL_ARRAY_BUFFER is not VAO state; the actual association between an attribute index and a buffer is made by glVertexAttribPointer.
[/quote]
[quote]The index buffer binding is stored within the VAO. If no VAO is bound, then you cannot bind a buffer object to GL_ELEMENT_ARRAY_BUFFER.
[/quote]
I was kinda hoping you’d come back and say yes haha
The reason I asked if it was wrong and if the VAO bind replayed commands is because nothing appears on my screen with that code. It would make sense if that was the case, since it would have already looked at the VBO bound.
//Code that does not render the quad(s) to the screen
public void render()
{
//Use the shader program
glUseProgram(shaderProgramHandle);
//Get the Uniform for the MVP shader
glUniformMatrix4(matrixBufferHandle, false, mvpBuffer);
//Enable the VAO and its attributes
glBindVertexArray(vertexArrayHandle[useBuffer]);
//Attempt to bind data to the VBO; Not sure why this fails to draw anything on screen
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[useBuffer]);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STREAM_DRAW);
GL11.glDrawElements(GL_TRIANGLES, indexDrawCount, GL11.GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
useBuffer = 1 - useBuffer;
}
I can only get something on my screen if I use my ‘static’ (Called before anything; in a class constructor) init method, where glBindBuffer and glBufferData are inside
public void initDebugBuffers()
{
vertexBuffer.clear();
int x = 0;
int y = 0;
Random random = new Random();
//X and Ys for the Quad; The Z is set to 1.0f
for(int i = 0; i < MAX_QUADS; i++)
{
x = random.nextInt(800);
y = random.nextInt(600);
draw(x, y, 32, 32);
}
//Flip the buffer
vertexBuffer.flip();
//Bind the vertex buffer's data
//Gets Quad to appear on screen, flickers but that is because we are only
//binding the data to one of the two VBOs. (vertexBuffer data is used in both handles)
//The VBO handles point to the same data spot; done for test purposes
//Comment out and nothing will appear on screen (the buffer binds are in the render
//method when the below is commented out)
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[0]);
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
//Indices for the Quad
indexDrawCount = MAX_INDEX_COUNT;
indexBuffer.clear();
for(int i = 0, j = 0; i < MAX_QUADS; i++, j+= 4)
{
indexBuffer.put(j);
indexBuffer.put(j + 1);
indexBuffer.put(j + 2);
indexBuffer.put(j + 3);
indexBuffer.put(j);
indexBuffer.put(j + 2);
}
indexBuffer.flip();
//Bind the index buffer's data
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL_STATIC_DRAW);
}
Am I’m missing something? The only thing that I can think of is that my VBO handles in my init VAO method are not set up right. That they are pointing to invalid data. But that really does not make much sense to me either since I can bind the data in the static method, where at that point both VAOs are disabled
private void initializeVAO()
{
//Bind the first VAO
glBindVertexArray(vertexArrayHandle[0]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[0]); //The first Vertex Buffer handle that should be used
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBindVertexArray(0);
//Bind the second VAO
glBindVertexArray(vertexArrayHandle[1]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle[1]); //The second Vertex Buffer handle that should be used
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBindVertexArray(0);
}