How to use vertex buffer objects (VBO) instead of calling glDrawArrays thousands of times in OpenGL ES 1.0 on iOS? - iphone

For a simulation we've created a OpenGL1.1 view with a grid of 32 x 48 rectangles.
We're drawing this grid every time the CADisplayLink calls our draw function, and the vertex positions never change. The only thing that changes from frame to frame is the color of a vertex.
This is a simplified example of how we do it:
- (void)drawFrame {
// draw grid
for (int i = 0; i < numRectangles; i++) {
// ... calculate CGPoint values for vertices ...
GLshort vertices[ ] = {
bottomLeft.x, bottomLeft.y,
bottomRight.x, bottomRight.y,
topLeft.x, topLeft.y,
topRight.x, topRight.y
};
glVertexPointer(2, GL_SHORT, 0, vertices);
glColor4f(r, g, b, 1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
The OpenGL instrument recommended using Vertex Buffer Objects (VBO) for better performance.
Is there an example of how to set up a very basic, simple usage of Vertex Buffer Objects in a case where the vertices don't change from frame to frame?
Apple is providing an example over here, under the section Use Vertex Buffer Objects to Manage Copying Vertex Data, but it's incomplete.
GLuint vertexBuffer;
GLuint indexBuffer;
void CreateVertexBuffers()
{
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
It doesn't show how to really create the data. The previous listing (which is supposed to be a "bad example") contains these two lines:
const vertexStruct vertices[] = {...};
const GLubyte indices[] = {...};
So these two arrays or structs have to be passed into:
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
and
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
?
Is this the "Interleaved (array of structs)" format preferred by iOS, according to Apple under the Use Interleaved Vertex Data section?

You're not supposed to draw single primitives using glDrawArrays, but large batches. So far you're using only regular vertex arrays, not vertex buffer objects.
The idea is, to put the geometry off all rectangles into one single VBO (a VBO is essentially a vertex array stored "in" OpenGL, rather your process). Changing single vertices is possible by using glBufferSubData.
Vertex color can be put into a vertex array, and hence into a VBO as well.
Update
Say you have some hexagon:
GLfloat vertices[2][] = {
{0, 0}, // 0
{1, 0}, // 1
{0.5, 0.866}, // 2
{-0.5, 0.866}, // 3
{-1, 0}, // 4
{0.5, -0.866}, // 5
{-0.5, -0.866}, // 6
};
and you want to draw only part of the triangles, say the triangles consisting of vertices [0,1,2], [0,3,4] and [0,5,6], then you'd create the following index array
GLushort indices[] = {
0, 1, 2,
0, 3, 4,
0, 5, 6
};
And use that as the indices for glDrawElements.
Update 2
One thing that many computer graphics and OpenGL newbies get wrong is, that a vertex is not merely a position, but a combination of vertex attributes. Which attributes make a vertex is a design choice made by the programmer. But the commonly used vertex attributes are
position
normal
texture coordinates
vertex color
Until OpenGL-3 core the position attribute was mandatory. Since OpenGL-3 core, which made shaders mandatory, vertex attributes are just arbitrary input data into shaders, and as long as a vertex shader manages to deliver the *gl_Position* output, OpenGL is happy.
The important thing is, that two vertices are identical only then, if all the attributes are the same. If they differ in just one attribute, they're not the same vertex. Now let's take our previous example of the hexagon. We're now making the triangles red, green and blue and were going to add two triangles, to extend the red and green ones into kind of diamond shapes:
// x, y, red, green, blue
GLfloat vertices[5][] = {
// red
{0, 0, 1, 0, 0}, // 0
{1, 0, 1, 0, 0}, // 1
{0.5, 0.866, 1, 0, 0}, // 2
{1, 1, 1, 0, 0}, // 3
// green
{0, 0, 0, 1, 0}, // 4
{-0.5, 0.866, 0, 1, 0}, // 5
{-1, 0, 0, 1, 0}, // 6
{-1, 1, 0, 1, 0}, // 7
// blue
{0, 0, 0, 0, 1}, // 8
{0.5, -0.866, 0, 0, 1}, // 9
{-0.5, -0.866, 0, 0, 1}, // 10
};
The triangles we now want to draw are
GLushort indices[] = {
// the two red triangles
0, 1, 2,
3, 2, 1,
// the two green triangles
4, 5, 6,
5, 7, 6,
// the blue triangle
8, 9, 10
};
Now we need to tell OpenGL about the structure of our vertex array. This is where the stride parameter of the gl…Pointer functions enters the picture. If nonzero, the stride tells OpenGL the distance (in bytes) between the start of each vertex in the array. By passing the data pointer with the right offset this makes OpenGL access the right things. In our case a vertex consists of
2 position elements of GLfloat with offset 0
3 color elements of GLfloat with offset 2*sizeof(GLfloat)
and each vertex is sizeof(GLfloat)*5 bytes apart.
We'll let the C compiler do the offset calculations for us, by simply dereferencing the right array elements and taking the address of it:
glVertexPointer(2, GL_FLOAT, sizeof(GLfloat)*5, &vertices[0][0]);
glColorPointer(3, GL_FLOAT, sizeof(GLfloat)*5, &vertices[0][2]);
The rest is just glDrawElements(GL_TRIANGLES, 5, GL_UNSIGNED_SHORT, indices).
Note that we're not using VBOs at this point, but client side vertex arrays only. VBOs build upon vertex arrays. So I strongly suggest you first get a strong grip of vertex arrays, before going to tackle VBOs. They quite easy to use actually, but there are a few conceptional pitfalls, like tricking the compiler to pass a number for a pointer parameter.

Related

OpenGL ES - glDrawElements - Trouble Understanding Indices

I wonder if anyone can help me understand how indices work with glDrawElements. In the below example (taken from http://www.everita.com/lightwave-collada-and-opengles-on-the-iphone) the author mentions that you can only have one set of indices, in this case
const GLushort tigerBottomIndices[] = {
0,1,2,
3,0,4,
1,5,6,
…
};
My question is what do these indices describe ? Am I right in thinking that the first three are the vertice positions, the second three are then the corresponding normals and the last three the texture co-ords ?
Thanks in advance !
#import "OpenGLCommon.h"
const Vertex3D tigerBottomPositions[] = {
{0.176567, 0.143711, 0.264963},
{0.176567, 0.137939, 0.177312},
{0.198811, 0.135518, 0.179324},
…
};
const Vertex3D tigerBottomNormals[] = {
{-0.425880, -0.327633, 0.350967},
{-0.480159, -0.592888, 0.042138},
{-0.113803, -0.991356, 0.065283},
…
};
const GLfloat tigerBottomTextureCoords[] = {
0.867291, 0.359728,
0.779855, 0.359494,
0.781798, 0.337223,
…
};
const GLushort tigerBottomIndices[] = {
0,1,2,
3,0,4,
1,5,6,
…
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, tigerTextures[5]);
glVertexPointer(3, GL_FLOAT, 0, tigerBottomPositions);
glNormalPointer(GL_FLOAT, 0, tigerBottomNormals);
glTexCoordPointer(2, GL_FLOAT, 0, tigerBottomTextureCoords);
glDrawElements(GL_TRIANGLES, 210, GL_UNSIGNED_SHORT, tigerBottomIndices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableEnableClientState(GL_TEXTURE_COORD_ARRAY);
Each and every value in the index array points at the same time for a position, a normal and a texture coordinate.
They are only organized in groups of 3 because they are simply discribing the vertices of a triangle, so 3 vertices = 1 triangle, of course.
const GLushort tigerBottomIndices[] = {
0,1,2, // #1 Triangle
3,0,4, // #2 Triangle
1,5,6, // #3 Triangle
…
So let's pick the first value of these indices, it's 0.
This means:
Pick the vertex position number 0
Also, pick the vertex normal number 0
And pick the texture coordinates number 0
const Vertex3D tigerBottomPositions[] = {
{0.176567, 0.143711, 0.264963}, // This is the position number 0
{0.176567, 0.137939, 0.177312},
{0.198811, 0.135518, 0.179324},
…
};
const Vertex3D tigerBottomNormals[] = {
{-0.425880, -0.327633, 0.350967}, // This is the normal number 0
{-0.480159, -0.592888, 0.042138},
{-0.113803, -0.991356, 0.065283},
…
};
const GLfloat tigerBottomTextureCoords[] = {
0.867291, 0.359728, // These are the tex-coords number 0
0.779855, 0.359494,
0.781798, 0.337223,
…
};
So this information gets sent to the vertex shader:
VertexPosition: 0.176567, 0.143711, 0.264963
VertexNormal: -0.425880, -0.327633, 0.350967
VertexTextureCoordinates: 0.867291, 0.359728
...
If you do not use indices, opengl will send those vertex data linearly, so after sending vertex data number 0, it would send the data at position 1 of the arrays, then 2, 3, 4 etc...
That's good but sometimes your triangles end up with one or two identical vertices. Consider this:
You can see 2 triangles forming a square, and they have 2 vertices in common, 0 and 2. So instead of having 6 vertices, being 3 for each triangle, we have only 4 and the 2 traingles use the same data for 2 of their vertices. That's good for performance, especially when you have big models with hundreds of triangles.
In order to draw the first triangle, we need the vertices number 0, 1 and 2 and for the second triangle we need the vertices number 0, 2 and 3.
See, without an index array, opengl would try to use vertices 0, 1 and 2 (ok for the first triangle) but for the second triangle opengl would look for the vertices 3, 4 and 5. Which is wrong.
And that's why we create the index array, so opengl can send the right vertices for the vertex shader. In our case our index array would look like this:
const GLushort tigerBottomIndices[] = {
0,1,2,
0,2,3,
}
The indices are meant in the sense of indices of elements in an array. Index 0 addresses the first element in an array, index 1 the second and so on.
In your example the first indices 0, 1, 2 address the first three vertices, which have the positions of the first three Vertex3D items of array tigerBottomPositions, the normals of the first three elements of tigerBottomNormals (with 3 floats forming one normal vector) and same for texture coordinates.
The first argument in the glDrawElements call tells OpenGL how to form primitives from the indexed vertices. GL_TRIANGLES means every three indexed vertices form a triangle.
So vertices with indices 0, 1, 2 form a triangle, 3,0,4 form the next, 1,5,6 form another triangle and so on.

Can I create Vertex Buffer Objects without color information?

I want to create a grid of rectangles for a simulation, where we colorize the rectangles based on calculation results.
Initially I would just want to build the VBO to define the grid. And then in every frame simply assign colors to the rectangles.
Is this possible or is a VBO always "hard-wired" with a set of colors? Because all examples I find online do it like this. They initialize the VBO together with colors and not just the vertex position data, like this:
// allocate a new buffer
glGenBuffers(1, &cubeVBO);
// bind the buffer object to use
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
const GLsizeiptr vertex_size = NUMBER_OF_CUBE_VERTICES*NUMBER_OF_CUBE_COMPONENTS_PER_VERTEX*sizeof(GLfloat);
const GLsizeiptr color_size = NUMBER_OF_CUBE_COLORS*NUMBER_OF_CUBE_COMPONENTS_PER_COLOR*sizeof(GLubyte);
// allocate enough space for the VBO
glBufferData(GL_ARRAY_BUFFER, vertex_size + color_size, 0, GL_STATIC_DRAW);
A VBO is just a piece of memory, you use it to make things run faster by having the data reside in the graphics card. (Some hardware uses system memory for VBO, so not really much to gain in this case)
I also find it cleaner to always use VBOs, but that's a bit of a personal preference.
Anyway you can create VBOs, and then change the data inside them, same as piece of RAM, if you need to change everything on VBO every frame again no performance benefit, but if you only need to change stuff from time to time, or some of the data is fixed (say your vertex data), then you start to get some benefits...
for example:
glGenBuffers(1, &vboObjects[vboGroupBeaver]);
glBindBuffer(GL_ARRAY_BUFFER, vboObjects[vboGroupBeaver]);
glBufferData(GL_ARRAY_BUFFER, beaverVerts*8*sizeof(GLfloat), 0, GL_STATIC_DRAW);
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
NSString *path;
path = [[NSBundle mainBundle] pathForResource:#"beaver01" ofType:#"bin"];
NSFileHandle *model = [NSFileHandle fileHandleForReadingAtPath:path];
float vertice[8];
int counter = 0;
while (read([model fileDescriptor], &vertice, 8*sizeof(float))) {
memcpy(vbo_buffer, vertice, 8*sizeof(GLfloat)); // 0
vbo_buffer += 8*sizeof(GLfloat);
counter++;
}
NSLog(#"Vertices %1i",counter);
glUnmapBufferOES(GL_ARRAY_BUFFER);
This bit of code loads a model into a VBO (vboGroupBeaver), in this example it's the first keyframe of a animation.
All the data is now in the VBO, if I do this after:
glVertexPointer(3, GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL));
glNormalPointer(GL_FLOAT, 8*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT,8*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLES, 0, beaverVerts);
I get a beaver drawn... (Notice that I'm using interleaved vertex data, that's why the pointer calls have the extra info).
In your case you would have a Color Pointer instead of a Texture pointer.
Now, if you want to change stuff all you have to do is glMapBufferOES to a buffer var, and interate thru it to change only the parts you need.
Something like:
vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
for (int i = start; i < end; i++) {
vbo_buffer += 6*sizeof(GLfloat); // offset to position
memcpy(vbo_buffer, whatYouWantToChange, 2*sizeof(GLfloat)); // change what you want, watch the size
}
EDIT give an example with color
First some example data, in this case a triangle with per vertex data interleaved:
static const ColoredTriangle vertexData[] = {
{
{0.0, 0.0, 0.0}, // Vertex 0
{0.0, 0.0, 1.0}, // Normal
{1.0, 0.0, 0.0, 1.0} // Color
},
{
{0.0, 480.0, 0.0}, // Vertex 1
{0.0, 0.0, 1.0}, // Normal
{1.0, 1.0, 0.0, 1.0} // Color
},
{
{320.0, 0.0, 0.0}, // Vertex 2
{0.0, 0.0, 1.0}, // Normal
{1.0, 1.0, 1.0, 1.0} // Color
}
Copy the thing to the vbo (after creating/binding/MapBuffer.
memcpy(vbo_buffer, vertexData, 10*3*sizeof(float));
draw the thing
glVertexPointer(3, GL_FLOAT, 10*sizeof(GLfloat), (GLvoid*)((char*)NULL));
glNormalPointer(GL_FLOAT, 10*sizeof(GLfloat), (GLvoid*)((char*)NULL+3*sizeof(GLfloat)));
glColorPointer(4, GL_FLOAT, 10*sizeof(GLfloat), (GLvoid*)((char*)NULL+6*sizeof(GLfloat)))
glDrawArrays(GL_TRIANGLES, 0, beaverVerts);
So now you have a triangle being draw with interleaved data from the VBO.
Now on each frame you want to do something just change the data.
GLvoid* vbo_buffer = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
vbo_buffer += 6*sizeof(GLfloat); // position the buffer at the first vertex color data
for (int i = 0; i < 3; i++) {
memcpy(vbo_buffer, newColor, 4*sizeof(GLfloat));
vbo_buffer += 10*sizeof(GLfloat); // skip the stripe
}
glUnmapBufferOES(GL_ARRAY_BUFFER);
Then draw again, and you just changed the color info.
Depending on the number of changes you're going to be making it might be better to change GL_STATIC_DRAW to something else also...
Disclaimer
This was made on the fly, so beware of dragons.
You can define the vertex coordinates in a VBO and the vertex colors in another, then you can use glVertexAttribPointer (or glVertexPointer, glColorPointer) to set up the vertex attributes for rendering.

How to batch render sprites in OpenGL ES (iPhone)

I have a game that renders a bunch of sprites (several hundred), almost all of which are using the same texture. Currently, I'm calling glDrawArrays(...) for each one, which I recently discovered was very inefficient. After doing some research, I've learned that I need to put all my vertices for every sprite into one big vertex buffer, and call glDrawArrays(...) just once using that. However, when I do so it only draws the first sprite, and the other 200 are blank.
blueSpriteVertices[blueBatchNum * 4] = Vertex3DMake(xloc, yloc, zloc);
blueSpriteVertices[blueBatchNum * 4 + 1] = Vertex3DMake(xloc + size, yloc, zloc);
blueSpriteVertices[blueBatchNum * 4 + 2] = Vertex3DMake(xloc, yloc + size, zloc);
blueSpriteVertices[blueBatchNum * 4 + 3] = Vertex3DMake(xloc + size, yloc + size, zloc);
blueBatchNum++;
//^^This block of code^^ is called iteratively, adding data for various sprites
//(around 200) to the vertex array. "xloc", "yloc", etc. are private members of
//this sprite class
//Draw the whole batch
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColor4f(1, 1, 1, 1);
//This code is actually in the Texture2D class implementation, hence "_name"
//and "coordinates"
glBindTexture(GL_TEXTURE_2D, _name);
glVertexPointer(3, GL_FLOAT, 0, blueSpriteVertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_VERTEX_ARRAY);
I finally solved this problem by using GL_TRIANGLES instead of GL_TRIANGLE_STRIP, and handled the triangle strips manually. By doing so I was able to eliminate all the "strips" that it was interpreting in between my sprites. Works like a charm now, and the batching definitely improved my game's performance astronomically.
Using (GL_TRIANGLES instead of GL_TRIANGLE_STRIP works for me (on Android)
glDrawElements(GL_TRIANGLES, 6 * mSpriteCounter, GL_UNSIGNED_SHORT, (char*) NULL);
The glDrawArrays() last parameter should contain the number of vertices (in your case you have only 4). Also you must have the same number of texture coordinates to match the drawn vertices!

How to dynamically create vertices in glVertexPointer() in openGLES

I have a set of textures which I need to draw at different vertices such that every texture is visible.I cannot define a static set of vertices since I load the textures dynamically and I dont know how many textures will be loaded everytime ( i choose them based on a condition).
This is how my code looks as of now.
for(int i=0;i<num_img;i++)
{
glLoadIdentity();
glTranslatef(0.0, 0.0, -3.0);
glRotatef(rot, 1.0, 1.0, 1.0);
glBindTexture(GL_TEXTURE_2D, texture[i]);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glNormalPointer(GL_FLOAT, 0, normals);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
Is there anyway to dynamically generate vertices that can be passed as final argument of glVertexPointer() ?
Do you know the maximum number of vertices that you will use? Maybe you could create the vertices array to be that maximum size then in the first parameter of glVertexPointer you only pass the number of vertices that you actually use.

OpenGL to OpenGL-ES - glRectf()

I am trying to learn OpenGL on the iPhone using the "Super Bible" but am having trouble porting from OpenGLto OpenGL ES. My understanding is that the glRectf() function is not available in the latter. What is the substitute approach? Any relevant conceptual information would be appreciated as well.
The substitute approach is to draw a triangle strip:
GLfloat texture[] =
{
0, 0,
0, 1,
1, 0,
1, 1
};
GLfloat model[] =
{
0, 0, // lower left
0, h, // upper left
w, 0, // lower right
w, h // upper right
};
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, model);
glTexCoordPointer(2, GL_FLOAT, 0, texture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
This draws a textured rectangle with width w and height h.
Rather than doing a rect, you just do two triangles.
This is really irrelevant though since GL-ES on the iPhone does not support immediate mode. You need to define all your vertices in an array and use one of the vertex array rendering functions to draw them rather than using the immediate mode functions.