OpenGL:Vertex Array数组顶点绘图

概要

你可以在一系列数组保存顶点信息,如顶点坐标,向量,纹理坐标,颜色信息,来代替立即模式下输入顶点信息的方法(即在glBegin()和glEnd()中指定顶点信息)。你可以使用数组索引找到数组中的元素,来画出几何体。


仔细看下面使用立即模式下画立方体的代码。

[cpp]  view plain  copy
  1. glBegin(GL_TRIANGLES);  // draw a cube with 12 triangles  
  2.   
  3.     // front face =================  
  4.     glVertex3fv(v0);    // v0-v1-v2  
  5.     glVertex3fv(v1);  
  6.     glVertex3fv(v2);  
  7.   
  8.     glVertex3fv(v2);    // v2-v3-v0  
  9.     glVertex3fv(v3);  
  10.     glVertex3fv(v0);  
  11.   
  12.     // right face =================  
  13.     glVertex3fv(v0);    // v0-v3-v4  
  14.     glVertex3fv(v3);  
  15.     glVertex3fv(v4);  
  16.   
  17.     glVertex3fv(v4);    // v4-v5-v0  
  18.     glVertex3fv(v5);  
  19.     glVertex3fv(v0);  
  20.   
  21.     // top face ===================  
  22.     glVertex3fv(v0);    // v0-v5-v6  
  23.     glVertex3fv(v5);  
  24.     glVertex3fv(v6);  
  25.   
  26.     glVertex3fv(v6);    // v6-v1-v0  
  27.     glVertex3fv(v1);  
  28.     glVertex3fv(v0);  
  29.   
  30.     ...                 // draw other 3 faces  
  31.   
  32. glEnd();  

        每一个面要调用glVertex*()函数六次,来画出二个三角形。如前面的三角片v0-v1-v2和三角片v2-v2-v0。一个立方体有六个面,共要调用glVertex*()函数36次。如果你再指定法向量,纹理坐标和顶点对应的颜色,将增加调用OpenGL函数的次数。顶点v0被三个邻近的面共享。前面、右面、顶面。在立即模式下,你使用了此顶点6次,twice for each side as show in the code。使用顶点数组,可以减少函数调用次数和冗余的共享顶点。

      有三种不同的OpenGL函数可使用顶点数组。glDrawArrays(),glDrawEelemnts()和glDrawRangeEelemnts().更好的方法是使用vertex buffer objects和display lists。

初始化OpenGL提供glEnableClientState()和glDisableClientState()函数来激活和禁止6种不同类型的数组。6个函数可指定这些数组的地址。OpenGL可在你的程序里访问这些数组。(me:即这些数组放在内存里,而不是显卡中)。
glVertexPointer():  指定一个指向顶点坐标的数组
glNormalPointer():  指定一个指向法向量的数组
glColorPointer():   指定一个指向颜色的数组
glIndexPointer():   指定一个指向索引的数组
glTexCoordPointer():  指定一个指向纹理的数组
glEdgeFlagPointer():  指定一个指向边标志的数组

glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
size: 顶点坐标的数量,2为2D点,3为3D点
type: GL_FLOAT, GL_SHORT, GL_INT或GL_DOUBLE.
stride: 到下一个顶点的偏移字节数(用于interleaved数组)
pointer: 指向顶点的数组

glNormalPointer(GLenum type, GLsizei stride, const GLvoid* pointer)
type: GL_FLOAT, GL_SHORT, GL_INT or GL_DOUBLE.
stride: 到下一个顶点的偏移字节数(用于interleaved数组)
pointer: 指向顶点的数组
注意,顶点数组在你的程序中(系统内存中),即在客户端。OpenGL在服务端访问它们。用glEnableClientState()和glDisableClientState()代替glEnable和glDisable()。

glDrawArrays()
glDrawArrays()从数组中读取顶点数据,但不能在数组中跳来跳去(me:即按索引顺序0,1,2,...,n读取数组元素,不能随机读取数组元素)。因为glDrawArrays()不允许在顶点数组中跳来跳去,你仍要重复每个面上的共享顶点。

glDrawArrays()有三个参数。第一个参数是图元类型。第二个参数是数组的偏移值。最后一个参数是顶点数组中顶点的数量。在上面画立方体的例子中,第一个参数是GL_TRIANGLES,第二个参数是,即从数组起点处开始。最后一个参数是36.一个立方体有6张面。每张面有6个顶点。每张面有二个三角形。共6x6=36
[cpp]  view plain  copy
  1. GLfloat vertices[] = {...}; // 36 of vertex coords  
  2. ...  
  3. // activate and specify pointer to vertex array  
  4. glEnableClientState(GL_VERTEX_ARRAY);  
  5. glVertexPointer(3, GL_FLOAT, 0, vertices);  
  6.   
  7. // draw a cube  
  8. glDrawArrays(GL_TRIANGLES, 0, 36);  
  9.   
  10. // deactivate vertex arrays after drawing  
  11. glDisableClientState(GL_VERTEX_ARRAY);  
你可以使用一个glDrawArrays()代替36个glVertex*()。然而,我们仍要重复共享的顶点,这样在数组中定义的顶点数仍旧是36个而不是8个。glDrawElements()可以减少在数组中顶点的数量,这样可向OpenGL传递更少的数据。


glDrawElements()

glDrawElements()可以使用相关的索引数组,按索引访问顶点数组,来画出图像。它即可以减少函数的调用次数也可以减少顶点的传输量。OpenGL可能缓存最近的被处理的结果并重用它们,而不用多次发送同样的顶点数据到渲染管线中。

glDrawElements需要4个参数:第一个参数是图元的类型,第二个参数是索引数组中索引的数量。第三个参数是索引数组的类型。最后一个参数是索引数组的地址。在此例中,参数为GL_TRIANGLES,36,GL_UNSIGNED_BYTE和索引数组地址。
<me>
void glVertexPointer( GLint   size,
  GLenum    type,
  GLsizei    stride,
  const GLvoid *   pointer);
glVertexPointer定义一个顶点坐标数量。
size 为每个顶点的坐标数量。必须是2,3,4。初始值为4.即(x,y),(x,y,z)及齐次坐标(x,y,z,w)
type 每个坐标分量的类型。可以是GL_SHORT,GL_INT,GL_FLOAT,或者是GL_DOUBLE。初始值为GL_FLOAT.
stride 指定两个连续顶点的字节偏移量。如果stride是0,顶点在数组中紧密排布。初始值为0。
pointer 指定数组地址。

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
        在此例中,顶点为三维点,故第一个参数为3,vertices的元素类型为GL_Float,故第二个参数为GL_FLOATglVertexPointer指定了glDrawElements要用到的顶点数据数组。而后,要为glDrawElements指定一个索引数组,此后glDrawElements从最近一次由glVertexPointer指定的顶点数组中,按索引数组indices中索引的顺序取顶点,完成绘图工作。
</me>
[cpp]  view plain  copy
  1. GLfloat vertices[] = {...};          // 8 of vertex coords  
  2. GLubyte indices[] = {0,1,2, 2,3,0,   // 36 of indices  
  3.                      0,3,4, 4,5,0,  
  4.                      0,5,6, 6,1,0,  
  5.                      1,6,7, 7,2,1,  
  6.                      7,4,3, 3,2,7,  
  7.                      4,7,6, 6,5,4};  
  8. ...  
  9. // activate and specify pointer to vertex array  
  10. glEnableClientState(GL_VERTEX_ARRAY);  
  11. glVertexPointer(3, GL_FLOAT, 0, vertices);  
  12.   
  13. // draw a cube  
  14. glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);  
  15.   
  16. // deactivate vertex arrays after drawing  
  17. glDisableClientState(GL_VERTEX_ARRAY);  
       顶点坐标数组的大小现在为8,此为立方体没有冗余的顶点数。

       注意索引数组的数据类型为GLubyte,而不是GLuint或GLushort。这样做,是为了减小索引数组的大小。GLubyte的大小可以
满足此例中最大索引数量的需要,否则可能会引起数据丢失。因为顶点数组包含8个顶点,GUubtye足够储存所有索引了。
 

在共享顶点上的不同的法向量


      你应该思考的一件事是在共享顶点上的不同的法向量。如果相邻多边形的法向量有同一个顶点,但法向量不同,那么这些法向量应该和面一样多,一个面一个法向量。举个例子,v0顶点被前面,右面和顶面共享,但在v0顶点上的法向量却不能被共享。前面的法向量为n0,右面的法向量为n1,顶面的法向量为n2。这种情况下,在共享顶点的法向量是不一样的。顶点不能在顶点数组里定义一次。必须在顶点坐标数组里定义一个顶点多次,这样做是为了使顶点坐标数组中元素的数量与法向量数组的大小相同。一个立方体有24个顶点:6个面x4。参见示例 代码

glDrawRangeElements()

        类似glDrawElements(), glDrawRangeElements()也可随机访问顶点数组。 然而,glDrawRangeElements()多了两个参数(start and end index) ,用来事先指定顶点的范围。通过添加范围约束,OpengGL可能事先得到顶点数据数组的受限大小,这可能提升性能。 在glDrawRangeElements中额外的参数是start和end index, OpenGL可以事先得到受限的顶点数组的大小:end - start + 1. 此值必须在start和end index之间。注意,晨不是所有在(start,end)范围内的的顶点都会都引用。But, if you specify a sparsely used range, it causes unnecessary process for many unused vertices in that range.
[cpp]  view plain  copy
  1. GLfloat vertices[] = {...};          // 8 of vertex coords  
  2. GLubyte indices[] = {0,1,2, 2,3,0,   // first half (18 indices)  
  3.                      0,3,4, 4,5,0,  
  4.                      0,5,6, 6,1,0,  
  5.   
  6.                      1,6,7, 7,2,1,   // second half (18 indices)  
  7.                      7,4,3, 3,2,7,  
  8.                      4,7,6, 6,5,4};  
  9. ...  
  10. // activate and specify pointer to vertex array  
  11. glEnableClientState(GL_VERTEX_ARRAY);  
  12. glVertexPointer(3, GL_FLOAT, 0, vertices);  
  13.   
  14. // draw first half, range is 6 - 0 + 1 = 7 vertices used  
  15. glDrawRangeElements(GL_TRIANGLES, 0, 6, 18, GL_UNSIGNED_BYTE, indices);  
  16.   
  17. // draw second half, range is 7 - 1 + 1 = 7 vertices used  
  18. glDrawRangeElements(GL_TRIANGLES, 1, 7, 18, GL_UNSIGNED_BYTE, indices+18);  
  19.   
  20. // deactivate vertex arrays after drawing  
  21. glDisableClientState(GL_VERTEX_ARRAY);  
      使用glGetIntegerv(),传入参数GL_MAX_ELEMENTS_VERTICES或GL_MAX_ELEMENTS_INDICES,你便可以查询顶点数组和索引数组的最大值。注意glDrawRangeElements()在OpenGL1.2及更高版本中有效。

猜你喜欢

转载自blog.csdn.net/qq_25241325/article/details/80580466