4
votes

I am developing an iPhone game using OpenGL ES 1.1 and need to use vertex buffer objects in order to render 500+ particles without the performance decreasing.

My game was able to draw successfully using the non-VBO method, but now that I've attempted to incorporate VBOs, nothing is drawing anymore.

Please help me to identify what I am doing wrong and provide a correct example.

I have a class called TexturedQuad which consists of the following:

// TexturedQuad.h

enum {
    ATTRIB_POSITION,
    ATTRIB_TEXTURE_COORD
 };

@interface TexturedQuad : NSObject {

    GLfloat *textureCoords; // 8 elements for 4 points (u and v)
    GLfloat *vertices;      // 8 elements for 4 points (x and y)
    GLubyte *indices;       // how many elements does this need?

    // vertex buffer array IDs generated using glGenBuffers(..)
    GLuint vertexBufferID;
    GLuint textureCoordBufferID;
    GLuint indexBufferID;

    // ...other ivars
}

// @synthesize in .m file for each property
@property (nonatomic, readwrite) GLfloat *textureCoords;
@property (nonatomic, readwrite) GLfloat *vertices;
@property (nonatomic, readwrite) GLubyte *indices;
@property (nonatomic, readwrite) GLuint vertexBufferID;
@property (nonatomic, readwrite) GLuint textureCoordBufferID;
@property (nonatomic, readwrite) GLuint indexBufferID;

// vertex buffer object methods
- (void) createVertexBuffers;
- (void) createTextureCoordBuffers;
- (void) createIndexBuffer;

In TexturedQuad.m, the vertex buffers are created:

- (void) createVertexBuffers {
    glGenBuffers(1, &vertexBufferID);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}

- (void) createTextureCoordBuffers {
    glGenBuffers(1, &textureCoordBufferID);
    glBindBuffer(GL_ARRAY_BUFFER, textureCoordBufferID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoords), textureCoords, GL_STATIC_DRAW);
}

- (void) createIndexBuffer {
    glGenBuffers(1, &indexBufferID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * 16, indices, GL_STATIC_DRAW);
}

The above VBO creation methods are invoked by a custom AtlasLibrary class which initializes each TexturedQuad instance.

Firstly, the vertices are arranged in the following format:

// bottom left
quad.vertices[0] = xMin;
quad.vertices[1] = yMin;

// bottom right
quad.vertices[2] = xMax;
quad.vertices[3] = yMin;

// top left
quad.vertices[4] = xMin;
quad.vertices[5] = yMax;

// top right
quad.vertices[6] = xMax;
quad.vertices[7] = yMax;

Secondly, texture coordinates are arranged in the following format (flipped to account for OpenGL ES's tendency to mirror images):

// top left (of texture)
quad.textureCoords[0] = uMin;
quad.textureCoords[1] = vMax;

// top right
quad.textureCoords[2] = uMax;
quad.textureCoords[3] = vMax;

// bottom left
quad.textureCoords[4] = uMin;
quad.textureCoords[5] = vMin;

// bottom right
quad.textureCoords[6] = uMax;
quad.textureCoords[7] = vMin;

...next, the VBO-creation methods are called (in AtlasLibrary)

[quad createVertexBuffers];
[quad createTextureCoordBuffers];
[quad createIndexBuffer];

Now the meat and potatoes. The SceneObject class. SceneObjects are objects in the game that are renderable. They reference a TexturedQuad instance and contain information about rotation, translation, and scale.

Here is the render method in SceneObject:

- (void) render {

    // binds texture in OpenGL ES if not already bound
    [[AtlasLibrary sharedAtlasLibrary] ensureContainingTextureAtlasIsBoundInOpenGLES:self.containingAtlasKey];

    glPushMatrix();

    glTranslatef(translation.x, translation.y, translation.z);

    glRotatef(rotation.x, 1, 0, 0);
    glRotatef(rotation.y, 0, 1, 0);
    glRotatef(rotation.z, 0, 0, 1);

    glScalef(scale.x, scale.y, scale.z);

    // change alpha
    glColor4f(1.0, 1.0, 1.0, alpha);

    // vertices
    glBindBuffer(GL_ARRAY_BUFFER, texturedQuad.vertexBufferID);
    glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT), &texturedQuad.vertices[0]);


    // texture coords
    glBindBuffer(GL_ARRAY_BUFFER, texturedQuad.textureCoordBufferID);
    glVertexAttribPointer(ATTRIB_TEXTURE_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT), &texturedQuad.textureCoords[0]);

    // bind index buffer array
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texturedQuad.indexBufferID);

    // draw
    glDrawElements(GL_TRIANGLE_STRIP, sizeof(texturedQuad.indices) / sizeof(texturedQuad.indices[0]), GL_UNSIGNED_BYTE, texturedQuad.indices);

    glPopMatrix();
}

I have a strong feeling that either my indices array is structured incorrectly or that the glDrawElements(..) function is called incorrectly.

To answer this question, please:

  • identify what I am doing incorrectly that would cause OpenGL ES to not draw my SceneObjects.
  • provide the correct way to do what I am trying to do (according to my framework, please)
  • provide any suggestions or links which may help (optional)

Thanks so much!

2

2 Answers

1
votes

I'm not experienced in the differences between OpenGL and OpenGL ES, but I'm not seeing any calls to glEnableVertexAttribArray() in your code.

The function is suspiciously absent in the OpenGL ES 1.1 docs, but is in 2.0, and is being used in Apple's OpenGL ES VBO article (thanks JSPerfUnkn0wn).

Here are some other good (though, non-ES) tutorials on Vertex Buffer Objects, and Index Buffer Objects.

0
votes

Unless I'm missing something, where are you loading the textures? You're setting texture coordinates but I see no glBindTexture. And I'm also assuming alpha is a valid value.

See Apple OpenGL ES VBO article, namely from Listing 9-1, and this OpenGL ES VBO tutorial.