Difference between revisions of "Client-Side Vertex Arrays"

From OpenGL Wiki
Jump to: navigation, search
m (Fixing link)
 
(5 intermediate revisions by 4 users not shown)
Line 1: Line 1:
Before VBOs, there was plain old vertex arrays. This means that your vertices and vertex attributes and indices are in RAM.<br>
+
Before VBOs, there were plain old vertex arrays. This means that your vertices and vertex attributes and indices are in RAM.<br>
 
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.<br>
 
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.<br>
 
<br>
 
<br>
 
===Sample Code===
 
===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.<br>
+
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.<br>
Make a structure for your vertex attributes in your C++ code :  
+
Make a structure for your vertex attributes in your C++ code:
 +
<source lang="cpp">
 
   struct MyVertex
 
   struct MyVertex
 
   {
 
   {
  float x, y, z;        //Vertex
+
      float x, y, z;        //Vertex
  float nx, ny, nz;    //Normal
+
      float nx, ny, nz;    //Normal
  float s0, t0;        //Texcoord0
+
      float s0, t0;        //Texcoord0
  float s1, t1;        //Texcoord1
+
      float s1, t1;        //Texcoord1
  float s2, t2;        //Texcoord2
+
      float s2, t2;        //Texcoord2
  float padding[4];
+
      float padding[4];
 
   };
 
   };
 +
</source>
  
Padding is added to make the vertex structure a multiple of 32 bytes since some GPUs prefer it that way, such as ATI/AMD.<br>
+
Padding is added to make the vertex structure a multiple of 32 bytes since some GPUs prefer it that way, such as ATI/AMD.
Setup your vertices :
+
 
 +
Create an array of vertices and fill your array. Of course, in a real program, you would read some file:
 +
<source lang="cpp">
 
   MyVertex vertex[50];
 
   MyVertex vertex[50];
 
   vertex[0].x=0.0;
 
   vertex[0].x=0.0;
Line 22: Line 26:
 
   vertex[0].z=0.0;
 
   vertex[0].z=0.0;
 
   and so on....
 
   and so on....
 +
</source>
  
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.<br>
+
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.<br>
 
Don't use anything ridiculous like unsigned byte.
 
Don't use anything ridiculous like unsigned byte.
   unsigned short index[99];
+
<source lang="cpp">
 +
   ushort index[99];
 
   index[0]=0;
 
   index[0]=0;
 
   index[1]=5;
 
   index[1]=5;
 
   index[2]=3;
 
   index[2]=3;
   and so on....
+
   // and so on....
 +
</source>
  
 
Call your gl***Pointer functions to make a INTERLEAVED ARRAY.<br>
 
Call your gl***Pointer functions to make a INTERLEAVED ARRAY.<br>
Line 35: Line 42:
 
You may also need to call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
 
You may also need to call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
  
 +
<source lang="cpp">
 
   glEnableClientState(GL_VERTEX_ARRAY);
 
   glEnableClientState(GL_VERTEX_ARRAY);
 
   glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), &vertex[0].x);
 
   glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), &vertex[0].x);
 
   glEnableClientState(GL_NORMAL_ARRAY);
 
   glEnableClientState(GL_NORMAL_ARRAY);
 
   glNormalPointer(GL_FLOAT, sizeof(MyVertex), &vertex[0].nx);
 
   glNormalPointer(GL_FLOAT, sizeof(MyVertex), &vertex[0].nx);
 +
  glClientActiveTexture(GL_TEXTURE0);
 
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
   glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s0);
 
   glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s0);
Line 47: Line 56:
 
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
   glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s2);
 
   glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), &vertex[0].s2);
 +
</source>
  
 
Now it's time to render. I have not included the part about setting up texture combiners, or shader, or binding shaders.  
 
Now it's time to render. I have not included the part about setting up texture combiners, or shader, or binding shaders.  
  
 +
<source lang="cpp">
 
   glDrawRangeElements(GL_TRIANGLES, x, y, z, GL_UNSIGNED_SHORT, index);
 
   glDrawRangeElements(GL_TRIANGLES, x, y, z, GL_UNSIGNED_SHORT, index);
 +
</source>
  
 
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.<br>
 
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.<br>
 
<br>
 
<br>
You might also want to read http://www.opengl.org/wiki/Vertex_Formats<br>
+
You might also want to read [[Vertex Formats]]
  
 
'''In summary:'''
 
'''In summary:'''
 
* Make you vertex structure.
 
* Make you vertex structure.
 
* Make it multiple of 32 bytes in size.
 
* Make it multiple of 32 bytes in size.
* Use 16 bit integer.
+
* Use 16 bit integer indices where reasonable.
 
* Try not to make many redundent calls to GL so that your performance stays the best.
 
* 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 http://www.opengl.org/wiki/Common_Mistakes
+
* Don't use glInterleaved array because almost nobody uses it and it's limited. You can read about it [[Common Mistakes|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.
 +
<source lang="cpp">
 +
  struct MyVertex
 +
  {
 +
      float  x, y, z;        //Vertex
 +
      float s0, t0;          //Texcoord0
 +
      uint color;            //RGBA color
 +
      float padding[2];
 +
  };
 +
</source>
 +
 
 +
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
 +
<source lang="cpp">
 +
  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);
 +
</source>
  
 +
In the above example, we disabled the normal array and some of the texcoord arrays. We enable the color array.
 
[[Category:Examples]]
 
[[Category:Examples]]
 +
[[Category:Deprecated]]

Latest revision as of 17:54, 2 January 2018

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.