1、OpenGL及二维编程,12/12/2018,华中科技大学计算机学院 李国宽,2,1、OpenGL简介OpenGL(Open Graphics Library,开放图形库),是一个二维/三维的计算机图形和模型库,它源于SGI(Silicon Graphics inc.)公司为其图形工作站开发的IRIS GL,在跨平台移植过程中发展成为OpenGL。 目前, OpenGL已成为开放的国际图形标准。,2.1 从一个简单的示例程序开始,12/12/2018,3,OpenGL的功能:模型绘制 绘制点、线和多边形,构造三维模型。模型观察 建立变换(坐标变换,投影变换,视窗变换)。颜色模式的指定:RGBA
2、模式和颜色索引模式光照应用图象效果增强:反走样、混合和雾化位图和图象处理纹理映射实时动画:双缓存技术(double buffer)交互技术 提供人机交互的接口。,OpenGL的特点 从程序开发人员的角度来看,OpenGL是一组绘图命令的API集合。利用这些API能够方便地描述二维和三维几何物体,并控制这些物体按某种方式绘制到显示缓冲区中。OpenGL的API集提供了物体描述、平移、旋转、缩放、光照、纹理、材质、像素、位图、文字、交互以及提高显示性能等方面的功能,基本涵盖了开发二、三维图形程序所需的各个方面。与一般的图形开发工具相比,OpenGL具有以下几个突出特点:,(1)跨平台特性OpenG
3、L与硬件、窗口和操作系统是相互独立的。为了构成一个完整功能的图形处理系统,其设计实现共分 5 层:图形硬件、操作系统、窗口系统、 OpenGL和应用软件。因而, OpenGL可以集成到各种标准窗口和操作系统中。例如,操作系统包括UNIX,Windows NT, Windows 95/98, DOS等;窗口系统包括X Windows, Microsoft Windows等。 OpenGL图形函数定义独立于任何程序设计语言,在各种编程语言中,如C,C+,FORTRAIN,Ada和Java等,都可以调用OpenGL的库函数。,(2)应用的广泛性 OpenGL是目前最主要的二、三维交互式图形应用程序开
4、发环境,已成为业界最受推荐的图形应用编程接口。自从1992年发表以来,OpenGL已被广泛地应用于CAD/CAM、三维动画、数字图像处理以及虚拟现实等领域,Kinetix公司的3D Studio Max就是突出的代表。无论是在PC机上,还是在工作站甚至是大型机和超级计算机上,OpenGL都能表现出它的高性能和强大威力。,(3)网络透明性建立在客户/服务器模型上的网络透明性是OpenGL的固有特性,它允许一个运行在工作站上的进程在本机或通过网络在远程工作站上显示图形。利用这种性质能够均衡各工作站的工作负荷,共同承担图形应用任务。 (4)高质量和高性能无论是在CAD/CAM、三维动画还是可视化仿真
5、等领域,OpenGL高质量和高效率的图形生成能力都能得到充分的体现。在这些领域中,开发人员可以利用OpenGL制作出效果逼真的二、三维图像来。,(5)出色的编程特性OpenGL在各种平台上已有多年的应用实践,加上严格的规范控制,因此OpenGL具有良好的稳定性。OpenGL具有充分的独立性与易使用性等。,12/12/2018,9,2、OpenGL的工作方式 (1)体系结构 一个完整的窗口系统的OpenGL图形处理系统的结构如右图所示:最底层为图形硬件,第二层为操作系统,第三层为窗口系统,第四层为OpenGL,最上面的层为应用软件。,12/12/2018,10,(2)OpenGL的流水线 当应用
6、程序进行OpenGL API函数调用时,OpenGL命令将被放在一个命令缓冲区中,这样,命令缓冲区中包含了大量的命令、顶点数据和纹理数据。当这个缓冲区被清空时,缓冲区中的命令和数据都将传递给流水线的下一个阶段。,12/12/2018,11,(3)OpenGL状态机制OpenGL的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。 状态变量可以通过glEnable()、glDisable()这两个函数来设置成有效或无效状态 。,12/12/2018,12,3、OpenGL的组成OpenGL是一种API,包括了多个图形函数,主要由以下函数库组成。 (1
7、)OpenGL核心库:gl (2)OpenGL实用程序库: glu (3)OpenGL编程辅助库:aux (4)OpenGL实用程序工具包(OpenGL utility toolkit,GLUT):glut (5)Windows专用库:wgl (6)Win32 API函数库,12/12/2018,13,4、OpenGL中的数据类型,12/12/2018,14,5、函数命名约定 OpenGL函数都遵循一个命名约定,即采用以下格式:例如函数glColor3f(),gl表示这个函数来自库gl.h,color表示该函数用于颜色设定,3f表示这个函数采用了三个浮点数参数。,12/12/2018,15,6
8、、OpenGL中的颜色 在OpenGL中,一种颜色用红、绿、蓝成分的混合来表示,每种成分的值范围是0.0到1.0 。,OpenGL初步编程,在Windows下开发和创建一个OpenGL程序的基本步骤:第一步,选择一个编译环境,如VC+第二步,安装GLUT工具包第三步,建立一个OpenGL工程第四步,编译连接 第五步,执行程序,OpenGL初步编程,OpenGL库和头文件: 动态库:opengl32.dll glu32.dll glut32.dll 放入windowssystem32目录下 静态库:opengl32.lib glu32.lib glut32.lib放入VC安装目录的LIB目录下
9、头文件:gl.h glu.h glut.h放入VC安装目录的IncludeGL文件夹下,程序清单1.1:在窗口内绘制一个矩形 /GLRect.c #include #include #include #include/ 函数RenderScene用于在窗口中绘制需要的图形 void RenderScene(void) /用当前清除色清除颜色缓冲区,即设定窗口的背景色glClear(GL_COLOR_BUFFER_BIT); /设置当前绘图使用的RGB颜色glColor3f(1.0f, 0.0f, 0.0f); /使用当前颜色绘制一个填充的矩形glRectf(100.0f, 150.0f, 15
10、0.0f, 100.0f); /刷新OpenGL命令队列glFlush();,/ 函数ChangeSize是窗口大小改变时调用的登记函数 void ChangeSize(GLsizei w, GLsizei h)if(h = 0) h = 1;/设置视区尺寸glViewport(0, 0, w, h); / 重置坐标系统,使投影变换复位glMatrixMode(GL_PROJECTION);glLoadIdentity();/ 建立修剪空间的范围if (w = h) glOrtho (0.0f, 250.0f, 0.0f, 250.0f*h/w, 1.0f, -1.0f);else glOrt
11、ho (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); ,void main(void)/初始化GLUT库OpenGL窗口的显示模式glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); / 创建一个名为GLRect的窗口glutCrea
12、teWindow(“GLRect“); / 设置当前窗口的显示回调函数和窗口再整形回调函数glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); SetupRC();/启动主GLUT事件处理循环glutMainLoop(); ,GLRect程序运行结果,2.1 一个简单的示例程序,#include void display() glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON);glVertex2f(-0.5,-0.5);glVertex2f(-0.5,0.5);glVertex2f(
13、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开头,2.2 GLUT,Opengl包含了许多渲染函数,但是它并没有包含打开窗口或者从键盘或鼠标读取事件的函数。所以GLUT的使用能够使Opengl得以完整实现。 另外, Opengl绘图函数仅限于生成简单的几何图元(点、直线、多边形),G
14、LUT还包含了一些函数,用于创建更复杂的三维物体。 GLUT是学习Opengl的一个非常好的起点。,2.2.1GLUT窗口管理,GLUT通过几个函数执行初始化窗口所需要的任务。 glutInit(int *argc,char *argv) glutInitDisplayMode(unsigned int mode) glutInitWindowPosition(int x,int y) glutInitWindowSize(int width,int size) glutInitContextVersion(int majorVersion,int minorVersion) glutCrea
15、te Window(char *string),glutInit(int *argc,char *argv),对GLUT进行初始化,并处理所有的命令行参数。glutInit()应该在调用其他任何GLUT函数和OpenGL函数之前调用。 glutInit()接受来自main()函数的参数,程序可以具体实现相关的方式来使用这些参数。,glutInitDisplayMode(unsigned int mode),指定了是使用RGBA模式还是颜色索引模式,另外还可以指定是使用单缓冲还是使用双缓冲窗口。,glutInitWindowPosition(int x,int y)指定了窗口左上角的屏幕位置,g
16、lutInitWindowSize(int width,int size)指定了窗口的大小(以像素为单位),glutInitContextVersion(int majorVersion,int minorVersion)声明了要使用opengl哪个版本。,glutCreate Window(char *string),创建一个支持OpenGL渲染环境的窗口。这个函数返回一个唯一的标示符,标识了这个窗口。这个函数将一个窗口以默认尺寸300*300像素显示在屏幕的默认位置上(屏幕左上角),参数可以为窗口创建一个标题。 注意:在调用glutMainLoop()函数之前,这个窗口并没有显示。,2.2
17、.2 GLUT显示回调函数,glutDisplayFunc(void(*func)(void)-每当GLUT确定一个窗口的内容需要重新显示时,通过glutDisplayFunc()注册的那个回调函数就会被执行。因此应该把重绘场景所需要的所有代码都放在这个显示回调函数里。 如果程序修改了窗口的内容,有时候可能需要调用glutPostRedisplay(),这个函数会指示glutMainLoop()调用已注册的显示回调函数。,2.2.3 GLUT运行程序,最后,必须调用glutMainLoop()来启动程序,而进入事件循环。所有已创建的窗口将会在这时显示,对这些窗口的渲染也开始生效。 事件处理循环
18、开始启动,已注册的显示回调函数被触发。一旦进入循环,它就永远不会退出!(除非借助回调函数或一些外部干预,如按下“终止”键),main函数中的任何位于该函数之后的代码将永远无法得到执行的机会。 该函数调用应作为main函数的最后一条语句出现。,2.2.4 GLUT处理输入事件,可以使用下面这些函数注册一些回调函数,当指定的事件发生时,这些函数便会被调用。 glutReshapeFunc(void(*func)int w,int h) glutKeyboardFunc(void(*func)(unsigned char key,int x,int y) glutMotionFunc(void(*f
19、unc)int x,int y),2.2.5 GLUT空闲处理,glutIdleFunc(void(*func)void)回调函数中指定一个函数,如果不存在其他尚未完成的事件,就执行这个函数。这个回调函数接受一个函数指针作为它的唯一参数。如果向它传递NULL(0),就相当于禁用这个函数。,2.3 绘图工具箱,2.3.1 清除窗口 2.3.2 指定颜色 2.3.3 强制完成绘图操作 2.3.4 坐标系统工具箱,2.3.1 清除窗口,绘制几何物体的程序都需要处理这些问题。 计算机屏幕绘图和纸上绘图不同:纸本白色,直接画计算机中,保存图片的内存通常被计算机前一幅图像所填充,因此在绘制新场景之前,一般
20、需要把它清除为某种背景颜色。,像素颜色存储方式有两种: 可以把像素颜色的红、绿、蓝和alpha值(RGBA)直接存储在位平面中。(常用) 也可以存储一个颜色索引值,用它来引用颜色查找表中的一个颜色项。,下面两行代码把一个RGBA模式的窗口清除为黑色 glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT);第一行代码把清除颜色设置为黑色第二行代码把整个窗口清除为当前清除颜色。glClear()的唯一参数表示需要清除的缓冲区。一般情况下,只要在程序早期设置一次清除颜色即可,以后可以根据需要随时清除缓冲区。,Void glClea
21、rColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)颜色范围限定在0,1之间,默认清除颜色是(0,0,0,0)即黑色。 Void glClear(GLbitfield mask); mask指定缓冲区:,OpenGL允许同时清除多个缓冲区 glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT )-执行速度快,2.3.2 指定颜色,物体颜色和形状无关,一般而言,程序员首先设置颜色和颜色方案,然后再绘制物体。 为了设置颜色,可以使用glColor3f()函数。这个函数接受3
22、个参数,都是0.01.0之间的浮点数,分别表示颜色的红、绿、蓝色成分。0.0表示不使用这种成分,1.0表示最大限度的使用这种成分。,2.3.3 强制完成绘图操作,多条绘图命令由CPU发出,但须顺序等待执行,会造成系统性能低,所以OpenGL提供了glFlush()函数,强制客户机发送网络数据包。 glFlush()并不等待绘图完成,只是强制绘图命令开始执行,以保证所有命令在有限时间内执行。 Void glFlush(void)强制以前发出的OpenGL命令开始执行,以保证它们能够在有限时间内完成。,2.3.4 坐标系统工具箱,使用OpenGL编程时,往往会用到各种坐标系。 应用程序所使用的坐标
23、x轴正方向水平向右,y轴正方向竖直向上。 大多数窗口系统所使用的坐标系中的y轴正方向都是竖直向下,所以如果我们想使x和y坐标只出现正值,可将坐标系原点设在屏幕的左上角。,X轴,Y轴,原点,2.4 描述点、直线、和多边形,讨论如何描述OpenGL几何图元。所有几何图元最终都是根据它们的顶点(vertex)来描述的。 2.4.1 什么是点、直线、多边形 2.4.2 指定顶点 2.4.3 OpenGL几何图元,2.4.1 什么是点、直线、多边形,点、直线、多边形数学概念比较简单。 点:用一组称为顶点的浮点数表示。所有的内部计算都是建立在把顶点看成是三维数据的基础上完成的。用户可以把顶点指定为二维形式
24、(也就是说,只指定x和y坐标),并由OpenGL把它的z坐标设置为0.,直线: OpenGL中,直线这个术语表示一段线段,而不是数学意义上的两端无限延伸的直线。 OpenGL指定的直线都是根据它们的端点(顶点)指定的。 多边形:由线段构成的单闭合环,其中线段是由它们的端点位置的顶点指定的。多边形可能很复杂, OpenGL对它实行了很强的限制:1. OpenGL多边形各条边不能相交。2. OpenGL多边形必须是凸多边形。,矩形:在应用程序中极为常见, OpenGL特别提供了填充矩形图元函数glRect*(). Void glRect(type x1,type y1,type x2,type y
25、2)-用矩形的左上角和右下角的x坐标和y坐标来指定一个长和宽二维矩形。 曲线和弯曲表面:通过模拟实现,组合大量的短直线或小多边形。,2.4.2 指定顶点,在OpenGL中,所有的几何物体最终都描述成一组有序的顶点。glVertex*()函数用于指定顶点。 Void glVertex234sifd(TYPE coords); Void glVertex234sifdv(const TYPE* coords); 说明:指定了一个用于描述几何物体的顶点。可以选择这个函数的适当版本,既可以为一个顶点提供多达4个的坐标(x,y,z,w),也可以只提供2个坐标(x,y). 注意: glVertex*()函
26、数只有位于glBegin()和glEnd()之间才有效。,举例,glVertex2s(2,3); glVertex3d(0.0,0.0,3.1415926535898); glVertex4f(2.3,1.0,-2.2,2.0);GLdouble dvect3=5.0,9.0,1992.0;glVertex3dv(dvect);- dvect是指向数组的指针,2.4.3 OpenGL几何图元,知道如何指定顶点,还需要知道如何告诉Opengl根据这些顶点创建一组点、一条直线、一个多边形。为了实现这个目的,需要把一组顶点放在一对glBegin()和glEnd()之间。函数glBegin的参数指定绘
27、制图元的类型。 见本章例题。,几何图元的名称和含义,使用glBegin()和glEnd()的限制,除这些函数外,之间不能使用其他OpenGL函数。,2.4 基本状态管理,OpenGL维护了许多状态和状态变量。物体在进行渲染时可能会使用光照、纹理、隐藏表面消除、雾以及其他物体外观的状态。 默认情况下,这些状态的大部分一开始是处于不活动状态的。激活这些状态可能需要较大的开销。为了打开或关闭这些状态,可以使用下面这两个简单的函数: Void glEnable(GLenum capability); Void glDisable(GLenum capability); glEnable()启用一个功能
28、,glDisable()关闭一个功能。,2.5 显示点、直线、多边形,最简单的计算机图形就是在屏幕上某个位置绘制一个点,并用特定的颜色绘制出来。请看下面的代码:glBegin(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_P
29、OINTS表示这个序列中绘制的是单个的点。,点的绘制,glVertex函数用于指定顶点,它可以有2,3,4个参数。带2个参数时指定的是空间点的x,y坐标,其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成了坐标值的一个缩放因子。,直线属性,在OpenGL中绘制一个点时,点大小
30、的默认值是一个象素。可以用函数glPointSize修改这个值:void glPointSize(GLfloat size); 这个函数采用一个参数来指定画点时以象素为单位的近似直径。但是不是任意大小点都支持,通常使用下面的代码来获取点大小的范围和它们之间最小的中间值:GLfloat sizes2; /保存绘制点的尺寸范围GLfloat step; /保存绘制点尺寸的步长glGetFloatv(GL_POINT_SIZE_RANGE,sizes);glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step); 在数组size中包含两个元素,分别保存了glPointS
31、ize的最小有效值和最大有效值,而变量step将保存点大小之间允许的最小增量。指定范围之外的大小不会被解释为错误,而是使用最接近指定值的可支持的最大或最小尺寸。 在OpenGL程序中,我们常可以利用离散的点来拟合一些常见的曲线,如圆,螺旋线等等。,直线的绘制,使用模式GL_LINES可以在两点之间画线,如下面的代码在两点(0,0,0)和(10,10,10)之间画一条直线:glBegin(GL_LINES);glVertex3f(0.0f,0.0f,0.0f);glVertex3f(10.0f,10.0f,10.0f);glEnd(); 注意,在glBegin/glEnd序列中两个顶点指定了一个
32、图元(直线),如果序列中指定的点为奇数个,那么最后一个顶点将被忽略。,有时我们需要在一系列的顶点之间绘制连续直线,此时需要用到GL_LINE_STRIP或GL_LINE_LOOP模式。GL_LINE_STRIP模式可以根据指定的一系列顶点,从一个顶点到另一个顶点用连续的线段画线。glBegin(GL_LINE_STRIP);glVertex3f(0.0f,0.0f,0.0f);glVertex3f(10.0f,10.0f,0.0f);glVertex3f(20.0f,5.0f,0.0f);glEnd(); 上面这段代码实际上在xy平面内绘制了两条直线(0,0,0)到(10,0,0)和(0,10
33、,0)到(20,5,0)。特别的,当沿着某条曲线指定一系列靠的很近的点,使用GL_LINE_STRIP模式可以绘制一条曲线。,曲线属性,GL_LINE_LOOP模式与GL_LINE_STRIP模式类似,只是会在指定的最后一个顶点与第一个顶点之间画最后一条线。 直线的属性包括线宽和线型。在OpenGL中可用glLineWidth指定线宽:void glLineWidth(GLfloat width) 与点的大小类似,glLineWidth函数采用一个参数来指定要画的线以象素计的近似宽度,可以用下面的代码来获取线宽范围和它们之间的最小间隔:GLfloat sizes2; /保存线宽的尺寸范围GLf
34、loat step; /保存线宽尺寸的最小间隔glGetFloatv(GL_LINE_WIDTH_RANGE,sizes);glGetFloatv(GL_LINE_WIDTH_GRANULARITY,&step); 数组seize中保存了glLineWidth的最小有效值和最大有效值,而变量step将保存线宽之间允许的最小增量。OpenGL规范只要求支持一种线宽:1.0。Microsoft的OpenGL实现允许线宽从0.5到10.0,最小增量为0.125。,多边形面的绘制,1. 三角形的绘制 在OpenGL中,面是由多边形构成的。三角形可能是最简单的多边形,它有三条边。可以使用GL_TRIAN
35、GLES模式通过把三个顶点连接到一起而绘出三角形。下面的代码绘制了一个三角形: glBegin(GL_TRIANGLES);glVertex2f(0.0,0.0); glVertex2f(15.0,15.0); glVertex2f(30.0,0.0); glEnd();注意,这里三角形将被用当前选定的颜色填充,如果尚未指定绘图的颜色,结果将是不确定的。,使用GL_TRIANGLE_STRIP模式可以绘制几个相连的三角形,系统根据前三个顶点绘制第一个多边形,以后每指定一个顶点,就与构成上一个三角形的后两个顶点绘制新的一个三角形。使用GL_TRIANGLE_FAN模式可以绘制一组相连的三角形,这
36、些三角形绕着一个中心点成扇形排列。第一个顶点构成扇形的中心,用前三个顶点绘制出最初的三角形之后,随后的所有顶点都和扇形中心以及紧跟在它前面的顶点构成下一个三角形,此时是以顺时针方向穿过顶点。这两种模式的推进如图 :,绕法,在绘制三角形的过程中,三个顶点将三角形封闭的过程是有序的,即三角形的构成路径具有方向性,我们把指定顶点时顺序和方向的组合称为“绕法”。比如上面的例子中画出的三角形的绕法就是顺时针的,若把后两个顶点的位置互换,就得到了逆时针绕法。绕法是任何多边形图元的一个重要特性。一般默认情况下,OpenGL认为逆时针绕法的多边形是正对着的,这一特性对于希望给多边形的正面和背面赋予不同的物理特
37、性十分有用。如果要反转OpenGL的默认行为,可以调用函数:glFrontFace(GL_CW);CL_CW告诉OpenGL应该把顺时针缠绕的多边形为正对着的。为了改回把逆时针绕法视为正面,可以使用CL_CCW。,明暗处理,在绘制多边形时,我们常常要指定绘制的颜色,而在OpenGL中,颜色实际上是对各个顶点而不是对各个多边形指定的。多边形的轮廓或者内部用单一的颜色或许多不同的颜色来填充的处理方式称为明暗处理。在OpenGL中,用单一颜色处理的称为平面明暗处理(Flat Shading),用许多不同颜色处理的称为光滑明暗处理(Smooth Shading),也称为Gourand 明暗处理(Gou
38、rand Shading)。设置明暗处理模式的函数为:void glShadeModel(GLenum mode);其中参数mode的取值为GL_FLAT或GL_SMOOTH,分别表示平面明暗处理和光滑明暗处理。应用平面明暗处理模式时,多边形内每个点的法向一致,且颜色也一致,OpenGL用指定多边形最后一个顶点时的当前颜色作为填充多边形的纯色;应用光滑明暗处理模式时,多边形所有点的法向是由内插生成的,具有一定的连续性,因此每个点的颜色也相应内插,故呈现不同色。这种模式下,插值方法采用的是双线性插值法,多边形的模式,多边形不是必须用当前颜色填充的。默认情况下绘制的多边形是实心的,但可以通过指定把
39、多边形绘制为轮廓或只是点(只画出顶点)来修改这项默认行为。函数glPolygonMode允许把多边形渲染为填充的实心、轮廓线或只是点。另外,可以把这项渲染模式应用到多边形的两面或只应用到正面或背面。使用下面的函数可以改变多边形模式:glPolygonMode(Glenum face,Glenum mode); 其中,参数face指定多边形的哪一面受模式改变的影响GL_FRONT,GL_BACK或GL_FRONT_AND_BACK。参数mode用于指定新的绘图模式。GL_FILL是默认值,生成填充的多边形;GL_LINE生成多边形的轮廓;而GL_POINT只画出顶点。GL_LINE和GL_POI
40、NT绘制的点和线受glEdgeFlag所设置边缘标记的影响。,我们也可以不对表面中的某一个或全部进行绘制,方法是借助glCullFace()将正面朝向或背面朝向多边形或全部多边形进行剪裁。 Void glCullFace(GLenum mode) 在绘图时忽略由mode(GL_FRONT,GL_BACK,GL_FRONT_AND_BACK)所指定的表面。 裁剪必须通过如下方式启用: glEnable(GL_CULL_FACE);,Void glFrontFace(GLenum mode) 允许将逆时针(GL_CCW)方向或顺时针(GL_CW)方向定义为正面朝向。,多边形的绘制规则,在使用大量多
41、边形构造一个复杂表面时,有两条重要规则。 第一条规则是所有多边形都必须是平面的,也就是说,多边形的所有顶点必须位于一个平面上,不能在空间中扭曲。 第二条规则是多边形的边缘决不能相交,而且多边形必须是凸的。如果有任意两条边交叉,就称这个多边形与自己相交。“凸的”是指任意经过多边形的直线进入和离开多边形的次数不超过一次。对于非凸多边形,可以把它分割成几个凸多边形(通常是三角形),再将它绘制出来。这样有出现了一个问题,如果这样的多边形被填充时看不到任何边缘,但如果切换到轮廓图形,就会看到组成大表面的所有小三角形,这会分散你的注意力。OpenGL提供了一个特殊标记来处理这些边缘,称为边缘标记。在指定顶
42、点的列表时,通过设置和清除边缘标记,可以通知OpenGL哪些线段被认为是边线(围绕图形边界的线),哪些线段不是(不应该显示的内部线段)。glEdgeFlag函数用一个参数把边缘标记设为True或false。当函数被设置为True时,后面的顶点将标记出边界线段的起点。,2.6 颜色插值,绘制多边形的颜色由当前颜色状态决定。在两次glVertex*()调用之间改变颜色时,修改了当前状态,但是在概念上我们是将新颜色与新顶点建立了关联。 例: glBegin(GL_LINE);glColor3f(1.0,0.0,0.0);glVertex2f(1.0,0.0);glColor3f(0.0,0.0, 1
43、.0);glVertex2f(0.0, 1.0);glEnd(); 但是,两点之间的颜色是什么呢?,红色点,蓝色点,默认情况下,将使用平滑着色依据两端顶点的颜色通过插值计算出中间点的颜色值。所以这条线的颜色是从红色逐渐过渡到蓝色,中间将经历明暗的紫红色。见openGLTest1程序 Opengl允许用最后一个顶点颜色来确定整个图元属性。平面着色 Void glShadeModel(GLenum mode) 将明暗模型设为平滑模式(GL_SMOOTH)或平面模式(GL_FLAT)。着色模式点默认为平滑模式。,将一个多边形分解为5个三角形时,如果对这5个三角形进行填充时,显示是没有问题的。但将多边
44、形模式设为边模式时,我们实际上想显示的是原始多边形的各边,而非产生的虚线新边。所以我们借助函数glEdgeFlag*(),可决定哪些边将被显示。如果该标记设为了GL_TRUE,则每个顶点都被认为是所要显示的线段的起点;若该标记被设为GL_FALSE,则构成虚边的顶点不被显示。 Void glEdgeFlag(GLboolean flag) Void glEdgeFlagv(GLboolean * flag),顶点数组,OpenGL需要进行大量的函数调用才能完成对几何图元的渲染。绘制一个20条边的多边形至少需要22个函数调用。首先调用1次glBegin(),然后为每个顶点调用1次函数,最后调用1
45、次glEnd()。 由于还需要额外的信息(多边形边界标志或表面法线),所以在每个顶点上还要增加函数调用。这可能会成倍地增加渲染几何物体所需要的函数调用数量。在有些系统中,函数调用具有相当大的开销,可能会影响应用程序的性能。,OpenGL提供了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,并用少量函数调用(与顶点数组的数量相仿)访问这些数据。使用顶点数组函数,一个拥有20条边的多边形的20个顶点可以放在1个数组中,并且只通过1个函数进行调用。如果每个顶点还有一条法线向量,所有20条法线向量可以放在另一个数组中,也可以只通过1个函数进行调用 。,把数据放在顶点数组中可以提高应用
46、程序的性能。使用顶点数组可以减少函数调用的次数,从而提高性能。另外,使用顶点数组还可以避免共享顶点的冗余处理。,使用顶点数组对几何图形进行渲染需要3个步骤: 1) 激活(启用)最多可达8个数组,每个数组用于存储不同类型的数据:顶点坐标、表面法线、RGBA颜色、辅助颜色、颜色索引、雾坐标、纹理坐标以及多边形的边界标志。,步骤1:启用数组,第一个步骤是调用glEnableClientState()函数(使用一个枚举值参数),激活选择的数组。从理论上说,最多可能调用这个函数8次,激活8个可用的数组。但是在实践中,可以激活的数组最多只有6个, 这是因为有些数组不能同时激活。例如, 不可能同时激活GL_
47、COLOR_ARRAY和GL_INDEX_ARRAY。应用程序的显示模式可以支持RGBA模式,也可以支持颜色索引模式,但是不能同时支持这两种模式。,void glEnableClientState(GLenum array) 指定了需要启用的数组。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_AR
48、RAY。 如果需要使用光照,可能需要为每个顶点定义一条法线向量。在这种情况下使用顶点数组时,需要同时激活表面法线数组和顶点坐标数组:glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);,步骤2:指定数组的数据,可以通过一种简单的方法,用一条命令指定客户空间中的一个数组。共有8个不同的函数可以用来指定数组,每个函数用于指定一个不同类型的数组。另外,还有一个函数可以一次指定客户空间中的几个数组,它们均来源于一个混合数组。,void glVertexPointer(GLint size, GLenum
49、type, GLsizei stride, const GLvoid *pointer); 指定了需要访问的空间坐标数据。pointer是数组包含的第一个顶点的第一个坐标的内存地址。type指定了数组中每个坐标的数据类型(GL_SHORT、GL_INT、GL_FLOAT或GL_DOUBLE)。size是每个顶点的坐标数量,它必须是2、3或4。stride是连续顶点之间的字节偏移量。如果stride是0,数组中的顶点便是紧密相邻的。,为了访问其他几个数组,可以使用下面这些类似的函数: void glColorPointer(GLint size, GLenum type, GLsizei str
50、ide, const GLvoid *pointer); void glSecondaryColorPointer(GLint size, GLenum type, GLsizei stride const GLvoid *pointer); void glIndexPointer(GLenum type, GLsizei stride, const GLvoid *pointer); void glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer); void glFogCoordPointer(GLenum type, GLsizei stride, const GLvoid *pointer); void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); void glEdgeFlagPointer(GLsizei stride, const GLvoid *pointer);,