Skip to main content
GameDev.net gamedev.net
🔒 Locked

when to add a VBO to a VAO

Started by shaqb4 Jun 29, 2013 at 3:41 PM 9 replies 7.7k views
Original Post
shaqb4
shaqb4

I just started learning OpenGL and decided to use the LWJGL in java to begin. I'm following the tutorials on their wiki, but I'm confused about something in the DrawElements tutorial.

In the tutorial, they create a VBO containing the vertices (four corners) of a rectangle and add it to a VAO. Then they create a VBO that contains the order the vertices are to be drawn, but this one isn't added to the VAO.


Here's the code where they're created:


float[] vertices = 
{
	-0.5f, 0.5f, 0.0f,
	-0.5f, -0.5f, 0.0f,
	0.5f, -0.5f, 0.0f,
	0.5f, 0.5f, 0.0f
};
vertexCount = vertices.length;
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertexCount);
verticesBuffer.put(vertices);
verticesBuffer.flip();
		
byte[] indices =
{
	0, 1, 2,
	2, 3, 0
};
indicesCount = indices.length;
ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
indicesBuffer.put(indices);
indicesBuffer.flip();
		
		
vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);
	addedVBO = GL15.glGenBuffers();
	GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, addedVBO);
	GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
		GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0); //Vbo is added to Vao
	GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
		
unaddedVBO = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, unaddedVBO);
	GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);



Here's the code where they're rendered:


GL30.glBindVertexArray(vaoId);
	GL20.glEnableVertexAttribArray(0);
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, unaddedVBO);
			GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_BYTE, 0);
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
	GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);


Is there a reason one VBO is added to the VAO and one isn't? Would it make a difference if it was added?Also, it occurs to me as I write this that maybe adding one to the VAO allows you to use both VBOs at the same time during rendering?

Promit
Promit

The source of confusion is that whoever wrote this code has no idea what they're doing.

First, a VAO encases the entire vertex attrib state and bound vertex/index buffers. That means that a VAO is configured at load, and you do not call BindBuffer or EnableVertexAttribArray or VertexAttribArrayPointer during rendering. If you do those things, you are actually modifying the VAO every single frame, which defeats the purpose entirely. Draw setup is BindVertexArray, Draw(Arrays/Elements). That's it. The correct call sequence goes something along these lines:

Upload data to buffer:


GenBuffers(1, &vbo1)
BindBuffer(vbo1)
BufferData(ARRAY_BUFFER, ...)
BindBuffer(0)

Plus indices if needed:


GenBuffers(1, &indicesVBO)
BindBuffer(indicesVBO)
BufferData(ELEMENT_ARRAY_BUFFER, ...)
BindBuffer(0)

Create VAO:


GenVertexArrays(1, &vao)
BindVertexArray(vao)
BindBuffer(ELEMENT_ARRAY_BUFFER, indicesVBO or 0)
BindBuffer(ARRAY_BUFFER, vbo1)
EnableVertexAttribArray(1)
VertexAttribPointer(1, ...)
BindBuffer(ARRAY_BUFFER, vbo2) <-- only if you are using multiple VBOs simultaneously, not usually needed
EnableVertexAttribArray(2)
VertexAttribPointer(2, ...)
BindBuffer is not needed or useful here
BindVertexArray(0)

And then to draw:


BindVertexArray(vao)
DrawElements(...)
BindVertexArray(0) if you do not want to use that vao anymore
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
shaqb4
shaqb4

That makes so much more sense, thank you! :)

Just to make sure I understand what's happening, though.

First, you create the 2 VBOs. Then You create the VAO. By binding the VBOs while the VAO is bound, they are added to the VAO. The Attrib calls allow the vertices to be used for rendering. Is that correct or close?

Promit
Promit




By binding the VBOs while the VAO is bound, they are added to the VAO. The Attrib calls allow the vertices to be used for rendering.
Not quite. There is a subtlety here that is a little confusing. BindBuffer(ARRAY_BUFFER) does nothing to a VAO. VertexAttribPointer is the call that creates "a binding", the precise definition of which is laid out by table 23.3 in the spec. You can bind any ARRAY_BUFFER VBO you want during VAO config, but it will have no effect whatsoever until VertexAttribPointer is invoked. At that point, the vertex attrib is linked to that buffer. This is what allows you to pull different vertex attribs from different VBOs as you see fit. BindBuffer(ELEMENT_ARRAY_BUFFER) on the other hand directly affects the current VAO state.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
shaqb4
shaqb4

I think I understand. So the call to VertexAttribPointer only affects the currently bound ARRAY_BUFFER VBO and no other type?

TheChubu
TheChubu

The source of confusion is that whoever wrote this code has no idea what they're doing.

AFirst, a VAO encases the entire vertex attrib state and bound vertex/index buffers. That means that a VAO is configured at load, and you do not call BindBuffer or EnableVertexAttribArray or VertexAttribArrayPointer during rendering.

As far as I know (on OpenGL 3.3), yes you do. You enable the attribute arrays for rendering after binding the VAO.

http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml

glEnableVertexAttribArray enables the generic vertex attribute array specified by index. glDisableVertexAttribArray disables the generic vertex attribute array specified by index. By default, all client-side capabilities are disabled, including all generic vertex attribute arrays. If enabled, the values in the generic vertex attribute array will be accessed and used for rendering when calls are made to vertex array commands such as glDrawArrays, glDrawElements, glDrawRangeElements, glMultiDrawElements, or glMultiDrawArrays.

EDIT: Nope, calling it once its enough as prommit/Brother Bob said biggrin.png

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"   My journals: dustArtemis ECS framework and 
Brother Bob
Brother Bob

The source of confusion is that whoever wrote this code has no idea what they're doing.

AFirst, a VAO encases the entire vertex attrib state and bound vertex/index buffers. That means that a VAO is configured at load, and you do not call BindBuffer or EnableVertexAttribArray or VertexAttribArrayPointer during rendering.

As far as I know (on OpenGL 3.3), yes you do. You enable the attribute arrays for rendering after binding the VAO.

http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml

glEnableVertexAttribArray enables the generic vertex attribute array specified by index. glDisableVertexAttribArray disables the generic vertex attribute array specified by index. By default, all client-side capabilities are disabled, including all generic vertex attribute arrays. If enabled, the values in the generic vertex attribute array will be accessed and used for rendering when calls are made to vertex array commands such as glDrawArrays, glDrawElements, glDrawRangeElements, glMultiDrawElements, or glMultiDrawArrays.

The vertex attribute enable state is part of the VAO, you don't have to enable or disable it again when rendering. See the specification; section 2.10 in both the 3.2 and 4.2 specifiction. I would be extremely surprised if this wasn't the case for 3.3 also.

BornToCode
BornToCode

VAO is just a container for States/index and vertex buffer. You set your VAO, assign it all the states index/vertex buffer. Then whenever you want to render that vbo, you just set it and draw.

TheChubu
TheChubu


The vertex attribute enable state is part of the VAO, you don't have to enable or disable it again when rendering. See the specification; section 2.10 in both the 3.2 and 4.2 specifiction. I would be extremely surprised if this wasn't the case for 3.3 also.

Then I might be doing something wrong, this how I make the draw call:


GL30.glBindVertexArray( vertexArrayId );
 
GL20.glEnableVertexAttribArray( 0 );
GL20.glEnableVertexAttribArray( 1 );
GL20.glEnableVertexAttribArray( 2 );

GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, indexBufferId );
GL11.glDrawElements( GL11.GL_TRIANGLES, indexCount, GL11.GL_UNSIGNED_INT, 0 );
GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, 0 );
 
GL20.glDisableVertexAttribArray( 0 );
GL20.glDisableVertexAttribArray( 1 );
GL20.glDisableVertexAttribArray( 2 );
 
GL30.glBindVertexArray( 0 );
If I comment, say, glEnableVertexAttribArray( 1 ), the normals, I get a dark terrain.
I'll look into it.
EDIT: Hm. You're (both) right. Seems that I wasn't calling it at buffer creation, thus, they never were enabled for rendering. If I call it once like prommit showed, its enough. And checking the Arcsynthesis online book I was learning from, it makes draw calls like I was making them for the first 80 pages, then it reveals VAOs and after that uses only the glBindVertexArray call to render. Thanks for the heads up. I'll read the VAO chapter again when I can.
"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"   My journals: dustArtemis ECS framework and 
shaqb4
shaqb4

Thanks a lot for the explanations. This entire discussion made was very clear and easy to understand. :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.