OpenGL顶点数组
尽管前面给出的例子中只包含少量的坐标位置,但描述包含若干个对象的场景一般会复杂得多。我们先考虑描述一个简单的很基本的对象:图3.58中的单位立方体,为简化后面的讨论而使用了整数坐标。定义顶点坐标的直接方法是用一个双下标数组,例如:
GLint points [8][3] = {{0,0,0},{0,1,0},{1,0,0},{1,1,0},{0,0,1},{0,1,1},{1,0,1},{1,1,1}};
也可以先定义一个三维顶点位置的数据类型,然后给出作为单下标数组元素的每一顶点位置的坐标,
例如:
typedef GLint vertex3 [3]; vertex3 pt [8] = {{0,0,0},{0,1,0},{1,0,0},{1,1,0},{0,0,1},{0,1,1},{1,0,1},{1,1,1}};
下面要定义该对象的六个面。为此,分六次调用glBegin (GL_POLYGON}或glBegin(GL_QUADS)。我们必须明确每一个面的顶点顺序符合从立方体外部对其观察时为逆时针次序的要求。在下面的程序段中,我们指定每个立方体面为一个四边形,并且使用一个函数调用将数组下标值传给OpenGL图元子程序。图3.59给出了与立方体顶点位置对应的数组pt的下标值。
void quad (Glint n1, Glint n2, Glint n3, Glint n4) { glBegin (GL_QUADS); glVertex3iv (pt [n1]); glVertex3iv (pt [n2]); glVertex3iv (pt [n3]); glVertex3iv (pt [n4]); glEnd (); } void cube () { quad(6, 2, 3, 7); quad(5, 1, 0, 4); quad(7, 3, 1, 5); quad(4, 0, 2, 6); quad(2, 0, 1, 3); quad(7, 5, 4, 6); }
这样,指定一个面要用六个OpenGL函数,共有六个面需要指定。在加入颜色描述和其他参数后,显示立方体的程序很容易包含一百个以上的OpenGL函数调用。而有许多复杂对象的场景会需要更多的函数调用。
从上面的例子中可以看出,复杂的场景描述需要使用几百或几千个坐标描述。另外还必须为各个对象建立各种属性和观察参数。因此,对象和场景描述要使用大量的函数调用,这对系统资源提出了要求并减慢了图形程序的执行。复杂显示进一步的问题是对象表面(如图3.58中的立方体)通常有共享顶点。使用已讨论过的方法,这些共享顶点需要多次指定。
为了简化这些问题,OpenGL提供了一种机制来减少处理坐标信息的函数调用数量。使用顶点数组(vertex array ),可以利用很少的函数调用来安排场景的描述信息。步骤如下:
1.引用函数glEnableClientState (GL_VERTEX_ARRAY)激活OpenGL的顶点数组特性。
2.使用函数glVertexPointer指定顶点坐标的位置和数据格式。
3.使用子程序如glDrawElements显示场景,该子程序可处理多个图元而仅需少量的函数调用。
使用前面定义的pt数组,实现下列程序示例中的三步。
glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (3, GL_INT, 0, pt); GLubyte vertIndex [] = (6, 2, 3, 7, 5, 1, 0, 4, 7, 3, 1, 5, 4, 0, 2, 6, 2, 0, 1, 3, 7, 5, 4, 6); glDrawElements (GL_QUADS, 24, GL_UNSIGNED_BYTE, verIndex);
第一条命令glEnableClientState (GL_VERTEX_ARRAY),激活了客户/服务器系统中客户端的能力(此时是顶点数组)。因为客户端(运行主程序的机器)保留图形的数据,顶点数组必须在那里。如第2章所指出的,服务器(如工作站)发出命今并显示图形。当然,单个计算机既是客户端又是服务器。OpenGL的顶点数组特性用下列命令来使其无效。
glDisableClientState (GL_VERTEX_ARRAY);
接下来为函数glVertexPointer提供对象顶点坐标的位置和格式。glVertexPointer的第一个参数在此例中为3,指出每一个项点描述中的坐标数目。顶点坐标的数据类型用函数中第二个参数OpenGL符号常量来指定。此例中数据类型为GL_INT。另外的数据类型用符号常量GL_BYTE、GL_ SHORT、GL_FLOAT和GL_DOUBLE来指定。第三个参数用来给出连续顶点之间的字节位移。这一参数的目的是允许多种类型的数据(如坐标和颜色)捆绑在同一个数组内。由于我们仅给出坐标数据,所以为这个位置参数赋值为0。函数glVertexPointer的最后一个参数指向包含坐标值的顶点数组。
立方体顶点的所有索引存放在数组vertIndex中。其中每一个索引是对应于该顶点值的数组pt的下标。该索引表被当做函数glDrawElements的最后一个参数,并由显示立方体的四边界表面的第一个参数是图元GL_QUADS使用。第二个参数指定数组vertIndex中的元素数量。因为一个四边形有4个顶点,所以我们指定24 , glDrawElements函数每次取出4个顶点来显示一个立方体面,直到24个顶点用完。这样,使用一个函数调用完成了整个立方体所有面的显示。glDrawElements函数的第三个参数给出索引值的类型。因为此时所用的索引是小整数,我们指定其为GL_UNSIGNED_BYTE类型。另外两种可用的索引类型是GL_UNSIGNED_SHORT和GL_UNSIGNED_INT。
也可将其他的信息与坐标值一起放进顶点数组中用于场景描述。我们可以在数组(由函数glDrawElements引用)中指定对象的颜色值和其他属性。为了提高效率,可以交替使用各种数组。下一章将讨论实现这些属性的方法。