Doubts about VBO

Hello!

I’m still reading the redbook but I got some doubts about how VBO works (and it’s buffers!) and I’ve google it but can’t find a answer. =/

Ok! My doubts:

  • GL_ARRAY_BUFFER is a single buffer? I can only store a single array on it?
    If I run this:

GLuint bufPointer[4];
glGenBuffers(4, bufPointer);

glBindBuffer(GL_ARRAY_BUFFER, bufPointer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(box1), [b]box1[/b], GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, bufPointer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(box2), [b]box2[/b], GL_STATIC_DRAW);
 
The second time I run the glBufferData will it override the first one? If so, I'll need to attach more GLfloat data to [b]box1[/b] (running it only once) if I want to create other objects in my scene? Or there is another way?


I'm using VBO, but I'm trying to have 2 cubes in my scene. I tried to create one cube using VBO (buffer and stuff) and the other using glVertexPointer (pointing to the vertex array) and glDrawElements (to the indices). But it doesn't work, I mean, it does, but only the VBO object, my second cube doesn't, it just doesn't appear. If I desable the glGenBuffer. Binder and stuff it works, but both on it doesn't. Any ideas?

my code:



#define BUFFER_OFFSET(offset) ((GLvoid *) NULL + offset)
#define restartIndex	0xffff
#define boxIndexSize	6*8

static GLfloat spin = 0.0;

static GLfloat box1[boxIndexSize] = 
{//   R    G    B		   X	  Y	    Z
	 1.0, 0.0, 0.0,		 -1.0,  -1.0,  1.0,		// 0	-- RED
	 0.0, 1.0, 0.0,		  1.0,  -1.0,  1.0,		// 1	-- GREEN
	 0.0, 0.0, 1.0,		  1.0,   1.0,  1.0,		// 2	-- BLUE
	 1.0, 1.0, 1.0,		 -1.0,   1.0,  1.0,		// 3	-- WHITE
	 0.5, 0.5, 0.5,		 -1.0,  -1.0, -1.0,		// 4	-- GRAY
	 1.0, 1.0, 0.0,		  1.0,  -1.0, -1.0,		// 5	-- YELLOW
	 1.0, 0.0, 1.0,		  1.0,   1.0, -1.0,		// 6	-- MAGENT
	 0.0, 1.0, 1.0,		 -1.0,   1.0, -1.0		// 7	-- CYAN
};  

static GLfloat box2[boxIndexSize] = 
{//   R    G    B		   X	  Y	    Z
	1.0, 0.0, 0.0,		  3.0,   3.0,  5.0,		// 8	-- RED
	0.0, 1.0, 0.0,		  5.0,   3.0,  5.0,		// 9	-- GREEN
	0.0, 0.0, 1.0,		  5.0,   5.0,  5.0,		// 10	-- BLUE
	1.0, 1.0, 1.0,		  3.0,   5.0,  5.0,		// 11	-- WHITE
	0.5, 0.5, 0.5,		  3.0,   3.0,  3.0,		// 12	-- GRAY
	1.0, 1.0, 0.0,	          5.0,   3.0,  3.0,		// 13	-- YELLOW
	1.0, 0.0, 1.0,		  5.0,   5.0,  3.0,		// 14	-- MAGENT
	0.0, 1.0, 1.0,	  	  3.0,   5.0,  3.0		// 15	-- CYAN
};  

GLubyte indices[][4] = { 
	{ 0, 1, 2, 3 }, 
	{ 5, 6, 7, 4 },
	{ 4, 0, 3, 7 },
	{ 5, 6, 2, 1 },
	{ 0, 4, 5, 2 }, 
	{ 7, 3, 2, 6 }
};


void init(void) {	
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glShadeModel(GL_SMOOTH);	
	glEnable(GL_DEPTH_TEST);
	
	glEnableClientState(GL_VERTEX_ARRAY);
		
	GLuint bufPointer[2];
	glGenBuffers(2, bufPointer);
	glBindBuffer(GL_ARRAY_BUFFER, bufPointer[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(box1), box1, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufPointer[1]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	glPushMatrix();
	
	glRotated(spin, 1, 1, 1);
	glScaled(50, 50, 50);
	
	glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*6, BUFFER_OFFSET(sizeof(GLfloat)*3));
	glDrawElements(GL_QUADS, boxIndexSize, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
	
	glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*6, box2+3);
	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indices);
	
	glPopMatrix();
	glutSwapBuffers();
}

void process(void){
	spin += 0.01; 
	if (spin > 360.0) spin = spin - 360.0; 
	glutPostRedisplay();
}

void reshape(int w, int h){
	glViewport(0, 0, (GLdouble) w, (GLdouble) h); 
	glMatrixMode(GL_PROJECTION); 
	glLoadIdentity(); 
	glOrtho(230-w, w, 230-h, h, -1000.0, 1000.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

int main(int argc, char** argv){
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );	
	glutInitWindowSize(450, 450);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("OpenGL Test");
	
	init();
	
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(process);
	
	glutMainLoop();
	return 0;
}



Hi,

  1. You need to disable the binding if you want to use non buffer rendering by glBindBuffer(0)
  2. You can create as many buffer as you like, each is used for a different cube, or create on large buffer and use offsets.
  3. GL_ARRAY_BUFFER is used to supply the data (pos…) and GL_ELEMENT_ARRAY_BUFFER is used to supply the indices, so if you are rendering using index array you need to bind them both for a single cube rendering.

Ido

Thanks #Ido Ilan for explaining!

You said I can create as many buffers I like, but how do I say to glVerterPointer and glColorPointer where to point?

The redbook, as far as I’ve read only show using BUFFER_OFFSET(0) (or just 0). If in the ini(); I create 2 buffers (one for each cube) and then in the display(); function I want to draw both cubes I’ll need to point the glVertexPointer (and color) to a different buffer, right? How do I do that?

Thanks!
Hope it’s not a stupid question! =D

Whenever you have a buffer bound to GL_ARRAY_BUFFER (via glBindBuffer), then the “pointer” you provide glVertexPointer, glColorPointer, and friends is an offset into that specific buffer.

So if you want to tell the vertex arrays to pull from another buffer, just bind a different buffer to GL_ARRAY_BUFFER, and then call gl*Pointer again with the appropriate offset into that buffer.

The redbook, as far as I’ve read only show using BUFFER_OFFSET(0) (or just 0).

I haven’t looked at the latest redbook lately so I’ll take your word for it.

If that’s the case, then it means they are using separate buffer objects for each vertex attribute, where each vertex attribute starts at the beginning of its own buffer. For example: buffer #0 might contain vertex positions, buffer #1 might contain vertex colors, buffer #2 might contain normals, etc.:


Buffer #0: PPPPPPPPP
Buffer #1: CCCCCCCCC
Buffer #2: NNNNNNNNN

With this model, you have to do a glBindBuffer of the appropriate buffer to GL_ARRAY_BUFFER and then call the relevant pointer call with “offset 0” (because each attribute starts at the beginning of its own buffer). Then repeat for the other vertex attributes:


   glBindBuffer ...positions buffer...
   glVertexPointer( ..., 0, BUFFER_OFFSET(0) );
   glBindBuffer ....colors buffer...
   glColorPointer( ..., 0, BUFFER_OFFSET(0) );

Another style of VBO usage “interleaves” the vertex attributes into a shared buffer:


Buffer #0: PCNPCNPCNPCN....

With this memory organization, you’d see something more like this to set up the batch to draw:


   glBindBuffer ...shared vertex attribute buffer...
   glVertexPointer( ..., 19, BUFFER_OFFSET(0) );
   glColorPointer( ..., 19, BUFFER_OFFSET(12) );
   glNormalPointer( ..., 19, BUFFER_OFFSET(16) );

with appropriate setting of the “stride” parameter in the gl*Pointer calls (19 in this example).

VBOs are actually even more flexible than that. You could put each vertex attribute in the same buffer but not interleave them (not recommended), you could even pack your DrawElements index lists into the same buffer with the vertex attributes, and/or you could even pack the data for multiple draw calls into the same buffer!

But don’t concern yourself with these right now. Just get something working first, understand it, and then tweak from there.

If in the ini(); I create 2 buffers (one for each cube) and then in the display(); function I want to draw both cubes I’ll need to point the glVertexPointer (and color) to a different buffer, right? How do I do that?

Just bind the buffer for the 2nd cube (glBindBuffer) and then call your vertex attribute pointer setup functions again (gl*Pointer).

Note: when you say you “create 2 buffers (one for each cube)”, that likely implies that either:

[ol][li] there is only 1 vertex attribute per object (e.g. vertex positions), OR [*] there are multiple vertex attributes (e.g. positions and colors), and you are interleaving them in a single buffer object[/ol][/li]Is this the case? Or is there another misunderstanding at work here.

As an aside, I “highly” recommend you start with a working client arrays test program that draws a cube, and then modify that to support VBOs. That lets you make small incremental changes and clarify your understanding. With this approach, you have less risk of “getting off into the weeds”.

  1. You can create single large buffer and divide it between the cubes and use an offset in glxxxPtr, you can even use one large array for pos,color,tex and each attribute is an offset in the array.
  2. When you bind a buffer, all calls are now “routed” to this binded buffer, so you bind, setup pointers , draw, unbind each object.

Just read simple tutorials:
http://www.opengl.org/wiki/VBO_-_just_examples
http://www.songho.ca/opengl/gl_vbo.html

Ido

@Ido Ilan and @Dark Photon, thank you for you reply!

I thought that I could send all my array (containing normal, color, vertex and stuff) to the buffer and just keep working with all of the from there. Again, that I could send more then one array to the graphic card and be able to delete that vertex from the client side.

Haven’t realize I have to bind the array every time I would like to draw it. Nice to know!

I thought VBO speed gain would be not need to load the object (assuming I got one object per array) every time.

So is this code correct? :


display() {

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
	

// BOX 2 
glBufferData(GL_ARRAY_BUFFER, sizeof(box1), box1, GL_STATIC_DRAW);	
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicesBox1), indicesBox1, GL_STATIC_DRAW);
	
glColorPointer(3, GL_FLOAT, 6*sizeof(GLfloat), BUFFER_OFFSET(0));
glVertexPointer(3, GL_FLOAT, 6*sizeof(GLfloat), BUFFER_OFFSET(3*sizeof(GLfloat)));
	
// DRAWING BOX 1
glDrawElements(GL_QUADS, 48, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
	

// BOX 2
glBufferData(GL_ARRAY_BUFFER, sizeof(box2), box2, GL_STATIC_DRAW);	
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicesBox2), indicesBox2, GL_STATIC_DRAW);
	
glColorPointer(3, GL_FLOAT, 6*sizeof(GLfloat), BUFFER_OFFSET(0));
glVertexPointer(3, GL_FLOAT, 6*sizeof(GLfloat), BUFFER_OFFSET(3*sizeof(GLfloat)));
	
// DRAWING BOX 2
glDrawElements(GL_QUADS, 48, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));

glPopMatrix();
glutSwapBuffers();

}


I just put that in my display() function and it works but I’m would like know if it’s the correct usage.

Thanks again!

Haven’t realize I have to bind the array every time I would like to draw it.

No, you don’t. You need to bind the buffer object. You don’t need to call glBufferData on it.

So is this code correct? :

There is no call to glBindBuffer there.

Thanks! Now I got it! =D

Now in the display() method I only have Binders and gl*Pointer() and a draw! No more glBufferData!

Thank you @Alfonse Rainheart!

Now I’ll keep reading the book.
Thanks everyone!