OpenGL vertex array学习

OpenGL Vertex Array

overview

相比起立即模式下(在glBegin()glEnd()之间)定义每个顶点数据,可以将这些顶带你数据保存在一个包括顶点坐标,法向量,材质坐标和颜色信息的数组中。并且你可以通过解引用具有数组索引的数组元素来只绘制一几何图元。

下列代码在立即模式下绘制一个正方体,每个面都需要调用6次glVertex*()来绘制两个三角形(OpneGL3.1以后不再支持了GL_QUADS的使用,所以这里改用GL_TRIANGLES来绘制)。例如,前面有v0-v1-v2和v2-v3-v0两个三角形。一个正方体有六个面,因此总共要调用glVertex*()36次,如果你还定义了法向量,材质坐标和颜色,会成倍增长函数调用的次数。

此外,v0顶点由三个相邻的面共享:前面,右面和上面。在立即模式中,你需要提供6次这个共享的顶点,每一面都要用两次。
在这里插入图片描述

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 faces

glEnd();

使用顶点数组减少函数调用的次数和多余的共享顶点。因此可以提高渲染性能。有三种不同的函数可以用于顶点数组:glDrawArrays()glDrawElements()glDrawRangeElement()。尽管最好的方法是使用vertex buffer objects(VBO)或display list。

Initialization

OpenGL提供glEnableClientState()glDisableClientState()函数来激活和关闭6种不同的数组。另外,还有6种函数用于定义数组准确的位置(地址),OpenGL可以访问你应用中的数组。

  • glVertexPointer(): 定义指向顶点坐标数组的指针
  • glNormalPointer(): 定义指向法向量数组的指针
  • glColorPointer(): 定义指向顶点坐标数组的指针
  • glIndexPointer(): 定义指向RGB颜色数组的指针
  • glTexCoordPointer(): 定义指向索引颜色数组的指针
  • glEdgeFlagPointer(): 定义指向边缘标志数组的指针

每个定义的函数参数都不相同。具体看OpenGL手册边缘标志用于标记顶点是否在边缘上。因此,如果glPolygonMode()置为GL_LINE,边缘标记启用后的唯一边缘是可见的。

glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
//size: 顶点的坐标数
//type: GL_FLOAT, GL_SHORT, GL_INT or GL_DOUBLE.
//stride: 偏移到下一个顶点的字节数(用于交叉存取数组)
//pointer: 指向顶点数组的指针
    
glNormalPointer(GLenum type, GLsizei stride, const GLvoid* pointer)
//type: GL_FLOAT, GL_SHORT, GL_INT or GL_DOUBLE.
//stride: 偏移到下一个法向量的字节数(用于交叉存取数组)
//pointer: 顶点数组的指针

顶点数组位于应用中(系统内存),在用户端。并且OpenGL服务器端可以访问他们。这就是为啥他们是顶点数组的独特命令,glEnableClientState()glDisableClientState()而不是用glEnable()glDisable()

glDrawArrays()

glDrawArrays()直接的而不是跳跃或跃迁的从激活数组中读取顶点数据。因为glDrawArrays()不允许在顶点数组中跳来跳去,还是需要重复为各面定义共享顶点。

glDrawArrays()有三个参数。第一个参数是原始类型,第二个参数是数组的起始偏移量,最后一个参数是传递给OpenGL渲染管线的顶点数。下面是画正方体的例子,第一个参数是GL_TRIANGLES,第二个参数是0,代表从数组头开始,最后一个参数是36,一个正方体有6个面,每个面需要6个顶点画两个三角形。

GLfloat vertices[] = {...}; // 36 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_TRIANGLES, 0, 36);

// deactivate vertex arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);

通过使用glDrawArrays(),你可以用一个glDrawArrays()调用替换36次glVertex*()的调用。然而,还是需要重复使用共享顶点,数组中定义的顶点数还是36,而不是8。glDrawElement()可以用于减少数组中的顶点数目,可以向OpenGL传输更少的数据。

glDrawElement()

glDrawElement()通过在具有关联数组索引的顶点数组中跳跃来绘制一系列基元。同时减少了函数调用和传输的顶点数目。此外,OpenGL可以缓存最近处理的顶点被重用它们,无需多次将相同的顶点重新发送到顶点变换管道中。

glDrawElement()需要4个参数,第一个参数是基本类型,第二个参数是索引数组的索引数,第三个是索引数组的数据类型,最后一个参数是索引数组的地址。在此例中,参数为GL_TRIANGLES,36,GL_UNSIGNED_BYTE,索引数组。

GLfloat vertices[] = {...};          // 8 of vertex coords
GLubyte 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 array
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// draw a cube
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);

// deactivate vertex arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);

顶点坐标数组的大小现在是 8,这与立方体中的顶点数完全相同,没有任何冗余条目。

索引数组的数据类型是 GLubyte,而不是 GLuintGLushort。应该是能够容纳最大索引数的最小数据类型,以减少索引数组的大小,否则可能会因索引数组的大小而导致性能下降。由于顶点数组包含 8 个顶点,因此 GLubyte 足以存储所有索引。

应该考虑的另一件事是共享顶点处的法向量。如果共享顶点的相邻多边形的法线都不同,则应指定与面数一样多的法线向量,每个面一次。

例如,顶点 v0 被正面、右侧和顶面共享,但无法在 v0 共享法线。正面法线为n0,右面法线为n1,顶面法线为n2。对于这种情况,共享顶点的法线是不一样的,顶点不能再在顶点数组中只定义一次。必须在数组中为顶点坐标多次定义它,以便匹配普通数组中相同数量的元素。具有适当法线的典型立方体需要 24 个独特的顶点:6 个面 × 每边 4 个顶点。

glDrawRangeElements()

glDrawElements()一样,glDrawRangeElements()也适用于在顶点数组周围跳跃。但是,glDrawRangeElements()还有两个参数(开始和结束索引)来指定要预取的顶点范围。通过添加此范围限制,OpenGL 可能能够在渲染之前仅获取有限数量的顶点数组数据,并且可能会提高性能。

glDrawRangeElements() 中的附加参数是开始和结束索引,然后 OpenGL 从这些值中预取有限数量的顶点:end - start + 1。并且索引数组中的值必须位于开始和结束索引之间。并非必须引用范围(start、end)中的所有顶点。但是,如果您指定一个稀疏使用的范围,则会导致对该范围内许多未使用的顶点进行不必要的处理。

GLfloat vertices[] = {...};          // 8 of vertex coords
GLubyte 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 array
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// draw first half, range is 6 - 0 + 1 = 7 vertices used
glDrawRangeElements(GL_TRIANGLES, 0, 6, 18, GL_UNSIGNED_BYTE, indices);

// draw second half, range is 7 - 1 + 1 = 7 vertices used
glDrawRangeElements(GL_TRIANGLES, 1, 7, 18, GL_UNSIGNED_BYTE, indices+18);

// deactivate vertex arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);

可以通过使用带有 GL_MAX_ELEMENTS_VERTICESGL_MAX_ELEMENTS_INDICESglGetIntegerv()找出要预取的最大顶点数和要引用的最大索引数。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值