OpenGL顶点数组详细介绍

Win32 OpenGL编程(5) 顶点数组详细介绍(转载)

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

提要

    本文主要讲解了OpenGL中使用顶点数组来绘制图形的需求及方法,

 

需求

   按照前面已经讲过的内容来说,简单的2D图形绘制已经没有问题了,并且,很多东西其实已经涉及到3D,但是会发现,复杂图形的绘制时,glBegin和 glEnd对中指定顶点的函数调用会非常多,glVertex函数调用的开销会过大。(虽然在C/C++中函数调用开销其实已经比较小)在3D图形绘制 时,此问题更加严重,想想,一个四边形,4个顶点,一个四方体,就有8个顶点了,但是按照我们目前描述平面的方式来描述就是需要指定6 * 4 = 24个顶点。这正是OpenGL作为高性能图像接口应该极力避免的事情,事实上,OpenGL当然不会一直要求我们用如此低效的方式来完成任务,我们有新 的高效的工具——顶点数组。事实上,在强调高效和尽量减少API的OpenGL ES中,甚至根本就没有glBegin,glEnd和glVertex*等API。

    顶点数组相对来说是OpenGL中用于提高效率的手段,不是核心概念。(虽然OpenGL ES中完全以顶点数组绘制图元,但是事实上OpenGL 1.1版中才加入了定点数组)顶点数组不能使我们绘制出以前以前绘制不了的图元,但是却实实在在的提高了我们绘制图形的效率,但是事实上,对于高性能图像 处理来说,提高效率是如此的重要,因为效率的提升从本质上来说没有增加我们绘制更多图形的能力(即不用提高效率手段也能绘制出相同图形),但是,随着效率 的提高,我们却能在同样的机器上绘制更多的图元,那么,意味着我们可以更加丰富和逼真地表现图像,这不正是图形绘制所追求的目标吗?

 

概念

    有的东西说简单就简单,说复杂就复杂,顶点数组的概念如此的简单,如其名,就是将原来由一系列的glVertex*指定的顶点放在一个数组中,(其意思就 是顶点数据组成的数组)由顶点数组处理函数一次性指定。事实上,不仅如此,其一次性指定的不仅仅是数组,还可以是其他系列的东西,比如表面发现,RGBA 颜色,辅助颜色等。

 

使用方式初步

    使用方式,编程手段的使用方式最佳讲解手段肯定是程序,我们以一个例子来讲解使用方式。

glVertexArray:

   1:
 //OpenGL初始化开始
   2:
 void
 SceneInit(int
 w,int
 h)
   3:
 {
   4:
     glClearColor(0.0, 0.0, 0.0, 0.0);
   5:
     // 启用顶点数组
   6:
     glEnableClientState(GL_VERTEX_ARRAY);
   7:
     
   8:
     // 顶点数组数据
   9:
     static
 GLfloat fVertices[] = {    -0.5, -0.5,
  10:
                                      0.5, -0.5,
  11:
                                      0.5,  0.5,
  12:
                                     -0.5,  0.5};
  13:
  
  14:
     glVertexPointer(2, GL_FLOAT, 0, fVertices);
  15:
 }
  16:
  
  17:
 //这里进行所有的绘图工作
  18:
 void
 SceneShow(GLvoid)        
  19:
 {
  20:
     glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
  21:
     glColor4f(1.0, 1.0, 1.0, 1.0);
  22:
  
  23:
     glBegin(GL_QUADS);    
  24:
     glArrayElement(0);
  25:
     glArrayElement(1);
  26:
     glArrayElement(2);
  27:
     glArrayElement(3);
  28:
     glEnd();
  29:
  
  30:
     // 上述函数调用与下面的效果一样
  31:
     //glBegin(GL_QUADS);    
  32:
     //glVertex3f(-0.5, -0.5, 0.0);
  33:
     //glVertex3f(0.5, -0.5, 0.0);
  34:
     //glVertex3f(0.5, 0.5, 0.0);
  35:
     //glVertex3f(-0.5, 0.5, 0.0);
  36:
     //glEnd();
  37:
  
  38:
     glFlush();
  39:
 }  

    为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glVertexArray/ 目录,获取方式见文章最后关于获取博客完整源代码的说明。

    上述例子的效果与以前SimpleRectangle的效果完全一样,请参考本OpenGL系列文章3,4,(以下简称XO3,XO4^^),截图如以前一样。

    分步骤说明一下上述源代码的含义:

glEnableClientState(GL_VERTEX_ARRAY);

用于表示启用顶点数组,此函数还可以搭配其他参数用于指定其他数组

OpenGL Reference Manual

glEnableClientState   — enable or disable client-side capability 
void glEnableClientState(    GLenum      cap); 
Parameters:Specifies the capability to enable. 
                        Symbolic constants 
                        GL_COLOR_ARRAY, 
                        GL_EDGE_FLAG_ARRAY, 
                        GL_FOG_COORD_ARRAY, 
                        GL_INDEX_ARRAY, 
                        GL_NORMAL_ARRAY, 
                        GL_SECONDARY_COLOR_ARRAY, 
                        GL_TEXTURE_COORD_ARRAY, and 
                        GL_VERTEX_ARRAY 
                        are accepted.

此例中仅仅使用了GL_VERTEX_ARRAY一种用法。

glVertexPointer也是关键函数,用于指定顶点数组的数据。

OpenGL Reference Manual

glVertexPointer   — define an array of vertex data 
C Specification 
void glVertexPointer(    GLint      size, 
     GLenum      type, 
     GLsizei      stride, 
     const GLvoid *      pointer); 
Parameters

size

                        Specifies the number of coordinates per vertex. Must be 2, 3, or 
                        4. The initial value is 4. 
type

                        Specifies the data type of each coordinate in the array. 
                        Symbolic constants 
                        GL_SHORT, 
                        GL_INT, 
                        GL_FLOAT, 
                        or GL_DOUBLE 
                        are accepted. The initial value is GL_FLOAT. 
stride

                        Specifies the byte offset between consecutive 
                        vertices. If stride is 0, the vertices are understood to be tightly packed in 
                        the array. The initial value 
                        is 0. 
pointer

                        Specifies a pointer to the first coordinate of the first vertex in the 
                        array. The initial value is 0.

此例中,数据以2个位一组,类型都是GL_FLOAT,跨度为0表示数据紧密排列,指针自然指向刚才分配的顶点数组数据。

glArrayElement用于指定顶点数组函数具体的顶点,放在glBegin与glEnd之间,产生glVertex*一样的效果,即一次指定一个顶点。

OpenGL Reference Manual

glArrayElement   — render a vertex using the specified vertex array element 
C Specification 
void glArrayElement(    GLint      i); 
Parameters

i

                        Specifies an index into the enabled vertex data arrays.

    上例中分别制定了0,1,2,3共4个顶点,与以前在SimpleRectangle中指定的4个顶点一样,注意取数据的方式,我们上面通过 glVertexPointer制定数据的时候说明了是2个数据一组,(如同缩进所示)那么这里指定的0,1,2,3其实分别是从数组的0,2,4,6个 数据开始取,每次取2个,意思就是此处glArrayElement指定的并不是顶点数组数据实际对应的C语言数组索引而是按照 glVertexPointer指定分组分组后的分组索引。

 

附带颜色的使用

    上述内容就已经包含了定点数组的核心内容,但是我们无法发现使用顶点数组的任何好处,甚至发现更加复杂,函数调用更多了。-_-!下面通过一个附带颜色的例子,加深理解顶点数组使用方式,并可以发现此时其初步减少函数调用次数的作用。

   1:
 //OpenGL初始化开始
   2:
 void
 SceneInit(int
 w,int
 h)
   3:
 {
   4:
     glClearColor(0.0, 0.0, 0.0, 0.0);
   5:
     // 启用顶点数组
   6:
     glEnableClientState(GL_VERTEX_ARRAY);
   7:
  
   8:
     // 颜色数组也需要启用
   9:
     glEnableClientState(GL_COLOR_ARRAY);
  10:
     
  11:
     // 默认就是此参数,可忽略,为了明确说明特意指定
  12:
     glShadeModel(GL_SHADE_MODEL);
  13:
  
  14:
     // 顶点数组数据
  15:
     static
 GLfloat fVertices[] = {    -0.5, -0.5,
  16:
                                      0.5, -0.5,
  17:
                                      0.5,  0.5,
  18:
                                     -0.5,  0.5};
  19:
  
  20:
     // 颜色数组
  21:
     static
 GLfloat fColor[] = { 1.0, 0.0, 0.0,
  22:
                                 0.0, 1.0, 0.0,
  23:
                                 0.0, 0.0, 1.0,
  24:
                                 1.0, 1.0, 1.0};
  25:
  
  26:
     // 指定顶点数组数据
  27:
     glVertexPointer(2, GL_FLOAT, 0, fVertices);
  28:
  
  29:
     // 制定颜色数组
  30:
     glColorPointer(3, GL_FLOAT, 0, fColor);
  31:
  
  32:
 }
  33:
  
  34:
 //这里进行所有的绘图工作
  35:
 void
 SceneShow(GLvoid)        
  36:
 {
  37:
     glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
  38:
  
  39:
     glPushMatrix();
  40:
     glTranslatef(-0.5, 0, 0);
  41:
     glBegin(GL_QUADS);    
  42:
     glArrayElement(0);
  43:
     glArrayElement(1);
  44:
     glArrayElement(2);
  45:
     glArrayElement(3);
  46:
     glEnd();
  47:
     glPopMatrix();
  48:
  
  49:
     // 上述函数调用与下面的效果一样
  50:
     glPushMatrix();
  51:
     glTranslatef(0.5, 0.0, 0.0);
  52:
     glBegin(GL_QUADS);    
  53:
     glColor3f(1.0, 0.0, 0.0);
  54:
     glVertex3f(-0.5, -0.5, 0.0);
  55:
  
  56:
     glColor3f(0.0, 1.0, 0.0);
  57:
     glVertex3f(0.5, -0.5, 0.0);
  58:
  
  59:
     glColor3f(0.0, 0.0, 1.0);
  60:
     glVertex3f(0.5, 0.5, 0.0);
  61:
  
  62:
     glColor3f(1.0, 1.0, 1.0);
  63:
     glVertex3f(-0.5, 0.5, 0.0);
  64:
     glEnd();
  65:
     glPopMatrix();
  66:
  
  67:
     glFlush();
  68:
 }  

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glVertexArrayWithColor/ 目录,获取方式见文章最后关于获取博客完整源代码的说明。

注 释已经详细说明了大部分新添内容的意义,此例子为加强对比,将用顶点数组绘制的图形移到了屏幕的左侧,而用glVertex*绘制的图形放在了屏幕的右 侧。同样的效果,在此例中由于glArrayElement同时指定了顶点和颜色,已经显示出了节省函数调用的作用,何况顶点数组还可以同时指定辅助颜 色,法线向量,雾坐标等等东西,此示例程序运行效果如下图所示。

20091018glVertexArrayWithColor  

 

更进一步,强大的glDrawElements

    glArrayElement的作用我们已经看到了,但是对于的数据仍然一个一个通过glArrayElement指定也不符合OpenGL高效的性格特 点,OpenGL提供了一系列更加高效的函数以完成对效率要求苛刻的任务,我们将逐一介绍,会发现,使用难度越来越高,适用范围越来越窄,但是函数调用越 来越少,功能越来越强大,事实上,函数调用少并不是OpenGL这样设计的唯一理由,越是这样同时处理多个数据的接口,因为一个接口掌握的信息越多,那么 也就越能更多的对其进行优化,此是题外话。

glDrawElements是首先要讲到的。

OpenGL Reference Manual :

glDrawElements   — render primitives from array data 
C Specification 
void glDrawElements(    GLenum      mode, 
     GLsizei      count, 
     GLenum      type, 
     const GLvoid *      indices); 
Parameters

mode

                        Specifies what kind of primitives to render. 
                        Symbolic constants 
                        GL_POINTS, 
                        GL_LINE_STRIP, 
                        GL_LINE_LOOP, 
                        GL_LINES, 
                        GL_TRIANGLE_STRIP, 
                        GL_TRIANGLE_FAN, 
                        GL_TRIANGLES, 
                        GL_QUAD_STRIP, 
                        GL_QUADS, 
                        and GL_POLYGON are accepted. 
count

                        Specifies the number of elements to be rendered. 
type

                        Specifies the type of the values in indices. Must be one of 
                        GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or 
                        GL_UNSIGNED_INT. 
indices

                        Specifies a pointer to the location where the indices are stored.

在《OpenGL Programming Guide 》中对其效果的描述为:

   1:
 glBegin(mode)
   2:
 for
 ( i = 0; i < count; i++)  
   3:
     glArrayElement(indices[i]);
   4:
 glEnd();

    理解了glArrayElement,那么理解glDrawElemets也就不难了。甚至因为此函数作用之大,有了它,我们连glBegin,glEnd都不需要了(OpenGL ES 2.0中就将上述函数都省了,只留下了glDrawElements)

下面看一个例子 。

   1:
 //这里进行所有的绘图工作
   2:
 void
 SceneShow(GLvoid)        
   3:
 {
   4:
     glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
   5:
  
   6:
     static
 GLubyte byRectIndices[] = { 0, 1, 2, 3};
   7:
     glPushMatrix();
   8:
     glTranslatef(-0.5, 0, 0);
   9:
     glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, byRectIndices);
  10:
     glPopMatrix();
  11:
  
  12:
     // 上述函数调用与下面的效果一样
  13:
     glPushMatrix();
  14:
     glTranslatef(0.5, 0, 0);
  15:
  
  16:
     glBegin(GL_QUADS);    
  17:
         glArrayElement(0);
  18:
         glArrayElement(1);
  19:
         glArrayElement(2);
  20:
         glArrayElement(3);
  21:
     glEnd();
  22:
     glPopMatrix();
  23:
  
  24:
     glFlush();
  25:
 }  

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glRectWithDrawElements/目录,获取方式见文章最后关于获取博客完整源代码的说明。  

    看了这个例子以后,真会感叹强中自有强中手啊。。。。。glArrayElement击败了glVertex*,但是glDrawElements又比 glArrayElement更高。glDrawElements使用的时候将一连串glArrayElement需要使用的数据放在一个数组中(此例中 是byRectIndices),然后通过一个函数调用一次指定。注意啊,glDrawElements自带glBegin及glEnd效果,不再需要它 们。(事实上,在OpenGL中命名中带Draw的一般都不需要glBegin和glEnd)程序运行的效果与 glVertexArrayWithColor例子运行效果相同。

 

更强大的glMultiDrawElements

    以前有句话说牛逼是无止尽的,OpenGL函数的强大也是一样。glDrawElements是目前看过的OpenGL函数中最强大的顶点指定函数了,但是glMultiDrawElements如其名,可以一个顶两->N个glDrawElemetns。

OpenGL Reference Manual :

glMultiDrawElements   — render multiple sets of primitives by specifying indices of array data elements 
C Specification 
void glMultiDrawElements(    GLenum      mode, 
     const GLsizei *      count, 
     GLenum      type, 
     const GLvoid **      indices, 
     GLsizei      primcount); 
Parameters

mode

                        Specifies what kind of primitives to render. 
                        Symbolic constants 
                        GL_POINTS, 
                        GL_LINE_STRIP, 
                        GL_LINE_LOOP, 
                        GL_LINES, 
                        GL_TRIANGLE_STRIP, 
                        GL_TRIANGLE_FAN, 
                        GL_TRIANGLES, 
                        GL_QUAD_STRIP, 
                        GL_QUADS, 
                        and GL_POLYGON are accepted. 
count

                        Points to an array of the elements counts. 
type

                        Specifies the type of the values in indices. Must be one of 
                        GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or 
                        GL_UNSIGNED_INT. 
indices

                        Specifies a pointer to the location where the indices are stored. 
primcount

                        Specifies the size of the count array.

在《OpenGL Programming Guide 》中,对其进行的程序语言描述如下:

   1:
 for
 (i = 0; i < primcount; i++) 
   2:
 {
   3:
     if
 ( count[i] > 0)
   4:
         glDrawElements(mode, count[i], type, indices[i]);
   5:
 }

还是看个例子,但是因为上面那个矩形的例子都已经无法承载(^^)glMultiDrawElements的强大了,我们换了个更加复杂一些的例子,用4个三角形拼出来的矩形。顶点数组数据也很简单,只需要增加一个中心点就行了。

   1:
 // OpenGL需要的头文件
   2:
 #include
 <GL/glew.h>
   3:
 #include
 <GL/wglew.h>
   4:
  
   5:
 //定义程序链接时所需要调用的OpenGL程序库,简化工程配置
   6:
 #pragma
 comment( lib, "opengl32.lib"
 ) 
   7:
 #pragma
 comment( lib, "glu32.lib"
 )  
   8:
 #pragma
 comment( lib, "glew32.lib"
 )  
   9:
 #pragma
 comment( lib, "glew32s.lib"
 ) 
  10:
  
  11:
 //OpenGL初始化开始
  12:
 void
 SceneInit(int
 w,int
 h)
  13:
 {
  14:
     GLenum err = glewInit();
  15:
     if
(err != GLEW_OK)
  16:
     {
  17:
         MessageBox(NULL, _T("Error"
), _T("Glew init failed."
), MB_OK);
  18:
         exit(-1);
  19:
     }
  20:
  
  21:
     glClearColor(0.0, 0.0, 0.0, 0.0);
  22:
     // 启用顶点数组
  23:
     glEnableClientState(GL_VERTEX_ARRAY);
  24:
  
  25:
     // 颜色数组也需要启用
  26:
     glEnableClientState(GL_COLOR_ARRAY);
  27:
     
  28:
     // 默认就是此参数,可忽略,为了明确说明特意指定
  29:
     glShadeModel(GL_SHADE_MODEL);
  30:
  
  31:
     // 顶点数组数据
  32:
     static
 GLfloat fVertices[] = {    -0.5, -0.5,
  33:
                                      0.5, -0.5,
  34:
                                      0.5,  0.5,
  35:
                                     -0.5,  0.5,
  36:
                                      0.0,  0.0};    // 添加的原点
  37:
  
  38:
     // 颜色数组
  39:
     static
 GLfloat fColor[] = { 1.0, 0.0, 0.0,
  40:
                                 0.0, 1.0, 0.0,
  41:
                                 0.0, 0.0, 1.0,
  42:
                                 0.0, 0.0, 0.0,
  43:
                                 1.0, 1.0, 1.0};        // 原点颜色为白色
  44:
  
  45:
     // 指定顶点数组数据
  46:
     glVertexPointer(2, GL_FLOAT, 0, fVertices);
  47:
  
  48:
     // 制定颜色数组
  49:
     glColorPointer(3, GL_FLOAT, 0, fColor);
  50:
  
  51:
 }
  52:
  
  53:
 //这里进行所有的绘图工作
  54:
 void
 SceneShow(GLvoid)        
  55:
 {
  56:
     glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
  57:
  
  58:
     static
 GLubyte byTopIndices[] = { 2, 3, 4};
  59:
     static
 GLubyte byLeftIndices[] = { 3, 0, 4};
  60:
     static
 GLubyte byBottomIndices[] = { 0, 1, 4};
  61:
     static
 GLubyte byRightIndices[] = { 1, 2, 4};
  62:
  
  63:
     static
 GLsizei iCounts[] = { 3, 3, 3, 3};
  64:
     static
 GLvoid *indices[] = { byTopIndices, byLeftIndices, byBottomIndices, byRightIndices};
  65:
     glPushMatrix();
  66:
     glTranslatef(-0.5, 0, 0);
  67:
     glMultiDrawElements(GL_TRIANGLES, (GLsizei*)iCounts, GL_UNSIGNED_BYTE, (const
 GLvoid**)indices, 4);
  68:
  
  69:
     glPopMatrix();
  70:
  
  71:
  
  72:
     // 上述函数调用与下面的效果一样
  73:
     glPushMatrix();
  74:
     glTranslatef(0.5, 0, 0);
  75:
     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byTopIndices);
  76:
     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byLeftIndices);
  77:
     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byBottomIndices);
  78:
     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byRightIndices);
  79:
     glPopMatrix();
  80:
  
  81:
     glFlush();
  82:
 }  

为 节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glRectWithMultiDrawElements/ 目录,获取方式见文章最后关于获取博客完整源代码的说明。 运行效果见下图,由于颜色的搭配加上渐变效果,感觉就像是两个从上向下看的金字塔^^

image3

为了方便顶点排列顺序的理解和上例中参数的含义,我特意画了张草图:

image10

    此例中首先需要说明的是,因为glMultiDrawElements函数是OpenGL 1.4版本引进的,而Windows仅仅实现了1.1,所以在Windows下必须需要进行额外处理才能使用了,我使用的是glew库,详细情况见 XO2,其中详细说明了怎么获取Windows中缺失的OpenGL函数。glMultiDrawElements函数仅仅通过最后一个参数(此例中为 4)表示指定4次glDrawElements调用,其他的参数与glDrawElements函数意义一致,仅仅是第2,4参数变成数组形式,这是种非 常有意思又很容易理解和记忆的参数排列方式,也是值得一学的。

 

序列顶点数组函数

还 是为了优化考虑,在对于排好序的顶点序列而言,可以通过接口指明这一点以允许OpenGL实现进一步的优化,OpenGL中对应的函数为 glDrawArrays和glMultiDrawArrays,分别于上述glDrawElements和glMultiDrawElements函数 对应,使用上由于有顶点数组按顺序这个优先条件在,参数更加简单一些,需在需要额外的索引数组,(前面例子中的Indice类数组)只需要指明起点和数量 就行。

OpenGL Reference Manual

glDrawArrays   — render primitives from array data 
C Specification 
void glDrawArrays(    GLenum      mode, 
     GLint      first, 
     GLsizei      count); 
Parameters

mode

                        Specifies what kind of primitives to render. 
                        Symbolic constants 
                        GL_POINTS, 
                        GL_LINE_STRIP, 
                        GL_LINE_LOOP, 
                        GL_LINES, 
                        GL_TRIANGLE_STRIP, 
                        GL_TRIANGLE_FAN, 
                        GL_TRIANGLES, 
                        GL_QUAD_STRIP, 
                        GL_QUADS, 
                        and GL_POLYGON are accepted. 
first

                        Specifies the starting index in the enabled arrays. 
count

                        Specifies the number of indices to be rendered.

glMultiDrawArrays   — render multiple sets of primitives from array data 
C Specification 
void glMultiDrawArrays(    GLenum      mode, 
     GLint *      first, 
     GLsizei *      count, 
     GLsizei      primcount); 
Parameters

mode

                        Specifies what kind of primitives to render. 
                        Symbolic constants 
                        GL_POINTS, 
                        GL_LINE_STRIP, 
                        GL_LINE_LOOP, 
                        GL_LINES, 
                        GL_TRIANGLE_STRIP, 
                        GL_TRIANGLE_FAN, 
                        GL_TRIANGLES, 
                        GL_QUAD_STRIP, 
                        GL_QUADS, 
                        and GL_POLYGON are accepted. 
first

                        Points to an array of starting indices in the enabled arrays. 
count

                        Points to an array of the number of indices to be rendered. 
primcount

                        Specifies the size of the first and count

下面是个例子

   1:
 //OpenGL初始化开始
   2:
 void
 SceneInit(int
 w,int
 h)
   3:
 {
   4:
     GLenum err = glewInit();
   5:
     if
(err != GLEW_OK)
   6:
     {
   7:
         MessageBox(NULL, _T("Error"
), _T("Glew init failed."
), MB_OK);
   8:
         exit(-1);
   9:
     }
  10:
  
  11:
     glClearColor(0.0, 0.0, 0.0, 0.0);
  12:
     // 启用顶点数组
  13:
     glEnableClientState(GL_VERTEX_ARRAY);
  14:
  
  15:
     // 颜色数组也需要启用
  16:
     glEnableClientState(GL_COLOR_ARRAY);
  17:
     
  18:
     // 默认就是此参数,可忽略,为了明确说明特意指定
  19:
     glShadeModel(GL_SHADE_MODEL);
  20:
  
  21:
     // 顶点数组数据
  22:
     static
 GLfloat fVertices[] = {    -0.5, -0.5,
  23:
                                      0.5, -0.5,
  24:
                                      0.5,  0.5,
  25:
                                     -0.5,  0.5,
  26:
                                      0.0,  0.0,     // 添加的原点
  27:
                                     -0.5, -0.5}; // 添加的第一个点
  28:
  
  29:
     // 颜色数组
  30:
     static
 GLfloat fColor[] = { 1.0, 0.0, 0.0,
  31:
                                 0.0, 1.0, 0.0,
  32:
                                 0.0, 0.0, 1.0,
  33:
                                 0.0, 0.0, 0.0,
  34:
                                 1.0, 1.0, 1.0,    // 原点颜色为白色
  35:
                                 1.0, 0.0, 0.0};    // 回归添加第一个点为红色
  36:
  
  37:
     // 指定顶点数组数据
  38:
     glVertexPointer(2, GL_FLOAT, 0, fVertices);
  39:
  
  40:
     // 制定颜色数组
  41:
     glColorPointer(3, GL_FLOAT, 0, fColor);
  42:
  
  43:
 }
  44:
  
  45:
 //这里进行所有的绘图工作
  46:
 void
 SceneShow(GLvoid)        
  47:
 {
  48:
     glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
  49:
  
  50:
     static
 GLubyte byTopIndices[] = { 2, 3, 4};
  51:
     static
 GLubyte byLeftIndices[] = { 3, 0, 4};
  52:
     static
 GLubyte byBottomIndices[] = { 0, 1, 4};
  53:
     static
 GLubyte byRightIndices[] = { 1, 2, 4};
  54:
  
  55:
     static
 GLsizei iCounts[] = { 3, 3, 3, 3};
  56:
     static
 GLvoid *indices[] = { byTopIndices, byLeftIndices, byBottomIndices, byRightIndices};
  57:
  
  58:
     static
 GLint iFirsts[] = { 0, 2, 3};
  59:
  
  60:
     // 左上角的图形展示了glDrawArrays的效果,glDrawArray连续的绘制0到4索引指定顶点的图形
  61:
     glPushMatrix();
  62:
     glTranslatef(-0.5, 0.0, 0.0);
  63:
     glDrawArrays(GL_QUADS, 0, 4);
  64:
     glPopMatrix();
  65:
  
  66:
     // 左下角的图展示了glMultiDrawArrays调用的效果
  67:
  
  68:
     glPushMatrix();
  69:
     glTranslatef(0.5, 0.0, 0.0);
  70:
     glMultiDrawArrays(GL_TRIANGLES, iFirsts, iCounts, 3);
  71:
     glPopMatrix();
  72:
  
  73:
     glFlush();
  74:
 }  

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glRectWithArrayDraw 目录,获取方式见文章最后关于获取博客完整源代码的说明。

按 上面例子中的顶点排序,我们没有办法再组织处一个完整的矩形了,这里我通过{0,1,2},{2,3,4}绘制出了3/4,然后补上了一个第一个点坐标通 过绘制{3,4,5},完成完整的矩形。记住array函数的按顺序特点就很好理解了。程序运行效果如下图,注意实现的不同导致与前面例子中的效果差异。

image13

 

范围限定顶点数组函数

事 实上除了上述函数,OpenGL还有一组为了优化而存在的指定索引范围的函数,(非Multi组的函数都有,在有些扩展中也有Multi的Range函 数)以Range加入上述函数名中的方式命名,使用方式与类似函数一致,仅仅是加入了两个参数表示索引的范围,以方便OpenGL的实现优化,如下例,就 是glDrawElements对应的范围函数glDrawRangeElements的例子,其他函数也类似,这里不再详述。

   1:
 glPushMatrix();
   2:
 glTranslatef(0.5, 0, 0);
   3:
 glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byTopIndices);
   4:
 glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byLeftIndices);
   5:
 glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byBottomIndices);
   6:
 glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byRightIndices);
   7:
 glPopMatrix();

 

OpenGL Reference Manual

glDrawRangeElements   — render primitives from array data 
C Specification 
void glDrawRangeElements(    GLenum      mode, 
     GLuint      start, 
     GLuint      end, 
     GLsizei      count, 
     GLenum      type, 
     const GLvoid *      indices); 
Parameters

mode

                        Specifies what kind of primitives to render. 
                        Symbolic constants 
                        GL_POINTS, 
                        GL_LINE_STRIP, 
                        GL_LINE_LOOP, 
                        GL_LINES, 
                        GL_TRIANGLE_STRIP, 
                        GL_TRIANGLE_FAN, 
                        GL_TRIANGLES, 
                        GL_QUAD_STRIP, 
                        GL_QUADS, 
                        and GL_POLYGON are accepted. 
start

                        Specifies the minimum array index contained in indices. 
end

                        Specifies the maximum array index contained in indices. 
count

                        Specifies the number of elements to be rendered. 
type

                        Specifies the type of the values in indices. Must be one of 
                        GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or 
                        GL_UNSIGNED_INT. 
indices

                        Specifies a pointer to the location where the indices are stored.

 

混合数组

原来的数组都是指定顶点的指定顶点,指定颜色的指定颜色,事实上OpenGL允许将顶点数组及颜色数组混用,即所谓的混合数组。

前 面glVertexPointer中的第三个参数跨距就是用来指定这种情况的数据的,事实上,我们还有更简单(更复杂?)的办法,那就是使用 glInterleavedArray函数以指定数据,需要注意的是glInterleavedArray与glVertexPointer等函数一样, 仅仅是指定数据而已,我们还得通过Draw一组的函数去绘制图形。并且,个人感觉,glInterleavedArrays的参数那个复杂啊。。。。《OpenGL Programming Guide 》),中甚至需要通过一张极为复杂的表来表示。这里我也不想做过多解释了,其参数之复杂也不是一下解释的清楚的。建议还是参考《OpenGL Programming Guide 》)一书Table 2-5 :  (continued) Variables that Direct glInterleavedArrays()的这个表比较好。

OpenGL Reference Manual

glInterleavedArrays — simultaneously specify and enable several interleaved arrays 
C Specification 
void glInterleavedArrays(    GLenum      format, 
     GLsizei      stride, 
     const GLvoid *      pointer); 
Parameters

format

                        Specifies the type of array to enable. Symbolic constants 
                        GL_V2F, 
                        GL_V3F, 
                        GL_C4UB_V2F, 
                        GL_C4UB_V3F, 
                        GL_C3F_V3F, 
                        GL_N3F_V3F, 
                        GL_C4F_N3F_V3F, 
                        GL_T2F_V3F, 
                        GL_T4F_V4F, 
                        GL_T2F_C4UB_V3F, 
                        GL_T2F_C3F_V3F, 
                        GL_T2F_N3F_V3F, 
                        GL_T2F_C4F_N3F_V3F, 
                        and 
                        GL_T4F_C4F_N3F_V4F 
                        are accepted. 
stride

                        Specifies the offset in bytes between each aggregate array element.

我这里有两个例子,分别使用了glVertexPointer,glColorPointer制定跨距的形式和glInterleavedArrays的形式,运行效果一致。

   1:
 //OpenGL初始化开始
   2:
 void
 SceneInit(int
 w,int
 h)
   3:
 {
   4:
     GLenum err = glewInit();
   5:
     if
(err != GLEW_OK)
   6:
     {
   7:
         MessageBox(NULL, _T("Error"
), _T("Glew init failed."
), MB_OK);
   8:
         exit(-1);
   9:
     }
  10:
  
  11:
     glEnableClientState(GL_VERTEX_ARRAY);
  12:
     glEnableClientState(GL_COLOR_ARRAY);
  13:
  
  14:
     // 前三列是颜色数组,后三列是顶点数组数据,
  15:
     static
 GLfloat fVertices[] = {    1.0, 0.0, 0.0, -0.5, -0.5, 0.0,  
  16:
                                     0.0, 1.0, 0.0,  0.5, -0.5, 0.0,  
  17:
                                     0.0, 0.0, 1.0,  0.5,  0.5, 0.0,  
  18:
                                     0.0, 0.0, 0.0, -0.5,  0.5, 0.0,  
  19:
                                     1.0, 1.0, 1.0,  0.0,  0.0, 0.0 };
  20:
  
  21:
     glVertexPointer(3, GL_FLOAT, 6 * sizeof
(GL_FLOAT), &fVertices[3]);
  22:
     glColorPointer(3, GL_FLOAT, 6 * sizeof
(GL_FLOAT), fVertices);
  23:
 }

为了更好分辨区别,我将上面例子中被替换掉功能的函数注释掉然后保留。

   1:
 void
 SceneInit(int
 w,int
 h)
   2:
 {
   3:
     GLenum err = glewInit();
   4:
     if
(err != GLEW_OK)
   5:
     {
   6:
         MessageBox(NULL, _T("Error"
), _T("Glew init failed."
), MB_OK);
   7:
         exit(-1);
   8:
     }
   9:
  
  10:
     //glEnableClientState(GL_VERTEX_ARRAY);
  11:
     //glEnableClientState(GL_COLOR_ARRAY);
  12:
  
  13:
     // 前三列是颜色数组,后三列是顶点数组数据,
  14:
     static
 GLfloat fVertices[] = {    1.0, 0.0, 0.0, -0.5, -0.5, 0.0,  
  15:
                                     0.0, 1.0, 0.0,  0.5, -0.5, 0.0,  
  16:
                                     0.0, 0.0, 1.0,  0.5,  0.5, 0.0,  
  17:
                                     0.0, 0.0, 0.0, -0.5,  0.5, 0.0,  
  18:
                                     1.0, 1.0, 1.0,  0.0,  0.0, 0.0 };
  19:
  
  20:
     //glVertexPointer(3, GL_FLOAT, 6 * sizeof(GL_FLOAT), &fVertices[3]);
  21:
     //glColorPointer(3, GL_FLOAT, 6 * sizeof(GL_FLOAT), fVertices);
  22:
     glInterleavedArrays(GL_C3F_V3F, 6 * sizeof
(GL_FLOAT), fVertices);
  23:
 }

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-18/glInterleavedArrays/目录,获取方式见文章最后关于获取博客完整源代码的说明。

可见glInterleavedArrays功能是很强大的,甚至能够管理状态的开启,可以预见,在大规模程序中,需要切换状态较多时,此函数对于效率的提升作用还是会较多的(其实也较有限),但是此函数实在复杂-_-!使用时甚至需要通过查表来寻找参数,晕倒。

 

小结

到 此为止,OpenGL顶点数组相关的用法算是基本讲完了,虽然顶点数组不是个OpenGL中的核心概念,但是对于效率的提升来说是很有价值的,所以 OpenGL提供了一系列一个比一个更有效率的函数,在程序规模扩大后,不仅效率会获得提升,事实上将数据按照OpenGL的规范存在数组中对于编程也会 带来很大的便利,相对于用数组加循环的方式来调用glVertex*会更加的容易理解和维护,毕竟,代码量的减少在很多时候那是工程容易维护的王道。

 

 

参考资料

1. 《OpenGL Reference Manual 》,OpenGL参考手册

2. 《OpenGL  编程指南》(《OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis  著,徐波译,机械工业出版社

3. 《Nehe OpenGL Tutorials》,Nehe著,在http://nehe.gamedev.net/   上可以找到教程及相关的代码下载,(有PDF版本教程下载)Nehe自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有中文版 ,各取所需吧。

4. 《OpenGL入门学习》 ,eastcowboy著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址:http://bbs.pfan.cn/post-184355.html

 

本系列下一篇 《Win32 OpenGL编程(6) 踏入3D世界

 

本OpenGL系列其他文章

1.    Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤

2. 《Win32 OpenGL编程(2) 寻找缺失的OpenGL函数

3. 《Win32 OpenGL编程(3) 基本图元(点,直线,多边形)的绘制

4. 《Win32 OpenGL编程(4) 2D图形基础(颜色及坐标体系进阶知识)

 

 

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://blog-sample-code.jtianling.googlecode.com/hg/

Mercurial使用方法见《分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code

posted on 2016-07-21 08:15  不吃鱼的猫  阅读(5141)  评论(0编辑  收藏  举报