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

StackOverflow https://stackoverflow.com/questions/8329546

  •  26-10-2019
  •  | 
  •  

Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top