Client-Side Vertex Arrays

From OpenGL.org
(Redirected from Vertex Arrays)
Jump to: navigation, search

Before VBOs, there were plain old vertex arrays. This means that your vertices and vertex attributes and indices are in RAM.
Of course, this doesn't give the best performance since every time you want GL to draw, the driver has to upload the vertices to the GPU.

Sample Code

I suggest that you interleave your vertex attributes for best performance. The order of of attributes should not matter for performance because it's just a pointer to a memory location for the GPU. Futhermore, you should think of a vertex as not as just a position, but also all the other attributes that go along with it such as normal, texcoord0, texcoord1, texcoord2, color, tangent vectors and binormal vectors.
Make a structure for your vertex attributes in your C++ code :

 struct MyVertex
 {
 float x, y, z;        //Vertex
 float nx, ny, nz;     //Normal
 float s0, t0;         //Texcoord0
 float s1, t1;         //Texcoord1
 float s2, t2;         //Texcoord2
 float padding[4];
 };

Padding is added to make the vertex structure a multiple of 32 bytes since some GPUs prefer it that way, such as ATI/AMD.

Create an array of vertices and fill your array. Of course, in a real program, you would read some file :

 MyVertex vertex[50];
 vertex[0].x=0.0;
 vertex[0].y=0.0;
 vertex[0].z=0.0;
 and so on....

Create an array of indices and setup your indices. Unsigned short is used (16 bit) since that is what most GPUs prefer. Some of them can deal with 32 bit indices as well.
Don't use anything ridiculous like unsigned byte.

 ushort index[99];
 index[0]=0;
 index[1]=5;
 index[2]=3;
 and so on....

Call your gl***Pointer functions to make a INTERLEAVED ARRAY.
Also, if you are using VBOs in some other part of your code, you would have to bind VBO 0 by calling glBindBuffer(GL_ARRAY_BUFFER, 0)
You may also need to call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

 glEnableClientState(GL_VERTEX_ARRAY);
 glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), &vertex[0].x);
 glEnableClientState(GL_NORMAL_ARRAY);
 glNormalPointer(GL_FLOAT, sizeof(MyVertex), &vertex[0].nx);
 glClientActiveTexture(GL_TEXTURE0);
 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s0);
 glClientActiveTexture(GL_TEXTURE1);
 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s1);
 glClientActiveTexture(GL_TEXTURE2);
 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s2);

Now it's time to render. I have not included the part about setting up texture combiners, or shader, or binding shaders.

 glDrawRangeElements(GL_TRIANGLES, x, y, z, GL_UNSIGNED_SHORT, index);

x would be the very first index, which might be 0. y would be the last vertex which would be 49 (not 50!). z would the number of indices to be processed.

You might also want to read Vertex Formats

In summary:

  • Make you vertex structure.
  • Make it multiple of 32 bytes in size.
  • Use 16 bit integer indices where reasonable.
  • Try not to make many redundent calls to GL so that your performance stays the best.
  • Don't use glInterleaved array because almost nobody uses it and it's limited. You can read about it here.

Sample Code 2

See Sample Code 1 for a more detailed description.

In this one, we will make a vertex structure that hold XYZ position, texcoord0 and color (RGBA format). We will declare the color as a uint which in our case is a unsigned 32 bit integer. You should check your target format what type defines a unsigned 32 bit integer. It is a good idea to use RGBA instead of RGB because GPUs prefer to have all of the components. All the components for color make a nice 32 bit data block. We should also state that OpenGL has never up to this point supported the BGRA format for vertex colors. You will notice that glColorPointer doesn't take a format value such as GL_RGBA. It only accepts component values such as 4 in this case.

 struct MyVertex
 {
 float  x, y, z;        //Vertex
 float s0, t0;          //Texcoord0
 uint color;            //RGBA color
 float padding[2];
 };

Padding is added to make the vertex structure a multiple of 32 bytes since some GPUs prefer it that way, such as ATI/AMD.

And the calls to the gl****Pointer functions would look like this

 glEnableClientState(GL_VERTEX_ARRAY);
 glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), &vertex[0].x);
 glDisableClientState(GL_NORMAL_ARRAY);
 glClientActiveTexture(GL_TEXTURE0);
 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s0);
 glClientActiveTexture(GL_TEXTURE1);
 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 glClientActiveTexture(GL_TEXTURE2);
 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 glEnableClientState(GL_COLOR_ARRAY);
 //OpenGL wants a component value. 4 in our case.
 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyVertex), &vertex[0].color);

In the above example, we disabled the normal array and some of the texcoord arrays. We enable the color array.