OpenGL编程指南6:顶点数组

1.前言

前面的例子中,我们可以看到,OpenGL需要进行大量的函数调用才能完成对几何图元的渲染。绘制一个20条边的多边形至少需要22个函数调用。首先调用一个glBegin(),然后为每个顶点调用一次函数,最后调用1次glEnd().如果我们还要添加其他的额外信息(如多边形边界标志或表面法线),在每个顶点上还要增加函数调用。这可能会成倍地增加渲染几何物体所需要的函数调用数量。在很多系统中,函数调用具有相当大的开销,可能会影响应用程序的性能。

另一个问题是,相邻多边形的共享定点的冗余处理。如上图所示的立方体具有六个面和8个共享顶点。遗憾的是,如果按照标准方法描述这个物体,每个顶点必须被指定3次,因为每个顶点都在三个面上。这样就造成了大量的冗余。
幸运的是,OpenGL提供了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,并用少量函数调用(与顶点数组的数量相仿)访问这些数据。使用顶点数组函数,一个拥有20条边的多边形的顶点可以放在一个数组中,并且只通过1个函数进行调用。其实有其他如文理、法向量等,也可以只通过1个函数进行调用。
把数据放在顶点数组中可以提高应用程序的性能。使用顶点数组可以减少函数的调用次数,从而提高性能,也可以避免共享顶点的冗余处理。

2.顶点数组对几何图形渲染步骤

1.激活/启用最多可达8个数组

每个数组用于存储不同类型的数据:顶点坐标、表面法线、RGBA颜色、辅助颜色、颜色索引、雾坐标、纹理坐标以及多边形的边界标志。
该步骤是调用void glEnableClientState(GLenum array)函数实现。
array指定了需要启用的数组。其参数可以是下面常量:GL_VERTEX_ARRAY / GL_COLOR_ARRAY / GL_SECONDARY_COLOR_ARRRAY / GL_INDEX_ARRAY / GL_NORMAL_ARRAY / GL_FOG_COORDINATE_ARRAY / GL_TEXTURE_COORD_ARRAY / GL_EDGE_FLAG_ARRAY.
例如,如果我们需要光照,就可以为每一个顶点定义一条发现向量。这种情况下使用顶点数组时,需要同时激活表面法向数组和顶点坐标数组。
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
void display(void)  
{  
    glClear(GL_COLOR_BUFFER_BIT);  
    GLfloat vertices[]={0.25,0.25,
                        0.75,0.25,
                        0.75,0.75,
                        0.25,0.75};
    GLfloat colors[]={1.0, 0.0, 0.0,
                      1.0, 1.0, 0.0,
                      0.0, 1.0, 0.0,
                      0.0, 0.0, 1.0};
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glVertexPointer(2,GL_FLOAT,0,vertices);
    glColorPointer (3,GL_FLOAT,0,colors);
    glBegin(GL_POLYGON);  
		glArrayElement(0);
		glArrayElement(1);
		glArrayElement(2);
		glArrayElement(3);
    glEnd();  
  
    glFlush();  
}  
输出结果如下:

2.把数据放入数组中

这些数据是通过指针进行访问的。

3.绘制几何图形

绘制图形关键点在于“解引用”,而解引用关键在于glArrayElement()函数。该函数通常在glBegin()和glEnd()之间调用。否则,glArrayElement()函数就会设置所有启用数组的当前状态(顶点除外,因为它不存在当前状态)。
下面例子展示的是,使用取自启用的顶点数组的0,3,4号顶点绘制三角形。
 glBegin(GL_POLYGON);  
		glArrayElement(0);
		glArrayElement(2);
		glArrayElement(3);
    glEnd(); 
输出结果:
由于glArrayElemrnt()函数对于每个顶点只调用一次,因此它可能减少对函数的调用数量,从而提高程序的整体性能。

3.重启图元

在OpenGL绘制图形时,可能需要绘制多个并不相连的图形。这样的情况下这几个图形没法被当做一个图形来处理。也就需要多次调用 DrawArrays 或 DrawElements. 如果图形很多,可能会需要用一个循环来调用:
for (int i = 0; i < num_objects; i++) {
    glDrawArrays(GL_TRIANGLES,
                object[n]->first_vertex,
                object[n]->vertex_count);
}
每一次调用OpenGL 的绘制函数,都需要一定的资源开销,如果每一帧调用太多次,会对程序的性能产生较大的影响。提高性能的办法就是调用一次绘制函数,画出分散的图形。
一种方法是使用 glMultiDrawElements 函数来代替旧的绘制函数,可以减少调用次数,仅需调用此函数一次即可。但是这个函数在OpenGL ES 没有得到支持。而且使用这个函数,仍然需要将每一个分散的图形维护一组单独的顶点坐标/纹理坐标,这个是免不了的,这些数据仍然需要分开上传,还是会消耗一定的资源。针对这种情况使用图元重启会更加合适。

3.1 基本介绍

考虑通常的情况,当用户绘制 GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_LINE_STRIP, GL_LINE_LOOP 这些图元时,所有的绘制点按照特定的顺序被连起来,以形成一个最终的复杂图形,也就是说最终的复杂图形由多个相连的三角形或线段组成。像上面提到的情况,想要绘制分散的图形,应该怎么办?
图元重启(Primitive restart) 允许用户绘制不连续的、分散的图形。考虑使用 glDrawElements 函数,绘制时按照indices所指定的顶点的顺序来绘制的。此时可以指定某一个值,该值表示一个重启的标志。遇到这个值的时候,OpenGL不会绘制图元,而是结束上一段绘制,然后重新启动新的绘制,也就是說用后面的索引所指定的顶点来从头绘制一个图形。
举个例子:比如指定8为重启的标志,遇到8就重启。


上面的是不启用图元重启的情况,即通常的情况。
下面的是启用图元重启的情况,我们可以看到,从9开始,又重新从头开始绘制Triangle strip了。

3.2 执行过程

指定重启位置的数值,在桌面版的OpenGL是可以自行设定的,glPrimitiveRestartIndex​ 函数指定重启的标志。在OpenGL ES 无法自行指定,只能用给定的值。
首先设置启用图元重启:
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
Fixed index就代表了使用固定的重启的标志,具体数值和indices内数据类型有关。glDrawElements 的indices参数类型必须是以下的一种:GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT. 那么分别对应的重启的标志就是 2^8 - 1;2^16 - 1;2^32 - 1;也就是說重启的标志的数值就是indices数组所能允许的最大值。这个值一般来说是不会被用到的,拿来当标志正好。
所以我们只需要在 indices 里面的合适的位置插入一个标志,然后再调用 glDrawElements 函数即可实现图元重启。
下面代码片段是一个具体的例子:


// Prepare index buffer data (not shown: vertex buffer data, loading vertex and index buffers)
GLushort indexData[11] = {
    0, 1, 2, 3, 4,    // triangle strip ABCDE
    0xFFFF,           // primitive restart index (largest possible GLushort value) 
    5, 6, 7, 8, 9,    // triangle strip FGHIJ
};
// Draw triangle strips
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDrawElements(GL_TRIANGLE_STRIP, 11, GL_UNSIGNED_SHORT, 0);

4.参看资料

[1].Primitive Restart Makes GPGPU Tech Sparkle
[2].Combining Drawing Functions, Combining Geometry Using Primitive
[3].TechniquesforWorkingwithVertexData

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言 第1章OpenGL简介 1.1什么是OpenGL? 1.2一段简单的OpenGL代码 1.3OpenGL函数的语法 1.4OpenGL是个状态机 1.5OpenGL渲染管线 1.5.1显示列表 1.5.2求值器 1.5.3基于顶点的操作 1.5.4图元装配 1.5.5像素操作 1.5.6纹理装配 1.5.7光栅化 1.5.8片断操作 1.6与OpenGL相关的函数库 1.6.1包含文件 1.6.2GLUT,OpenGL实用工具包 1.7动画 1.7.1暂停刷新 1.7.2动画=重绘+交换 第2章状态管理和绘制几何物体 2.1绘图工具箱 2.1.1清除窗口 2.1.2指定颜色 2.1.3强制完成绘图操作 2.1.4坐标系统工具箱 2.2描述点、直线和多边形 2.2.1什么是点、直线和多边形? 2.2.2指定顶点 2.2.3OpenGL几何图元 2.3基本状态管理 2.4显示点、直线和多边形 2.4.1点的细节 2.4.2直线的细节 2.4.3多边形的细节 2.5法线向量 2.6顶点数组 2.6.1步骤1:启用数组 2.6.2步骤2:指定数组的数据 2.6.3步骤3:解引用和渲染 2.7缓冲区对象 2.7.1创建缓冲区对象 2.7.2激活缓冲区对象 2.7.3用数据分配和初始化缓冲区对象 2.7.4更新缓冲区对象的数据值 2.7.5清除缓冲区对象 2.7.6使用缓冲区对象存储顶点数据数据 2.8属性组 2.9关于创建多边形表面模型的一些提示 2.9.1例子:创建一个二十面体 第3章视图 3.1简介:照相机比喻 3.2.1一个简单的例子:绘制立方体 3.1.2通用的变换函数 3.2视图和模型变换 3.2.1对变换进行思考 3.2.2模型变换 3.2.3视图变换 3.3投影变换 3.3.1透视投影 …… 第4章颜色 第5章光照 第6章混合、抗锯齿、雾和多边形偏移 第7章显示列表 第8章绘制像素、位图、字体和图像 第9章纹理贴图 第10章帧缓冲区 第11章分格化和二次方程表面 第12章求值器和NURBS 第13章选择和反馈 第14章OpenGL高级技巧 第15章OpenGL着色语言 附录A操作顺序 附录B状态变量 附录COpenGL和窗口系统 附录DGLUT(OpenGL实用工具库)基础知识 附录E计算法线向量 附录F齐次坐标和变换矩阵 附录G编程提示 附录HOpenGL的不变性规则 附录IOpenGL着色语言内置的变量和函数 术语表

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值