OpenGLES 2.0 separate buffers for vertices, colors and texture coordinates

polyclick picture polyclick · Jan 6, 2012 · Viewed 15.3k times · Source

I've been learning OpenGL for a few days now by following some tutorials and coding some experiments of my own. But there is one thing I really don't understand which blocks me from continuing. I've been googling for a few hours now and didn't find an answer yet to my question.

Where should I specify every separate color value and texture coordinate for every individual vertex? Should those properties always be listed in the same array (struct) as the vertex positions? Like so:

const Vertex Vertices[] = {
    // Front
    {{1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}},
    {{1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}},
    {{-1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}},
    {{-1, -1, 0}, {0, 0, 0, 1}, {0, 0}},

    ...

Or is there a way to put color values and texture coordinates in separate arrays? But then the question arises: how do I call glDrawElements with separate arrays?

In case you are wondering why I want to separate these values: I'm currently making my own .obj parser in obj-c and I was wondering: what if you load a model without a texture and only want to show a color on the object? Or: what if you want load a model with only a texture mapped to it but no separate color per vertex? And: Isn't putting color values and texture coordinate bloating the Vertex struct with too much data.

Answer

warhead picture warhead · Jan 6, 2012

Actually it is the usual way to separate the vertex data into position, color, etc. using several arrays/buffers.

Last time I came in contact with ES 2.0 was in the context of WebGL (which is has a slightly different spec but is ultimately based on ES 2.0).

What is basically done is writing the data to seperate buffers using

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), positions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), colors, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

...

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort), indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

with positions and colors being float arrays containing the vertex data and indices containing the indices as unsigned shorts in this case.

To render this data, you'd use the buffers and attribute pointers to your shader:

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(vertexPositionAttribute, 3,  GL_FLOAT, false, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glVertexAttribPointer(vertexColorAttribute, 4, GL_FLOAT, false, 0, 0);

Finally bind the index buffer:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

and render:

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);

To get the attributes:

glUseProgram(shaderProgram);

vertexPositionAttribute= glGetAttribLocation(shaderProgram, "vertexPosition");
glEnableVertexAttribArray(vertexPositionAttribute);

vertexColorAttribute = glGetAttribLocation(shaderProgram, "vertexColor");
glEnableVertexAttribArray(vertexColorAttribute );

...

If you don't have custom shaders(using fixed function) you might be able to use

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexPointer(3,  GL_FLOAT, false, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glColorPointer(4, GL_FLOAT, false, 0, 0);

instead. I'd advise against it, though, as it is outdated (if at all available in ES 2.0). If you still want to use it, you can skip the whole buffer business altogether and use

glVertexPointer(3, GL_FLOAT, false, 0, positions);
glColorPointer(4, GL_FLOAT, false, 0, colors);

with

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, indices);

I hope that wasn't too confusing and helps a bit. For further reading, although targeted at OpenGL, I'd suggest the Nehe tutorials.