OpenGL Vertex Array

来源:互联网 发布:云计算网络虚拟化技术 编辑:程序博客网 时间:2024/05/22 00:08

原文地址http://www.songho.ca/opengl/gl_vertexarray.html

概要

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


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

glBegin(GL_TRIANGLES);  // draw a cube with 12 triangles    // front face =================    glVertex3fv(v0);    // v0-v1-v2    glVertex3fv(v1);    glVertex3fv(v2);    glVertex3fv(v2);    // v2-v3-v0    glVertex3fv(v3);    glVertex3fv(v0);    // right face =================    glVertex3fv(v0);    // v0-v3-v4    glVertex3fv(v3);    glVertex3fv(v4);    glVertex3fv(v4);    // v4-v5-v0    glVertex3fv(v5);    glVertex3fv(v0);    // top face ===================    glVertex3fv(v0);    // v0-v5-v6    glVertex3fv(v5);    glVertex3fv(v6);    glVertex3fv(v6);    // v6-v1-v0    glVertex3fv(v1);    glVertex3fv(v0);    ...                 // draw other 3 facesglEnd();

        每一个面要调用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
GLfloat vertices[] = {...}; // 36 of vertex coords...// activate and specify pointer to vertex arrayglEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertices);// draw a cubeglDrawArrays(GL_TRIANGLES, 0, 36);// deactivate vertex arrays after drawingglDisableClientState(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>
GLfloat vertices[] = {...};          // 8 of vertex coordsGLubyte indices[] = {0,1,2, 2,3,0,   // 36 of indices                     0,3,4, 4,5,0,                     0,5,6, 6,1,0,                     1,6,7, 7,2,1,                     7,4,3, 3,2,7,                     4,7,6, 6,5,4};...// activate and specify pointer to vertex arrayglEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertices);// draw a cubeglDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);// deactivate vertex arrays after drawingglDisableClientState(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.
GLfloat vertices[] = {...};          // 8 of vertex coordsGLubyte indices[] = {0,1,2, 2,3,0,   // first half (18 indices)                     0,3,4, 4,5,0,                     0,5,6, 6,1,0,                     1,6,7, 7,2,1,   // second half (18 indices)                     7,4,3, 3,2,7,                     4,7,6, 6,5,4};...// activate and specify pointer to vertex arrayglEnableClientState(GL_VERTEX_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertices);// draw first half, range is 6 - 0 + 1 = 7 vertices usedglDrawRangeElements(GL_TRIANGLES, 0, 6, 18, GL_UNSIGNED_BYTE, indices);// draw second half, range is 7 - 1 + 1 = 7 vertices usedglDrawRangeElements(GL_TRIANGLES, 1, 7, 18, GL_UNSIGNED_BYTE, indices+18);// deactivate vertex arrays after drawingglDisableClientState(GL_VERTEX_ARRAY);
      使用glGetIntegerv(),传入参数GL_MAX_ELEMENTS_VERTICES或GL_MAX_ELEMENTS_INDICES,你便可以查询顶点数组和索引数组的最大值。注意glDrawRangeElements()在OpenGL1.2及更高版本中有效。
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 深夜吃撑了难受怎么办 吃撑了肚子难受怎么办 喝水喝撑了难受怎么办 晚饭吃撑了难受怎么办 吃多了肚子难受怎么办 电信4g信号变3g怎么办 五险交了3月离职怎么办 怎么办工行携程灵通卡 易买得消费卡怎么办 贷款回执单丢了怎么办 医院挂号过号了怎么办 脑子里总是胡思乱想睡不着怎么办 被妹夫给睡了怎么办 被妹夫抱了下怎么办 最里面牙齿烂了怎么办 老公有外遇不承认怎么办 对老公疑心太重怎么办 当你发现老公出轨怎么办 发现自己老婆出轨了怎么办 笔袋密码忘记了怎么办 海关锁密码忘了怎么办 800度近视献了血怎么办 当老公有了外遇妻子该怎么办 妻子有外遇我该怎么办 喜欢拔粗的头发怎么办 小腿长水泡很痒怎么办 脚丫有水泡又痒怎么办 脚上起水泡怎么办很疼 脚上反复长水泡怎么办 高考没考上二本怎么办 车子被扣了60分怎么办 科目三挂了三次怎么办 装修到一半跑了怎么办 13岁自慰被发现怎么办 应用锁密码忘了怎么办 3d缺少dll文件怎么办 真三国无双6闪退怎么办 孙策死了大乔怎么办 30周羊水偏多怎么办 怀孕34周羊水多怎么办 孕34周羊水偏多怎么办