版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1,OpenGL及二维编程,2,14.07.2020,华中科技大学计算机学院 李国宽,2,1、OpenGL简介 OpenGL(Open Graphics Library,开放图形库),是一个二维/三维的计算机图形和模型库,它源于SGI(Silicon Graphics inc.)公司为其图形工作站开发的IRIS GL,在跨平台移植过程中发展成为OpenGL。 目前, OpenGL已成为开放的国际图形标准。,2.1 从一个简单的示例程序开始,3,14.07.2020,3,OpenGL的功能: 模型绘制 绘制点、线和多边形,构造三维模型。 模型观察 建立变换(坐标变换,投影变换,视窗变换)。 颜色
2、模式的指定:RGBA模式和颜色索引模式 光照应用 图象效果增强:反走样、混合和雾化 位图和图象处理 纹理映射 实时动画:双缓存技术(double buffer) 交互技术 提供人机交互的接口。,4,OpenGL的特点 从程序开发人员的角度来看,OpenGL是一组绘图命令的API集合。利用这些API能够方便地描述二维和三维几何物体,并控制这些物体按某种方式绘制到显示缓冲区中。OpenGL的API集提供了物体描述、平移、旋转、缩放、光照、纹理、材质、像素、位图、文字、交互以及提高显示性能等方面的功能,基本涵盖了开发二、三维图形程序所需的各个方面。与一般的图形开发工具相比,OpenGL具有以下几个突
3、出特点:,5,(1)跨平台特性 OpenGL与硬件、窗口和操作系统是相互独立的。为了构成一个完整功能的图形处理系统,其设计实现共分 5 层:图形硬件、操作系统、窗口系统、 OpenGL和应用软件。 因而, OpenGL可以集成到各种标准窗口和操作系统中。例如,操作系统包括UNIX,Windows NT, Windows 95/98, DOS等;窗口系统包括X Windows, Microsoft Windows等。 OpenGL图形函数定义独立于任何程序设计语言,在各种编程语言中,如C,C+,FORTRAIN,Ada和Java等,都可以调用OpenGL的库函数。,6,(2)应用的广泛性 Ope
4、nGL是目前最主要的二、三维交互式图形应用程序开发环境,已成为业界最受推荐的图形应用编程接口。自从1992年发表以来,OpenGL已被广泛地应用于CAD/CAM、三维动画、数字图像处理以及虚拟现实等领域,Kinetix公司的3D Studio Max就是突出的代表。无论是在PC机上,还是在工作站甚至是大型机和超级计算机上,OpenGL都能表现出它的高性能和强大威力。,7,(3)网络透明性 建立在客户/服务器模型上的网络透明性是OpenGL的固有特性,它允许一个运行在工作站上的进程在本机或通过网络在远程工作站上显示图形。利用这种性质能够均衡各工作站的工作负荷,共同承担图形应用任务。 (4)高质量
5、和高性能 无论是在CAD/CAM、三维动画还是可视化仿真等领域,OpenGL高质量和高效率的图形生成能力都能得到充分的体现。在这些领域中,开发人员可以利用OpenGL制作出效果逼真的二、三维图像来。,8,(5)出色的编程特性 OpenGL在各种平台上已有多年的应用实践,加上严格的规范控制,因此OpenGL具有良好的稳定性。 OpenGL具有充分的独立性与易使用性等。,9,14.07.2020,9,2、OpenGL的工作方式 (1)体系结构 一个完整的窗口系统的OpenGL图形处理系统的结构如右图所示:最底层为图形硬件,第二层为操作系统,第三层为窗口系统,第四层为OpenGL,最上面的层为应用软
6、件。,10,14.07.2020,10,(2)OpenGL的流水线 当应用程序进行OpenGL API函数调用时,OpenGL命令将被放在一个命令缓冲区中,这样,命令缓冲区中包含了大量的命令、顶点数据和纹理数据。当这个缓冲区被清空时,缓冲区中的命令和数据都将传递给流水线的下一个阶段。,11,14.07.2020,11,(3)OpenGL状态机制 OpenGL的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。 状态变量可以通过glEnable()、glDisable()这两个函数来设置成有效或无效状态 。,12,14.07.2020,12,3、Op
7、enGL的组成 OpenGL是一种API,包括了多个图形函数,主要由以下函数库组成。(1)OpenGL核心库:gl(2)OpenGL实用程序库: glu(3)OpenGL编程辅助库:aux(4)OpenGL实用程序工具包(OpenGL utility toolkit,GLUT):glut(5)Windows专用库:wgl(6)Win32 API函数库,13,14.07.2020,13,4、OpenGL中的数据类型,14,14.07.2020,14,5、函数命名约定 OpenGL函数都遵循一个命名约定,即采用以下格式: 例如函数glColor3f(),gl表示这个函数来自库gl.h,color表
8、示该函数用于颜色设定,3f表示这个函数采用了三个浮点数参数。,15,14.07.2020,15,6、OpenGL中的颜色 在OpenGL中,一种颜色用红、绿、蓝成分的混合来表示,每种成分的值范围是0.0到1.0 。,16,OpenGL初步编程,在Windows下开发和创建一个OpenGL程序的基本步骤: 第一步,选择一个编译环境,如VC+ 第二步,安装GLUT工具包 第三步,建立一个OpenGL工程 第四步,编译连接 第五步,执行程序,17,OpenGL初步编程,OpenGL库和头文件: 动态库:opengl32.dll glu32.dll glut32.dll 放入windowssystem
9、32目录下 静态库:opengl32.lib glu32.lib glut32.lib放入VC安装目录的LIB目录下 头文件:gl.h glu.h glut.h放入VC安装目录的IncludeGL文件夹下,18,程序清单1.1:在窗口内绘制一个矩形 /GLRect.c #include #include #include #include / 函数RenderScene用于在窗口中绘制需要的图形 void RenderScene(void) /用当前清除色清除颜色缓冲区,即设定窗口的背景色 glClear(GL_COLOR_BUFFER_BIT); /设置当前绘图使用的RGB颜色 glColo
10、r3f(1.0f, 0.0f, 0.0f); /使用当前颜色绘制一个填充的矩形 glRectf(100.0f, 150.0f, 150.0f, 100.0f); /刷新OpenGL命令队列 glFlush(); ,19,/ 函数ChangeSize是窗口大小改变时调用的登记函数 void ChangeSize(GLsizei w, GLsizei h) if(h = 0) h = 1; /设置视区尺寸 glViewport(0, 0, w, h); / 重置坐标系统,使投影变换复位 glMatrixMode(GL_PROJECTION); glLoadIdentity(); / 建立修剪空间的
11、范围 if (w = h) glOrtho (0.0f, 250.0f, 0.0f, 250.0f*h/w, 1.0f, -1.0f); else glOrtho (0.0f, 250.0f*w/h, 0.0f, 250.0f, 1.0f, -1.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /函数SetupRC用于初始化,常用来设置场景渲染状态 void SetupRC(void) / 设置窗口的清除色为白色 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ,20,void main(void) /初始化GL
12、UT库OpenGL窗口的显示模式 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); / 创建一个名为GLRect的窗口 glutCreateWindow(GLRect); / 设置当前窗口的显示回调函数和窗口再整形回调函数 glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); SetupRC(); /启动主GLUT事件处理循环 glutMainLoop(); ,21,GLRect程序运行结果,22,2.1 一个简单的示例程序,#include void display() glClear(G
13、L_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glVertex2f(-0.5,-0.5); glVertex2f(-0.5,0.5); glVertex2f(0.5,0.5); glVertex2f(0.5,-0.5); glEnd(); glFlush(); int main(int argc, char* argv) glutInit( ,包含两个函数:main()和display()。其中main()负责opengl的初始化, display()负责所要绘制图形的定义。,这些语句以gl开头,这些语句以glut开头,23,2.2 GLUT,Opengl
14、包含了许多渲染函数,但是它并没有包含打开窗口或者从键盘或鼠标读取事件的函数。所以GLUT的使用能够使Opengl得以完整实现。 另外, Opengl绘图函数仅限于生成简单的几何图元(点、直线、多边形),GLUT还包含了一些函数,用于创建更复杂的三维物体。 GLUT是学习Opengl的一个非常好的起点。,24,2.2.1GLUT窗口管理,GLUT通过几个函数执行初始化窗口所需要的任务。 glutInit(int *argc,char *argv) glutInitDisplayMode(unsigned int mode) glutInitWindowPosition(int x,int y)
15、glutInitWindowSize(int width,int size) glutInitContextVersion(int majorVersion,int minorVersion) glutCreate Window(char *string),25,glutInit(int *argc,char *argv),对GLUT进行初始化,并处理所有的命令行参数。glutInit()应该在调用其他任何GLUT函数和OpenGL函数之前调用。 glutInit()接受来自main()函数的参数,程序可以具体实现相关的方式来使用这些参数。,26,glutInitDisplayMode(uns
16、igned int mode),指定了是使用RGBA模式还是颜色索引模式,另外还可以指定是使用单缓冲还是使用双缓冲窗口。,27,glutInitWindowPosition(int x,int y) 指定了窗口左上角的屏幕位置,28,glutInitWindowSize(int width,int size) 指定了窗口的大小(以像素为单位),29,glutInitContextVersion(int majorVersion,int minorVersion) 声明了要使用opengl哪个版本。,30,glutCreate Window(char *string),创建一个支持OpenGL渲
17、染环境的窗口。这个函数返回一个唯一的标示符,标识了这个窗口。这个函数将一个窗口以默认尺寸300*300像素显示在屏幕的默认位置上(屏幕左上角),参数可以为窗口创建一个标题。 注意:在调用glutMainLoop()函数之前,这个窗口并没有显示。,31,2.2.2 GLUT显示回调函数,glutDisplayFunc(void(*func)(void)-每当GLUT确定一个窗口的内容需要重新显示时,通过glutDisplayFunc()注册的那个回调函数就会被执行。因此应该把重绘场景所需要的所有代码都放在这个显示回调函数里。 如果程序修改了窗口的内容,有时候可能需要调用glutPostRedis
18、play(),这个函数会指示glutMainLoop()调用已注册的显示回调函数。,32,2.2.3 GLUT运行程序,最后,必须调用glutMainLoop()来启动程序,而进入事件循环。所有已创建的窗口将会在这时显示,对这些窗口的渲染也开始生效。 事件处理循环开始启动,已注册的显示回调函数被触发。一旦进入循环,它就永远不会退出!(除非借助回调函数或一些外部干预,如按下“终止”键),main函数中的任何位于该函数之后的代码将永远无法得到执行的机会。 该函数调用应作为main函数的最后一条语句出现。,33,2.2.4 GLUT处理输入事件,可以使用下面这些函数注册一些回调函数,当指定的事件发生
19、时,这些函数便会被调用。 glutReshapeFunc(void(*func)int w,int h) glutKeyboardFunc(void(*func)(unsigned char key,int x,int y) glutMotionFunc(void(*func)int x,int y),34,2.2.5 GLUT空闲处理,glutIdleFunc(void(*func)void)回调函数中指定一个函数,如果不存在其他尚未完成的事件,就执行这个函数。这个回调函数接受一个函数指针作为它的唯一参数。如果向它传递NULL(0),就相当于禁用这个函数。,35,2.3 绘图工具箱,2.3.
20、1 清除窗口 2.3.2 指定颜色 2.3.3 强制完成绘图操作 2.3.4 坐标系统工具箱,36,2.3.1 清除窗口,绘制几何物体的程序都需要处理这些问题。 计算机屏幕绘图和纸上绘图不同: 纸本白色,直接画 计算机中,保存图片的内存通常被计算机前一幅图像所填充,因此在绘制新场景之前,一般需要把它清除为某种背景颜色。,37,像素颜色存储方式有两种: 可以把像素颜色的红、绿、蓝和alpha值(RGBA)直接存储在位平面中。(常用) 也可以存储一个颜色索引值,用它来引用颜色查找表中的一个颜色项。,38,下面两行代码把一个RGBA模式的窗口清除为黑色 glClearColor(0.0, 0.0,
21、0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); 第一行代码把清除颜色设置为黑色第二行代码把整个窗口清除为当前清除颜色。 glClear()的唯一参数表示需要清除的缓冲区。 一般情况下,只要在程序早期设置一次清除颜色即可,以后可以根据需要随时清除缓冲区。,39,Void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) 颜色范围限定在0,1之间,默认清除颜色是(0,0,0,0)即黑色。 Void glClear(GLbitfield mask); mask指定缓冲区
22、:,40,OpenGL允许同时清除多个缓冲区 glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT )-执行速度快,41,2.3.2 指定颜色,物体颜色和形状无关,一般而言,程序员首先设置颜色和颜色方案,然后再绘制物体。 为了设置颜色,可以使用glColor3f()函数。这个函数接受3个参数,都是0.01.0之间的浮点数,分别表示颜色的红、绿、蓝色成分。0.0表示不使用这种成分,1.0表示最大限度的使用这种成分。,42,43,2.3.3 强制完成绘图操作,多条绘图命令由CPU发出,但须顺序等待执行,会造成系统性能低,所以OpenGL提供了glFlus
23、h()函数,强制客户机发送网络数据包。 glFlush()并不等待绘图完成,只是强制绘图命令开始执行,以保证所有命令在有限时间内执行。 Void glFlush(void) 强制以前发出的OpenGL命令开始执行,以保证它们能够在有限时间内完成。,44,2.3.4 坐标系统工具箱,使用OpenGL编程时,往往会用到各种坐标系。 应用程序所使用的坐标x轴正方向水平向右,y轴正方向竖直向上。 大多数窗口系统所使用的坐标系中的y轴正方向都是竖直向下,所以如果我们想使x和y坐标只出现正值,可将坐标系原点设在屏幕的左上角。,X轴,Y轴,原点,45,2.4 描述点、直线、和多边形,讨论如何描述OpenGL
24、几何图元。所有几何图元最终都是根据它们的顶点(vertex)来描述的。 2.4.1 什么是点、直线、多边形 2.4.2 指定顶点 2.4.3 OpenGL几何图元,46,2.4.1 什么是点、直线、多边形,点、直线、多边形数学概念比较简单。 点:用一组称为顶点的浮点数表示。所有的内部计算都是建立在把顶点看成是三维数据的基础上完成的。用户可以把顶点指定为二维形式(也就是说,只指定x和y坐标),并由OpenGL把它的z坐标设置为0.,47,直线: OpenGL中,直线这个术语表示一段线段,而不是数学意义上的两端无限延伸的直线。 OpenGL指定的直线都是根据它们的端点(顶点)指定的。 多边形:由线
25、段构成的单闭合环,其中线段是由它们的端点位置的顶点指定的。 多边形可能很复杂, OpenGL对它实行了很强的限制:1. OpenGL多边形各条边不能相交。 2. OpenGL多边形必须是凸多边形。,48,矩形:在应用程序中极为常见, OpenGL特别提供了填充矩形图元函数glRect*(). Void glRect(type x1,type y1,type x2,type y2)-用矩形的左上角和右下角的x坐标和y坐标来指定一个长和宽二维矩形。 曲线和弯曲表面:通过模拟实现,组合大量的短直线或小多边形。,49,2.4.2 指定顶点,在OpenGL中,所有的几何物体最终都描述成一组有序的顶点。g
26、lVertex*()函数用于指定顶点。 Void glVertex234sifd(TYPE coords); Void glVertex234sifdv(const TYPE* coords); 说明:指定了一个用于描述几何物体的顶点。可以选择这个函数的适当版本,既可以为一个顶点提供多达4个的坐标(x,y,z,w),也可以只提供2个坐标(x,y). 注意: glVertex*()函数只有位于glBegin()和glEnd()之间才有效。,50,举例,glVertex2s(2,3); glVertex3d(0.0,0.0,3.1415926535898); glVertex4f(2.3,1.0,
27、-2.2,2.0); GLdouble dvect3=5.0,9.0,1992.0; glVertex3dv(dvect);- dvect是指向数组的指针,51,2.4.3 OpenGL几何图元,知道如何指定顶点,还需要知道如何告诉Opengl根据这些顶点创建一组点、一条直线、一个多边形。为了实现这个目的,需要把一组顶点放在一对glBegin()和glEnd()之间。函数glBegin的参数指定绘制图元的类型。 见本章例题。,52,几何图元的名称和含义,53,54,55,使用glBegin()和glEnd()的限制,除这些函数外,之间不能使用其他OpenGL函数。,56,2.4 基本状态管理,
28、OpenGL维护了许多状态和状态变量。物体在进行渲染时可能会使用光照、纹理、隐藏表面消除、雾以及其他物体外观的状态。 默认情况下,这些状态的大部分一开始是处于不活动状态的。激活这些状态可能需要较大的开销。为了打开或关闭这些状态,可以使用下面这两个简单的函数: Void glEnable(GLenum capability); Void glDisable(GLenum capability); glEnable()启用一个功能,glDisable()关闭一个功能。,57,2.5 显示点、直线、多边形,最简单的计算机图形就是在屏幕上某个位置绘制一个点,并用特定的颜色绘制出来。请看下面的代码: g
29、lBegin(GL_POINTS); glVertex3f(0.0f,0.0f,0.0f); glVertex3f(10.0f,10.0f,10.0f); glEnd(); 计算机中的图元只是把一组顶点或顶点列表解释为屏幕上绘制的某些形状,而顶点是用函数glVertex3f来定义,该函数中的参数指明定义点的x、y和z坐标。OpenGL中定义的定点到放在函数glBegin和glEnd之间,由函数glBegin的参数指定绘制图元的类型,GL_POINTS表示这个序列中绘制的是单个的点。,58,点的绘制,glVertex函数用于指定顶点,它可以有2,3,4个参数。带2个参数时指定的是空间点的x,y坐
30、标,其z坐标为默认值0,在绘制平面图形时常常使用这类函数;带3个参数时指定的是空间点的x,y和z坐标;带4个参数时,除了定义空间点的x,y,z坐标,还有一个不为0的w坐标,这样,点的坐标(x,y,z,w)实际上构成了一个齐次坐标。在OpenGL中,我们仍然使用规范化齐次坐标以保证点的齐次坐标与三维坐标的一一对应关系,最后指定的空间点的坐标为(x/w,y/w,z/w,1),w成了坐标值的一个缩放因子。,59,60,直线属性,在OpenGL中绘制一个点时,点大小的默认值是一个象素。可以用函数glPointSize修改这个值: void glPointSize(GLfloat size); 这个函数
31、采用一个参数来指定画点时以象素为单位的近似直径。但是不是任意大小点都支持,通常使用下面的代码来获取点大小的范围和它们之间最小的中间值: GLfloat sizes2; /保存绘制点的尺寸范围 GLfloat step; /保存绘制点尺寸的步长 glGetFloatv(GL_POINT_SIZE_RANGE,sizes); glGetFloatv(GL_POINT_SIZE_GRANULARITY, 其中参数mode的取值为GL_FLAT或GL_SMOOTH,分别表示平面明暗处理和光滑明暗处理。应用平面明暗处理模式时,多边形内每个点的法向一致,且颜色也一致,OpenGL用指定多边形最后一个顶点时
32、的当前颜色作为填充多边形的纯色;应用光滑明暗处理模式时,多边形所有点的法向是由内插生成的,具有一定的连续性,因此每个点的颜色也相应内插,故呈现不同色。这种模式下,插值方法采用的是双线性插值法,68,多边形的模式,多边形不是必须用当前颜色填充的。默认情况下绘制的多边形是实心的,但可以通过指定把多边形绘制为轮廓或只是点(只画出顶点)来修改这项默认行为。函数glPolygonMode允许把多边形渲染为填充的实心、轮廓线或只是点。另外,可以把这项渲染模式应用到多边形的两面或只应用到正面或背面。使用下面的函数可以改变多边形模式: glPolygonMode(Glenum face,Glenum mode
33、); 其中,参数face指定多边形的哪一面受模式改变的影响GL_FRONT,GL_BACK或GL_FRONT_AND_BACK。参数mode用于指定新的绘图模式。GL_FILL是默认值,生成填充的多边形;GL_LINE生成多边形的轮廓;而GL_POINT只画出顶点。GL_LINE和GL_POINT绘制的点和线受glEdgeFlag所设置边缘标记的影响。,69,我们也可以不对表面中的某一个或全部进行绘制,方法是借助glCullFace()将正面朝向或背面朝向多边形或全部多边形进行剪裁。 Void glCullFace(GLenum mode) 在绘图时忽略由mode(GL_FRONT,GL_BA
34、CK,GL_FRONT_AND_BACK)所指定的表面。 裁剪必须通过如下方式启用: glEnable(GL_CULL_FACE);,70,Void glFrontFace(GLenum mode) 允许将逆时针(GL_CCW)方向或顺时针(GL_CW)方向定义为正面朝向。,71,多边形的绘制规则,在使用大量多边形构造一个复杂表面时,有两条重要规则。 第一条规则是所有多边形都必须是平面的,也就是说,多边形的所有顶点必须位于一个平面上,不能在空间中扭曲。 第二条规则是多边形的边缘决不能相交,而且多边形必须是凸的。如果有任意两条边交叉,就称这个多边形与自己相交。“凸的”是指任意经过多边形的直线进入
35、和离开多边形的次数不超过一次。 对于非凸多边形,可以把它分割成几个凸多边形(通常是三角形),再将它绘制出来。这样有出现了一个问题,如果这样的多边形被填充时看不到任何边缘,但如果切换到轮廓图形,就会看到组成大表面的所有小三角形,这会分散你的注意力。OpenGL提供了一个特殊标记来处理这些边缘,称为边缘标记。在指定顶点的列表时,通过设置和清除边缘标记,可以通知OpenGL哪些线段被认为是边线(围绕图形边界的线),哪些线段不是(不应该显示的内部线段)。glEdgeFlag函数用一个参数把边缘标记设为True或false。当函数被设置为True时,后面的顶点将标记出边界线段的起点。,72,73,2.6
36、 颜色插值,绘制多边形的颜色由当前颜色状态决定。在两次glVertex*()调用之间改变颜色时,修改了当前状态,但是在概念上我们是将新颜色与新顶点建立了关联。 例: glBegin(GL_LINE); glColor3f(1.0,0.0,0.0); glVertex2f(1.0,0.0); glColor3f(0.0,0.0, 1.0); glVertex2f(0.0, 1.0); glEnd(); 但是,两点之间的颜色是什么呢?,红色点,蓝色点,74,默认情况下,将使用平滑着色依据两端顶点的颜色通过插值计算出中间点的颜色值。所以这条线的颜色是从红色逐渐过渡到蓝色,中间将经历明暗的紫红色。见o
37、penGLTest1程序 Opengl允许用最后一个顶点颜色来确定整个图元属性。平面着色 Void glShadeModel(GLenum mode) 将明暗模型设为平滑模式(GL_SMOOTH)或平面模式(GL_FLAT)。着色模式点默认为平滑模式。,75,将一个多边形分解为5个三角形时,如果对这5个三角形进行填充时,显示是没有问题的。但将多边形模式设为边模式时,我们实际上想显示的是原始多边形的各边,而非产生的虚线新边。所以我们借助函数glEdgeFlag*(),可决定哪些边将被显示。如果该标记设为了GL_TRUE,则每个顶点都被认为是所要显示的线段的起点;若该标记被设为GL_FALSE,则
38、构成虚边的顶点不被显示。 Void glEdgeFlag(GLboolean flag) Void glEdgeFlagv(GLboolean * flag),76,顶点数组,OpenGL需要进行大量的函数调用才能完成对几何图元的渲染。绘制一个20条边的多边形至少需要22个函数调用。首先调用1次glBegin(),然后为每个顶点调用1次函数,最后调用1次glEnd()。 由于还需要额外的信息(多边形边界标志或表面法线),所以在每个顶点上还要增加函数调用。这可能会成倍地增加渲染几何物体所需要的函数调用数量。在有些系统中,函数调用具有相当大的开销,可能会影响应用程序的性能。,77,OpenGL提供
39、了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,并用少量函数调用(与顶点数组的数量相仿)访问这些数据。使用顶点数组函数,一个拥有20条边的多边形的20个顶点可以放在1个数组中,并且只通过1个函数进行调用。如果每个顶点还有一条法线向量,所有20条法线向量可以放在另一个数组中,也可以只通过1个函数进行调用 。,78,把数据放在顶点数组中可以提高应用程序的性能。使用顶点数组可以减少函数调用的次数,从而提高性能。另外,使用顶点数组还可以避免共享顶点的冗余处理。,79,使用顶点数组对几何图形进行渲染需要3个步骤: 1) 激活(启用)最多可达8个数组,每个数组用于存储不同类型的数据:顶
40、点坐标、表面法线、RGBA颜色、辅助颜色、颜色索引、雾坐标、纹理坐标以及多边形的边界标志。,80,步骤1:启用数组,第一个步骤是调用glEnableClientState()函数(使用一个枚举值参数),激活选择的数组。从理论上说,最多可能调用这个函数8次,激活8个可用的数组。但是在实践中,可以激活的数组最多只有6个, 这是因为有些数组不能同时激活。例如, 不可能同时激活GL_COLOR_ARRAY和GL_INDEX_ARRAY。应用程序的显示模式可以支持RGBA模式,也可以支持颜色索引模式,但是不能同时支持这两种模式。,81,voidglEnableClientState(GLenumarra
41、y) 指定了需要启用的数组。a r r a y 参数可以使用下面这些符号常量: GL_VERTEX_ARRAY、GL_COLOR_ARRAY、GL_SECONDARY_COLOR_ARRAY、GL_INDEX_ARRAY、GL_NORMAL_ARRAY、GL_FOG_COORDINATE_ARRAY、GL_TEXTURE_COORD_ ARRAY和GL_EDGE_FLAG_ARRAY。 如果需要使用光照,可能需要为每个顶点定义一条法线向量。在这种情况下使用顶点数组时,需要同时激活表面法线数组和顶点坐标数组: glEnableClientState(GL_NORMAL_ARRAY); glEna
42、bleClientState(GL_VERTEX_ARRAY);,82,步骤2:指定数组的数据,可以通过一种简单的方法,用一条命令指定客户空间中的一个数组。共有8个不同的函数可以用来指定数组,每个函数用于指定一个不同类型的数组。另外,还有一个函数可以一次指定客户空间中的几个数组,它们均来源于一个混合数组。,83,voidglVertexPointer(GLintsize,GLenumtype,GLsizeistride, constGLvoid*pointer); 指定了需要访问的空间坐标数据。pointer是数组包含的第一个顶点的第一个坐标的内存地址。type指定了数组中每个坐标的数据类型(
43、GL_SHORT、GL_INT、GL_FLOAT或GL_DOUBLE)。size是每个顶点的坐标数量,它必须是2、3或4。stride是连续顶点之间的字节偏移量。如果stride是0,数组中的顶点便是紧密相邻的。,84,为了访问其他几个数组,可以使用下面这些类似的函数: voidglColorPointer(GLintsize,GLenumtype,GLsizeistride, constGLvoid*pointer); voidglSecondaryColorPointer(GLintsize,GLenumtype,GLsizeistride constGLvoid*pointer); vo
44、idglIndexPointer(GLenumtype,GLsizeistride,constGLvoid*pointer); voidglNormalPointer(GLenumtype,GLsizeistride, constGLvoid*pointer); voidglFogCoordPointer(GLenumtype,GLsizeistride, constGLvoid*pointer); voidglTexCoordPointer(GLintsize,GLenumtype,GLsizeistride, constGLvoid*pointer); voidglEdgeFlagPoin
45、ter(GLsizeistride,constGLvoid*pointer);,85,示例程序;启用和加载顶点数组,staticGLintvertices=25,25, 100,325, 175,25, 175,325, 250,25, 325,325; staticGLfloatcolors=1.0,0.2,0.2, 0.2,0.2,1.0, 0.8,1.0,0.2, 0.75,0.75,0.75, 0.35,0.35,0.35, 0.5,0.5,0.5; glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_A
46、RRAY); glColorPointer(3,GL_FLOAT,0,colors); glVertexPointer(2,GL_INT,0,vertices);,86,跨距(Stride) gl*Pointer()函数的stride参数告诉OpenGL如何访问指针数组中的数据。它的值应该是两个连续的指针元素之间的字节数量(或者是0,这是一种特殊情况)。例如,假设顶点的RGB值和(x, y, z)坐标存储在同一个数组中,如下所示:,87,staticGLfloatintertwined= 1.0,0.2,1.0,100.0,100.0,0.0, 1.0,0.2,0.2,0.0,200.0,0.
47、0, 1.0,1.0,0.2,100.0,300.0,0.0, 0.2,1.0,0.2,200.0,300.0,0.0, 0.2,1.0,1.0,300.0,200.0,0.0, 0.2,0.2,1.0,200.0,100.0,0.0; 如果只想引用这个intertwined数组中的颜色值,下面这个调用从数组的起始地址(可以用 对于顶点坐标指针,需要从这个数组的其他位置开始引用。我们从intertwined的第4个元素开始(记住,C的数组是从0开始计数的):,88,glVertexPointer(3,GL_FLOAT,6*sizeof(GLfloat), 如果stride参数的值为0,每种类型
48、的顶点数组(RGB颜色、颜色索引、顶点坐标等)必须紧密相邻。数组中的数据必须是一致的。也就是说,数据必须全部是RGB颜色值,或者全部是顶点坐标,也可以是其他类似的数据。,89,步骤3:解引用和渲染,在顶点数组的内容被解引用(即提取指针所指向的数据)之前,数组一直保存在客户端,它们的内容很容易进行修改。在步骤3中,数组中的数据被提取,接着发送到服务器,然后发送到图形处理管线进行渲染。 可以从单个数组元素(索引位置)提取数据,也可以从一个有序的数组元素列表(可能被限制为整个顶点数组数据的一个子集)中提取数据,或者从一个数组元素序列中提取数据。,90,解引用单个数组元素 voidglArrayEle
49、ment(GLintith) 获取当前所有已启用数组的一个顶点(第ith个)的数据。对于顶点坐标数组,对应的函数是glVertexsizetypev(),其中size是2, 3, 4之一。type是s, i, f, d之一,分别表示GLshort、GLint、GLfloat和GLdouble。 glArrayElement()通常是在glBegin()和glEnd()之间调用。否则,glArrayElement()函数就会设置所有启用的数组的当前状态(顶点除外,因为它不存在当前状态)。,91,使用取自启用的顶点数组的第3、第4和第6个顶点绘制了一个三角形(同样,我们需要记住C的数组是从0开始计
50、数的)。 glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glColorPointer(3,GL_FLOAT,0,colors); glVertexPointer(2,GL_INT,0,vertices); glBegin(GL_TRIANGLES); glArrayElement(2); glArrayElement(3); glArrayElement(5); glEnd();,92,解引用数组元素的一个列表 glArrayElement()对于随机存取数据的数组是非常有效的。与它类似的函
51、数,例如glDrawElements()、glMultiDrawElements()和glDrawRangeElements()则采用一种更为有序的方式对数据数组进行随机存取。,93,voidglDrawElements(GLenummode,GLsizeicount,GLenumtype,constGLvoid*indices); 使用count个元素定义一个几何图元序列,这些元素的索引值保存在indices数组中。type必须是GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT或GL_UNSIGNED_INT,表示indices数组的数据类型。mode参数指定了被创建的是
52、哪种类型的图元,它的值和glBegin()函数所接受的参数值相同,例如GL_POLYGON、GL_LINE_LOOP、GL_LINES和GL_POINTS等。 glDrawElements()的效果差不多相当于下面这段代码: glBegin(mode); for(i=0;icount;i+) glArrayElement(indicesi); glEnd(); glDrawElements()还会执行检查,确保mode、count和type参数的值都是合法的。,94,使用glDrawElements(),立方体每个侧面的顶点可以放在一个索引数组中。程序显示了用glDrawElements()对
53、这个立方体进行渲染的两种方法; staticGLubytefrontIndices=4,5,6,7; staticGLubyterightIndices=1,2,6,5; staticGLubytebottomIndices=0,1,5,4; staticGLubytebackIndices=0,3,2,1; staticGLubyteleftIndices=0,4,7,3; staticGLubytetopIndices=2,3,7,6; glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,frontIndices); glDrawElements(GL_Q
54、UADS,4,GL_UNSIGNED_BYTE,rightIndices); glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,bottomIndices); glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,backIndices); glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,leftIndices); glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,topIndices);,95,把glDrawElements()放在glBegin()和
55、glEnd()之间是错误的做法。 对于其中几种图元类型(例如GL_QUADS、GL_TRIANGLES和GL_LINES),可以把几个索引列表压缩在一起,放在同一个数组中。由于GL_QUADS图元把每4个顶点解释为一个多边形,因此可以把上个程序使用的所有索引值压缩到一个数组中。 staticGLubyteallIndices=4,5,6,7,1,2,6,5, 0,1,5,4,0,3,2,1, 0,4,7,3,2,3,7,6; glDrawElements(GL_QUADS,24,GL_UNSIGNED_BYTE,allIndices);,96,glMultiDrawElements()函数的作
56、用是把几个glDraw-Elements()调用合并到1个函数调用中。 voidglMultiDrawElements(GLenummode,GLsizei*count, GLenumtype,constGLvoid*indices, GLsizeiprimcount); 调用一系列的glDrawElements()函数(数量为primcount个)。indices是一个指针数组,包含了数组元素的列表。count是一个数组,包含了每个相应的数组元素列表中能够找到的顶点数量。mode(图元类型)和type(数据类型)与它们在glDrawElements()函数中的含义相同。,97,与glDraw
57、Elements()和glMultiDrawElements()相似,glDrawRangeElements()函数也适用于随机存取的数据数组,用于对它们的内容进行渲染。glDrawRangeElements()还对它所接受的合法索引值引入了范围限制,这可以提高程序的性能。为了实现优化的性能,有些OpenGL实现能够预先提取(在渲染之前获取)有限数量的顶点数组数据。glDrawRangeElements()允许指定预先提取的顶点的范围。,98,voidglDrawRangeElements(GLenummode,GLuintstart,GLuintend,GLsizeicount, GLenu
58、mtype,constGLvoid*indices); 创建了一个几何图元序列,类似于glDrawElements()创建的序列,但是具有更强的限制。glDrawRangeElements()引入了两个新参数:start和end,它们指定了indices可以接受的值的范围。indices数组中的值必须位于start和end之间才是合法的(包含start和end)。,99,解引用一个数组元素序列,glArrayElements()、glDrawElements()和glDrawRangeElements()能够对数据数组进行随机存取,但是glDrawArrays()只能按顺序访问它们。 void
59、glDrawArrays(GLenummode,GLintfirst,GLsizeicount); 创建一个几何图元序列,使用每个被启用的数组中从first开始,到first + count-1结束的数组元素。mode指定了创建的图元类型,它的值和glBegin()函数所接受的参数值相同。例如:GL_POLYGON、GL_LINE_LOOP、GL_LINES和GL_POINTS等。 调用glDrawArrays()函数的效果差不多相当于下面这段代码: glBegin(mode); for(i=0;icount;i+) glArrayElement(first+i); glEnd(); 和glDrawElements()相似,glDrawArrays()也会对它的参数值执行错误检查,如果对应的数组被
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 血源性病原体职业暴露应急演练考核试卷及答案
- 实验幼儿园亲子趣味运动会活动方案
- 2026年专业技术人员共需科目考试试题含答案(保密意识和保密常识教育)
- 养老院室内消火栓使用应急演练脚本
- CBD项目金属屋面幕墙施工方案
- 敬老院群体性事件应急预案
- 某地区重点工程管道焊接施工方案
- 2026危险化学品安全作业经典例题附答案详解
- 管道堵塞渗漏应急处置细则
- 中职美术面试题及答案
- 内江市2026年社会工作服务岗位人员招募考试备考题库及答案详解
- 2026年医院卫生监督所监督员高频面试题包含详细解答
- 四年级下数学(简便运算)易错专项训练
- 2026年国家卫健委遴选笔试模拟题
- 2026年全国青少年航天创新大赛(航天知识问答)经典试题及答案
- 聊城职业技术学院辅导员招聘考试真题及答案
- 2026年征兵政治考核工作规定题库
- NB∕T 33009-2021 电动汽车充换电设施建设技术导则
- 经筋理论的临床意义课件
- 汉密尔顿焦虑量表课件
- 2022年小学音乐教师考试题
评论
0/150
提交评论