Each face needs 4 times of glVertex*() calls to make a quad, for example, the quad at front is v0-v1-v2-v3. A cube has 6 faces, so the total number of glVertex*() calls is 24. If you also specify normals and colors to the corresponding vertices, the number of function calls increases to 3 times more; 24 of glColor*() and 24 of glNormal*().
The other thing that you should notice is the vertex "v0" is shared with 3 adjacent polygons; front, right and up face. In immediate mode, you have to provide the shared vertex 3 times, once for each face as shown in the code.
glBegin(GL_QUADS); // draw a cube with 6 quads glVertex3fv(v0); // front face glVertex3fv(v1); glVertex3fv(v2); glVertex3fv(v3); glVertex3fv(v0); // right face glVertex3fv(v3); glVertex3fv(v4); glVertex3fv(v5); glVertex3fv(v0); // up face glVertex3fv(v5); glVertex3fv(v6); glVertex3fv(v1); ... // draw other 3 faces glEnd();
Initialization
OpenGL provides glEnableClientState() and glDisableClientState() functions to activate and deactivate 6 different types of arrays. Plus, there are 6 functions to specify the exact positions(addresses) of arrays, so, OpenGL can access the arrays in your application.- glVertexPointer(): specify pointer to vertex coords array
- glNormalPointer(): specify pointer to normal array
- glColorPointer(): specify pointer to RGB color array
- glIndexPointer(): specify pointer to indexed color array
- glTexCoordPointer(): specify pointer to texture cords array
- glEdgeFlagPointer(): specify pointer to edge flag array
Notice that vertex arrays are located in your application(system memory), which is on the client side. And, OpenGL on the server side gets access to them. That is why there are distinctive commands for vertex array; glEnableClientState() and glDisableClientState() instead of using glEnable() and glDisable().
glDrawArrays()
glDrawArrays() reads vertex data from the enabled arrays by marching straight through the array without skipping or hopping. Because glDrawArrays() does not allows hopping around the vertex arrays, you still have to repeat the shared vertices once per face.glDrawArrays() takes 3 arguments. The first thing is the primitive type. The second parameter is the starting offset of the array. The last parameter is the number of vertices to pass to rendering pipeline of OpenGL.
For above example to draw a cube, the first parameter is GL_QUADS, the second is 0, which means starting from beginning of the array. And the last parameter is 24: a cube requires 6 faces and each face needs 4 vertices to build a quad, 6 × 4 = 24.
GLfloat vertices[] = {...}; // 24 of vertex coords ... // activate and specify pointer to vertex array glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); // draw a cube glDrawArrays(GL_QUADS, 0, 24); // deactivate vertex arrays after drawing glDisableClientState(GL_VERTEX_ARRAY);
The size of vertex coordinates array is now 8, which is exactly same number of vertices in the cube without any redundant entries.
Note that the data type of index array is GLubyte instead of GLuint or GLushort. It should be the smallest data type that can fit maximum index number in order to reduce the size of index array, otherwise, it may cause performance drop due to the size of index array. Since the vertex array contains 8 vertices, GLubyte is enough to store all indices.
For example, the vertex v0 is shared with the front, right and up face, but, the normals cannot be shared at v0. The normal of the front face is n0, the right face normal is n1 and the up face is n2. For this situation, the normal is not the same at a shared vertex, the vertex cannot be defined only once in vertex array any more. It must be defined multiple times in the array for vertex coordinates in order to match the same amount of elements in the normal array.
glDrawRangeElements()
Like glDrawElements(), glDrawRangeElements() is also good for hopping around vertex array. However, glDrawRangeElements() has two more parameters (start and end index) to specify a range of vertices to be prefetched. By adding this restriction of a range, OpenGL may be able to obtain only limited amount of vertex array data prior to rendering, and may increase performance.The additional parameters in glDrawRangeElements() are start and end index, then OpenGL prefetches a limited amount of vertices from these values: end - start + 1. And the values in index array must lie in between start and end index. Note that not all vertices in range (start, end) must be referenced. But, if you specify a sparsely used range, it causes unnecessary process for many unused vertices in that range.
GLfloat vertices[] = {...}; // 8 of vertex coords GLubyte indices[] = {0,1,2,3, // 24 of indices 0,3,4,5, 0,5,6,1, 1,6,7,2, 7,4,3,2, 4,7,6,5}; ... // activate and specify pointer to vertex array glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); // draw first half, range is 6 - 0 + 1 = 7 vertices glDrawRangeElements(GL_QUADS, 0, 6, 12, GL_UNSIGNED_BYTE, indices); // draw second half, range is 7 - 1 + 1 = 7 vertices glDrawRangeElements(GL_QUADS, 1, 7, 12, GL_UNSIGNED_BYTE, indices+12); // deactivate vertex arrays after drawing glDisableClientState(GL_VERTEX_ARRAY);
Note that glDrawRangeElements() is available OpenGL version 1.2 or greater.
No comments:
Post a Comment