VBO that can hold multiple VBO's?

Well recently i have been trying to see if it is possible to use a single vbo to hold multiple vbo’s. For example I’m trying to make a Text rendering sub that uses vbo however i do not want to bind a new vbo unless i truly have to for each symbols XY, UV, and RBGA. So I’m a little confused on if this is possible and if not then why not? Also If it is not possible any other way i can do things to make it better?

Here is the source for my text rendering class that I’m trying to get to use multiple VBO’s into a single VBO.

void create_text_vertex(text *str, widget *control)//must be done every text change
{
	float x2,x = 0;
	float y2,y = 0;
	char *p = str->data;
	float w, h;
	float textx,texty;
	float texx,texy,texw,texh;
	GLuint index[4] = {0,1,2,3};

	textx = str->xy.x + control->actualpos.x;
	texty = str->xy.y + control->actualpos.y;

	if(str->textbuf.buffer == NULL){
		str->textbuf.buffer = (vertex_array *)calloc(1, 16 * sizeof(vertex_array));
		str->size = 16;
		str->count = 0;
	}

	while(*p){
		texx = (float)character[*p].x / 256;
		texw = (float)(character[*p].x + character[*p].glyph_bitmap->bitmap.width)/ 256;
		texy = (float)character[*p].y / 128;
		texh = (float)(character[*p].y + character[*p].glyph_bitmap->bitmap.rows)/ 128;
		x2 = (x + character[*p].glyph_bitmap->left);
		y2 = (y - character[*p].glyph_bitmap->top) ;
		w = (character[*p].glyph_bitmap->bitmap.width);
		h = (character[*p].glyph_bitmap->bitmap.rows + str->resize);
		y = texty; //reset Y to the original other wise text becomes out of control XD.

		//Moves a certain amount of space After the symbol based on glyph set size.

		x += (character[*p].advancex >> 6) + str->resize;
		y += (character[*p].advancey >> 6) + 12;

		if (!character[*p].glyph_bitmap->bitmap.width || !character[*p].glyph_bitmap->bitmap.rows){
			p++; continue;
		}

		if(str->count >= str->size){
			str->size = (uint16)next_power_of_two(str->size + 2);
			text_resize_buffer(str->textbuf.buffer,str->size);
		}

		/*index 0*/
		str->textbuf.buffer[str->count].vertex[0].u = texx; str->textbuf.buffer[str->count].vertex[0].v = texy;
		str->textbuf.buffer[str->count].vertex[0].x = x2; str->textbuf.buffer[str->count].vertex[0].y = y2;
		str->textbuf.buffer[str->count].vertex[0].r = str->col.r; str->textbuf.buffer[str->count].vertex[0].b = str->col.b; str->textbuf.buffer[str->count].vertex[0].g = str->col.g; str->textbuf.buffer[str->count].vertex[0].a = str->col.a;

		/*index 1*/
		str->textbuf.buffer[str->count].vertex[1].u = texw; str->textbuf.buffer[str->count].vertex[1].v = texy;
		str->textbuf.buffer[str->count].vertex[1].x = x2 + w; str->textbuf.buffer[str->count].vertex[1].y = y2;
		str->textbuf.buffer[str->count].vertex[1].r = str->col.r; str->textbuf.buffer[str->count].vertex[1].b = str->col.b; str->textbuf.buffer[str->count].vertex[1].g = str->col.g; str->textbuf.buffer[str->count].vertex[1].a = str->col.a;

		/*index 2*/
		str->textbuf.buffer[str->count].vertex[2].u = texx; str->textbuf.buffer[str->count].vertex[2].v = texh;
		str->textbuf.buffer[str->count].vertex[2].x = x2; str->textbuf.buffer[str->count].vertex[2].y = y2 + h;
		str->textbuf.buffer[str->count].vertex[2].r = str->col.r; str->textbuf.buffer[str->count].vertex[2].b = str->col.b; str->textbuf.buffer[str->count].vertex[2].g = str->col.g; str->textbuf.buffer[str->count].vertex[2].a = str->col.a;

		/*index 3*/
		str->textbuf.buffer[str->count].vertex[3].u = texw; str->textbuf.buffer[str->count].vertex[3].v = texh;
		str->textbuf.buffer[str->count].vertex[3].x = x2 + w; str->textbuf.buffer[str->count].vertex[3].y = y2 + h;
		str->textbuf.buffer[str->count].vertex[3].r = str->col.r; str->textbuf.buffer[str->count].vertex[3].b = str->col.b; str->textbuf.buffer[str->count].vertex[3].g = str->col.g; str->textbuf.buffer[str->count].vertex[3].a = str->col.a;
		str->count++;
		p++;
	}

	glGenBuffers(1,&str->textbuf.vertex_buffer);
	glGenBuffers(1,&str->textbuf.index_buffer);

	glBindBuffer(GL_ARRAY_BUFFER,str->textbuf.vertex_buffer);
	glBufferData(GL_ARRAY_BUFFER,str->size *( 4 * sizeof(vertex_t)),str->textbuf.buffer,GL_STATIC_DRAW);//fill up the array with vertex and color-data

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,str->textbuf.index_buffer);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,4 * sizeof(GLuint),index,GL_STATIC_DRAW);//this one with indices
}
void text_draw_beta(text *str, widget *parent)
{
	char *p = str->data;
	uint16 count = 0;
	uint32 use = 0;

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);

	glColor4f(str->col.r , str->col.g , str->col.b , str->col.a);// set the image color properties, 1 being highest 0.0000 being lowest
	glBindTexture(GL_TEXTURE_2D, texture);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, str->textbuf.index_buffer);
	glIndexPointer(GL_UNSIGNED_INT,sizeof(GLuint),0);

	glBindBuffer(GL_ARRAY_BUFFER, str->textbuf.vertex_buffer);

	while(*p){
		if (!character[*p].glyph_bitmap->bitmap.width || !character[*p].glyph_bitmap->bitmap.rows){
			p++; continue;
		}
		glVertexPointer(2, GL_FLOAT, sizeof(struct vertex_t), 0);
		glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex_t), (GLvoid *)offsetof(struct vertex_t, u));
		glColorPointer(4, GL_FLOAT, sizeof(struct vertex_t), (GLvoid *)offsetof(struct vertex_t, r));

		glDrawElements(GL_TRIANGLE_STRIP, 4 ,GL_UNSIGNED_INT,0); //GL_TRIANGLE_STRIP
		count++;
		p++;
	}

	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
}
struct multi_buffer{
	GLuint vertex_buffer; //texture vertex buffer.
	GLuint index_buffer; //index buffer.
	vertex_array *buffer;
};

struct vertex_t {
	float x, y;
	float u, v;
	float r, g, b, a;
};

struct vertex_array {
	vertex_t vertex[4];
};

hopefully someone can at least point me in the right direction.

Well recently i have been trying to see if it is possible to use a single vbo to hold multiple vbo’s.

That is a contradiction in terms. Buffer objects hold a single piece of memory. They cannot hold multiple pieces of memory.

Now, a single buffer object can hold the vertex data for multiple objects. But that’s a completely different matter entirely. Is that what you’re trying to do? Because your “one character per buffer object” strategy isn’t going to work.

text rendering class

Please stop using C++ like it’s C with Classes. Your textbuf.buffer is really just a sad (and memory leak prone) re-implementation of std::vector<vertex_array>. If this is, in fact, C code, and you were using “class” colloquially, then where exactly does characters come from in create_text_vertex?

There is simply too much wrong with your code to tell you how to fix it. The only real “right direction” I could point you to would be to throw away both versions and start from a good Intro to C++ book, before even thinking about OpenGL further.

However, here are a couple of things:

glIndexPointer(GL_UNSIGNED_INT,sizeof(GLuint),0);

This function doesn’t do what you think it does. It has nothing to do with the indices of vertices, for example.

opps XD Well Vertex arrays was what i meant sorry about that also, I had no intentions of using one character per buffer that’s why i wanted a way to use 1 vbo to allow multiple vertex arrays. So thank you for giving me the glindexpointer function. Close the topic please?

you can use a VBO with a lot of different vertex attributes as long as the memory where the vertices are used (inside VBO) fit the vertex attributes you are currently using

If you want to reuse different attributes with the same data its possible but I never used that way and its probably not faster, and storing all vertices not packed as XYZ, NORMAL COLOR etc… but XYZ XYZ XYZ NORMAL NORMAL NORMAL COLOR COLOR COLOR, will be more complicated to handle in the VBO when you want to update for example some part of it without the need to lock all the buffer…

[QUOTE=Sylvain Rochette;1253201]you can use a VBO with a lot of different vertex attributes as long as the memory where the vertices are used (inside VBO) fit the vertex attributes you are currently using

If you want to reuse different attributes with the same data its possible but I never used that way and its probably not faster, and storing all vertices not packed as XYZ, NORMAL COLOR etc… but XYZ XYZ XYZ NORMAL NORMAL NORMAL COLOR COLOR COLOR, will be more complicated to handle in the VBO when you want to update for example some part of it without the need to lock all the buffer…[/QUOTE]

Well basically i have different positions and such for each symbol in my string and i want to have a single vbo that holds all the vertexs, colors, and texture positions for each symbols

so basically my vbo is setup like this

H<(XY,UV,RGBA) * 4> e<(XY,UV,RGBA) * 4>l<(XY,UV,RGBA) * 4>l<(XY,UV,RGBA) * 4>o<(XY,UV,RGBA) * 4>
*4 being X[4] y[4] etc.
So that be a vertex array of a string saying hello. How would i go about this is what i wanted to know while using a single vbo as all i can get it to load is the H XD.

Edit: Before going to my original reply below, lets talk briefly about the Hello example.

  • I would count “Hello” as a single object / draw call. What I call object can have unique transformation and can get culled individually.
  • You can draw it with a single draw call using indexed primitives using glDrawElemenst(GL_TRIANGLES, …)
  • Each letter has 4 vertices and one quad = two triangles = 6 indices
  • Every time you add a new letter to the text, you append 4 vertices to VBO and 6 indices to the index buffer

Vertices for two letters (“Hi” for example)


3---2  7---6
| / |  | / |
0---1  4---5

Those would use indices 0, 1, 2, 0, 2, 3, /* H / 4, 5, 6, 4, 6, 7, / i */

Since changing size of GL buffer sizes is a bad idea, text buffer is typically created with fixed size vertex and index buffers big enough to hold some reasonable maximum amount of text (say 2000 letters).

Notice that while contents of vertex buffer depend on the text, index buffer contents will always be constant. Thus you can prepare contents of index buffer when you create the text buffer, and all you need to do when appending a letter is to append the vertex data to the VBO.

Original reply:

[ol]
[li]You can put multiple vertex arrays to a single VBO, interleaved or not.[/li]This is basic feature of glVertexAttribPointer, shown below.
[li]You can also put multiple sets of (interleaved or not) vertex arrays into a single VBO, for multiple objects.[/li]This is slightly more involved, as you need to tell GL which set you want to use, and there several way to do it.
Edit: This does not exactly answer the original question though…
[/ol]

Read the documentation for glVertexAttribPointer, a few times. This is the function that you use to tell GL exactly where each attribute gets the values from. Below is some notes that assume 2 objects, first with 10 vertices, second with 20, and vertex format with vec3 position and vec3 color, interleaved.

  • You need to have the correct VBO bound before you call VertexAttribPointer
  • Stride is the total size of the vertex format. For the example setup described above, stride would be 3 * 4 + 3 * 4 = 24 bytes (size of vec3 plus size of vec3).
  • Offset for the first attribute (position in my example) is always 0.
  • Offset for second attribute (color in my example) is offset of previous attribute + size of previous attribute + possibly additional padding required for alignment (*). In the example, offset of the color would be 0 + 12 + 0 = 12.

With those pieces of information, when you want to draw first object, you’d call


// Assuming you already have the correct VBO bound
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, 0); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, 12); glEnableVertexAttribArray(1);
// Draw the object

If you can, you probably should use VAO and split the above to VAO setup code and draw code with VAO bind.(*) Offsets must be aligned to the size of the type, so GL_FLOAT attributes for example must be aligned to 4 bytes. Typically people don’t need padding in vertex format, but if there is any, it is included in offsets and stride.

Now for the second part: In order to draw multiple objects from a single VBO, it depends on the GL version and supported extensions how you should do it. NOTE: Below is only a subset of possibilities, newer GL has even more options…

A) Using base vertex:

  • If you have GL 3.2 or GL_ARB_draw_elements_base_vertex extension, you can use glDrawElementsBaseVertex() which has an extra argument to tell the first vertex
  • In the example base vertex for first object would be 0, and 10 for the second object
  • You only need to do the vertex attrib pointer setup (as above, directly with vertexAttribPointer calls, or, if possible, ideally by binding VAO) when switching the VBO or vertex format.
  • You can put as many objects as you want to a single VBO and no need to change attribute pointer setup, if they share same vertex format.
  • Note that index values for each object start from 0.
  • Each object typically have their own indices, so indices pointer (offset to IBO) passed to draw call is unique per mesh

B) Without base vertex, using baked indices:

  • If you cannot use base vertex (no GL 3.2 or GL_ARB_draw_elements_base_vertex - for example, OpenGL ES), you can “bake” base vertex into the indices
  • The first object would start indices from 0, the second object would start indices from 10
  • Like option A, you only update attribute pointer setup when switching VBO or vertex format
  • Indices for each object start from where previous object ended. This means you can easily run out of 16-bit indices, a limitation option A did not have
  • Edit: You can merge multiple draw calls into single draw call, if the objects share same transform and so on. So you could possibly consider H, e, l, l, o as individual objects which share the VBO and get drawn with a single draw call.

C) Using VAO per object, object offset added to attribute offset

  • Create a VAO per object, and add an “object offset” to all attribute offsets
  • Object offset for first object would be 0
  • Object offset for second object would be stride * number of vertices, in the example that would be 24 * 10 = 240
  • You setup the VAO during startup
  • At draw time you update attribute pointers by binding VAO before draw call

For the second object you would then do at startup:


// gen and bind VAO for the second object
// make sure correct VBO is bound
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, 0 + 240); glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, 12 + 240); glEnableVertexAttribArray(1);
// bind the index buffer
// Optionally: bind VAO 0 to avoid accidentally modifying this VAO ever after

At draw time:

// bind VAO for the second object - This gets all the state needed to get attribute pointers to VBO right
// draw call

D) Object offset added to vertex attribute pointers, no VAO nor base vertex available

  • This is like option C, but without VAO, so you do all VertexAttribPointer setup for every object
  • Not really recommended, it would be better to use option B instead.

E) One VBO per object, attrib pointer calls before every draw call

  • Just for comparison
  • Same as D but in addition to all vertex attrib pointer setup calls, one also binds the VBO
  • The basic version, possibly most often used
  • Any of the above options is probably better than this - if you try them: :smiley:
  • If you settle for this: :frowning: