收藏 分享(赏)

OpenGL入门C++教程.doc

上传人:scg750829 文档编号:7229136 上传时间:2019-05-10 格式:DOC 页数:95 大小:481KB
下载 相关 举报
OpenGL入门C++教程.doc_第1页
第1页 / 共95页
OpenGL入门C++教程.doc_第2页
第2页 / 共95页
OpenGL入门C++教程.doc_第3页
第3页 / 共95页
OpenGL入门C++教程.doc_第4页
第4页 / 共95页
OpenGL入门C++教程.doc_第5页
第5页 / 共95页
点击查看更多>>
资源描述

1、 OpenGL 入门 c+教程1.第一课:说起编程作图,大概还有很多人想起 TC 的#include 吧?但是各位是否想过,那些画面绚丽的 PC 游戏是如何编写出来的?就靠 TC 那可怜的 640*480 分辨率、16色来做吗?显然是不行的。本帖的目的是让大家放弃 TC 的老旧图形接口,让大家接触一些新事物。OpenGL 作为当前主流的图形 API 之一,它在一些场合具有比 DirectX 更优越的特性。1、与 C 语言紧密结合。OpenGL 命令最初就是用 C 语言函数来进行描述的,对于学习过 C 语言的人来讲,OpenGL 是容易理解和学习的。如果你曾经接触过 TC 的 graphics.

2、h,你会发现,使用 OpenGL 作图甚至比 TC 更加简单。2、强大的可移植性。微软的 Direct3D 虽然也是十分优秀的图形 API,但它只用于 Windows 系统(现在还要加上一个 XBOX 游戏机) 。而 OpenGL 不仅用于 Windows,还可以用于 Unix/Linux 等其它系统,它甚至在大型计算机、各种专业计算机(如:医疗用显示设备)上都有应用。并且,OpenGL 的基本命令都做到了硬件无关,甚至是平台无关。3、高性能的图形渲染。OpenGL 是一个工业标准,它的技术紧跟时代,现今各个显卡厂家无一不对 OpenGL 提供强力支持,激烈的竞争中使得 OpenGL 性能一直

3、领先。总之,OpenGL 是一个很 NB 的图形软件接口。至于究竟有多 NB,去看看 DOOM3 和 QUAKE4 等专业游戏就知道了。OpenGL 官方网站(英文)http:/www.opengl.org下面我将对 Windows 下的 OpenGL 编程进行简单介绍。学习 OpenGL 前的准备工作第一步,选择一个编译环境现在 Windows 系统的主流编译环境有 Visual Studio,Broland C+ Builder,Dev-C+等,它们都是支持OpenGL 的。但这里我们选择 Visual Studio 2005 作为学习 OpenGL 的环境。第二步,安装 GLUT 工具包

4、GLUT 不是 OpenGL 所必须的,但它会给我们的学习带来一定的方便,推荐安装。Windows 环境下的 GLUT 下载地址:(大小约为 150k)http:/www.opengl.org/resources/libraries/glut/glutdlls37beta.zip无法从以上地址下载的话请使用下面的连接:http:/ 环境下安装 GLUT 的步骤:1、将下载的压缩包解开,将得到 5 个文件2、在“ 我的电脑 ”中搜索“gl.h” ,并找到其所在文件夹(如果是 VisualStudio2005,则应该是其安装目录下面的“VCPlatformSDKincludegl 文件夹”) 。把

5、解压得到的 glut.h 放到这个文件夹。3、把解压得到的 glut.lib 和 glut32.lib 放到静态函数库所在文件夹(如果是 VisualStudio2005,则应该是其安装目录下面的“VClib”文件夹) 。4、把解压得到的 glut.dll 和 glut32.dll 放到操作系统目录下面的 system32 文件夹内。 (典型的位置为:C:WindowsSystem32)第三步,建立一个 OpenGL 工程这里以 VisualStudio2005 为例。选择 File-New-Project,然后选择 Win32 Console Application,选择一个名字,然后按 O

6、K。在谈出的对话框左边点 Application Settings,找到 Empty project 并勾上,选择 Finish。然后向该工程添加一个代码文件,取名为“OpenGL.c”,注意用.c 来作为文件结尾。搞定了,就跟平时的工程没什么两样的。第一个 OpenGL 程序一个简单的 OpenGL 程序如下:(注意,如果需要编译并运行,需要正确安装 GLUT,安装方法如上所述)#include void myDisplay(void)glClear(GL_COLOR_BUFFER_BIT);glRectf(-0.5f, -0.5f, 0.5f, 0.5f);glFlush();int ma

7、in(int argc, char *argv)glutInit(glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);glutInitWindowPosition(100, 100);glutInitWindowSize(400, 400);glutCreateWindow(“第一个 OpenGL 程序“);glutDisplayFunc(glutMainLoop();return 0;该程序的作用是在一个黑色的窗口中央画一个白色的矩形。下面对各行语句进行说明。怎么样?代码还不算长吧?首先,需要包含头文件#include ,这是 GLUT 的头文件。本来 O

8、penGL 程序一般还要包含和,但 GLUT 的头文件中已经自动将这两个文件包含了,不必再次包含。然后看 main 函数。int main(int argc, char *argv),这个是带命令行参数的 main 函数,各位应该见过吧?没见过的同志们请多翻翻书,等弄明白了再往下看。注意 main 函数中的各语句,除了最后的 return 之外,其余全部以 glut 开头。这种以 glut 开头的函数都是GLUT 工具包所提供的函数,下面对用到的几个函数进行介绍。1、glutInit,对 GLUT 进行初始化,这个函数必须在其它的 GLUT 使用之前调用一次。其格式比较死板,一般照抄这句 gl

9、utInit((二)glVertex2f(1.0f, 3.0f);(三)glVertex3f(1.0f, 3.0f, 0.0f);(四)glVertex4f(1.0f, 3.0f, 0.0f, 1.0f);(五)GLfloat VertexArr3 = 1.0f, 3.0f, 0.0f;glVertex3fv(VertexArr3);以后我们将用 glVertex*来表示这一系列函数。注意:OpenGL 的很多函数都是采用这样的形式,一个相同的前缀再加上参数说明标记,这一点会随着学习的深入而有更多的体会。三、开始绘制假设现在我已经指定了若干顶点,那么 OpenGL 是如何知道我想拿这些顶点来干

10、什么呢?是一个一个的画出来,还是连成线?或者构成一个多边形?或者做其它什么事情?为了解决这一问题,OpenGL 要求:指定顶点的命令必须包含在 glBegin 函数之后,glEnd 函数之前(否则指定的顶点将被忽略) 。并由 glBegin 来指明如何使用这些点。例如我写:glBegin(GL_POINTS);glVertex2f(0.0f, 0.0f);glVertex2f(0.5f, 0.0f);glEnd();则这两个点将分别被画出来。如果将 GL_POINTS 替换成 GL_LINES,则两个点将被认为是直线的两个端点,OpenGL 将会画出一条直线。我们还可以指定更多的顶点,然后画出

11、更复杂的图形。另一方面,glBegin 支持的方式除了 GL_POINTS 和 GL_LINES,还有GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLES ,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN等,每种方式的大致效果见下图:声明:该图片来自 www.opengl.org,该图片是OpenGL 编程指南一书的附图,由于该书的旧版(第一版,1994 年)已经流传于网络,我希望没有触及到版权问题。我并不准备在 glBegin 的各种方式上大作文章。大家可以自己尝试改变 glBegin 的方式和顶点的位置,生成一些有趣的图案。程序代码:void

12、myDisplay(void)glClear(GL_COLOR_BUFFER_BIT);glBegin( /* 在这里填上你所希望的模式 */ );/* 在这里使用 glVertex*系列函数 */* 指定你所希望的顶点位置 */glEnd();glFlush();把这段代码改成你喜欢的样子,然后用它替换第一课中的 myDisplay 函数,编译后即可运行。两个例子例一、画一个圆/*正四边形,正五边形,正六边形,直到正 n 边形,当 n 越大时,这个图形就越接近圆当 n 大到一定程度后,人眼将无法把它跟真正的圆相区别这时我们已经成功的画出了一个“圆”(注:画圆的方法很多,这里使用的是比较简单,

13、但效率较低的一种)试修改下面的 const int n 的值,观察当 n=3,4,5,8,10,15,20,30,50 等不同数值时输出的变化情况将 GL_POLYGON 改为 GL_LINE_LOOP、GL_POINTS 等其它方式,观察输出的变化情况*/#include const int n = 20;const GLfloat R = 0.5f;const GLfloat Pi = 3.1415926536f;void myDisplay(void)int i;glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_POLYGON);for(i=0; icons

14、t GLfloat Pi = 3.1415926536f;void myDisplay(void)GLfloat a = 1 / (2-2*cos(72*Pi/180);GLfloat bx = a * cos(18 * Pi/180);GLfloat by = a * sin(18 * Pi/180);GLfloat cy = -a * cos(18 * Pi/180);GLfloatPointA2 = 0, a ,PointB2 = bx, by ,PointC2 = 0.5, cy ,PointD2 = -0.5, cy ,PointE2 = -bx, by ;glClear(GL_CO

15、LOR_BUFFER_BIT);/ 按照 A-C-E-B-D-A 的顺序,可以一笔将五角星画出glBegin(GL_LINE_LOOP);glVertex2fv(PointA);glVertex2fv(PointC);glVertex2fv(PointE);glVertex2fv(PointB);glVertex2fv(PointD);glEnd();glFlush();例三、画出正弦函数的图形/*由于 OpenGL 默认坐标值只能从-1 到 1, (可以修改,但方法留到以后讲)所以我们设置一个因子 factor,把所有的坐标值等比例缩小,这样就可以画出更多个正弦周期试修改 factor 的值

16、,观察变化情况*/#include const GLfloat factor = 0.1f;void myDisplay(void)GLfloat x;glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_LINES);glVertex2f(-1.0f, 0.0f);glVertex2f(1.0f, 0.0f); / 以上两个点可以画 x 轴glVertex2f(0.0f, -1.0f);glVertex2f(0.0f, 1.0f); / 以上两个点可以画 y 轴glEnd();glBegin(GL_LINE_STRIP);for(x=-1.0f/factor; x“

17、属性”对话框中,设置图片的高度和宽度均为 32。用放大镜观察图片,并编辑之。黑色对应二进制零(镂空) ,白色对应二进制一(不镂空) ,编辑完毕后保存。然后,就可以使用以下代码来获得这个 Mask 数组了。static GLubyte Mask128;FILE *fp;fp = fopen(“mask.bmp“, “rb“);if( !fp )exit(0);/ 移动文件指针到这个位置,使得再读 sizeof(Mask)个字节就会遇到文件结束/ 注意-(int)sizeof(Mask)虽然不是什么好的写法,但这里它确实是正确有效的/ 如果直接写-sizeof(Mask)的话,因为 sizeof

18、取得的是一个无符号数,取负号会有问题if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )exit(0);/ 读取 sizeof(Mask)个字节到 Maskif( !fread(Mask, sizeof(Mask), 1, fp) )exit(0);fclose(fp);好的,现在请自己编辑一个图片作为 mask,并用上述方法取得 Mask 数组,运行后观察效果。说明:绘制虚线时可以设置 factor 因子,但多边形的镂空无法设置 factor 因子。请用鼠标改变窗口的大小,观察镂空效果的变化情况。#include #include void myDisp

19、lay(void)static GLubyte Mask128;FILE *fp;fp = fopen(“mask.bmp“, “rb“);if( !fp )exit(0);if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )exit(0);if( !fread(Mask, sizeof(Mask), 1, fp) )exit(0);fclose(fp);glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_POLYGON_STIPPLE);glPolygonStipple(Mask);glRectf(-0.5f, -0.5f,

20、 0.0f, 0.0f); / 在左下方绘制一个有镂空效果的正方形glDisable(GL_POLYGON_STIPPLE);glRectf(0.0f, 0.0f, 0.5f, 0.5f); / 在右上方绘制一个无镂空效果的正方形glFlush();小结本课学习了绘制几何图形的一些细节。点可以设置大小。直线可以设置宽度;可以将直线画成虚线。多边形的两个面的绘制方法可以分别设置;在三维空间中,不可见的多边形可以被剔除;可以将填充多边形绘制成镂空的样式。了解这些细节会使我们在一些图象绘制中更加得心应手。另外,把一些数据写到程序之外的文件中,并用专门的工具编辑之,有时可以显得更方便。4.第四课:Op

21、enGL 支持两种颜色模式:一种是 RGBA,一种是颜色索引模式。无论哪种颜色模式,计算机都必须为每一个像素保存一些数据。不同的是,RGBA 模式中,数据直接就代表了颜色;而颜色索引模式中,数据代表的是一个索引,要得到真正的颜色,还必须去查索引表。1. RGBA 颜色RGBA 模式中,每一个像素会保存以下数据:R 值(红色分量) 、G 值(绿色分量) 、B 值(蓝色分量)和A 值(alpha 分量) 。其中红、绿、蓝三种颜色相组合,就可以得到我们所需要的各种颜色,而 alpha 不直接影响颜色,它将留待以后介绍。在 RGBA 模式下选择颜色是十分简单的事情,只需要一个函数就可以搞定。glCol

22、or*系列函数可以用于设置颜色,其中三个参数的版本可以指定 R、G、B 的值,而 A 值采用默认;四个参数的版本可以分别指定 R、 G、B 、A 的值。例如:void glColor3f(GLfloat red, GLfloat green, GLfloat blue);void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);(还记得吗?3f 表示有三个浮点参数 请看第二课中关于 glVertex*函数的叙述。 )将浮点数作为参数,其中 0.0 表示不使用该种颜色,而 1.0 表示将该种颜色用到最多。例如:

23、glColor3f(1.0f, 0.0f, 0.0f); 表示不使用绿、蓝色,而将红色使用最多,于是得到最纯净的红色。glColor3f(0.0f, 1.0f, 1.0f); 表示使用绿、蓝色到最多,而不使用红色。混合的效果就是浅蓝色。glColor3f(0.5f, 0.5f, 0.5f); 表示各种颜色使用一半,效果为灰色。注意:浮点数可以精确到小数点后若干位,这并不表示计算机就可以显示如此多种颜色。实际上,计算机可以显示的颜色种数将由硬件决定。如果 OpenGL 找不到精确的颜色,会进行类似“四舍五入”的处理。大家可以通过改变下面代码中 glColor3f 的参数值,绘制不同颜色的矩形。v

24、oid myDisplay(void)glClear(GL_COLOR_BUFFER_BIT);glColor3f(0.0f, 1.0f, 1.0f);glRectf(-0.5f, -0.5f, 0.5f, 0.5f);glFlush();注意:glColor 系列函数,在参数类型不同时,表示“最大”颜色的值也不同。采用 f 和 d 做后缀的函数,以 1.0 表示最大的使用。采用 b 做后缀的函数,以 127 表示最大的使用。采用 ub 做后缀的函数,以 255 表示最大的使用。采用 s 做后缀的函数,以 32767 表示最大的使用。采用 us 做后缀的函数,以 65535 表示最大的使用。这

25、些规则看似麻烦,但熟悉后实际使用中不会有什么障碍。2、索引颜色在索引颜色模式中,OpenGL 需要一个颜色表。这个表就相当于画家的调色板:虽然可以调出很多种颜色,但同时存在于调色板上的颜色种数将不会超过调色板的格数。试将颜色表的每一项想象成调色板上的一个格子:它保存了一种颜色。在使用索引颜色模式画图时,我说“我把第 i 种颜色设置为某某” ,其实就相当于将调色板的第 i 格调为某某颜色。 “我需要第 k 种颜色来画图” ,那么就用画笔去蘸一下第 k 格调色板。颜色表的大小是很有限的,一般在 2564096 之间,且总是 2 的整数次幂。在使用索引颜色方式进行绘图时,总是先设置颜色表,然后选择颜

26、色。2.1、选择颜色使用 glIndex*系列函数可以在颜色表中选择颜色。其中最常用的可能是 glIndexi,它的参数是一个整形。void glIndexi(GLint c);是的,这的确很简单。2.2、设置颜色表OpenGL 并直接没有提供设置颜色表的方法,因此设置颜色表需要使用操作系统的支持。我们所用的Windows 和其他大多数图形操作系统都具有这个功能,但所使用的函数却不相同。正如我没有讲述如何自己写代码在 Windows 下建立一个窗口,这里我也不会讲述如何在 Windows 下设置颜色表。GLUT 工具包提供了设置颜色表的函数 glutSetColor,但我测试始终有问题。现在为

27、了让大家体验一下索引颜色,我向大家介绍另一个 OpenGL 工具包:aux。这个工具包是 VisualStudio 自带的,不必另外安装,但它已经过时,这里仅仅是体验一下,大家不必深入。#include #include #include #pragma comment (lib, “opengl32.lib“)#pragma comment (lib, “glaux.lib“)#include const GLdouble Pi = 3.1415926536;void myDisplay(void)int i;for(i=0; iconst GLdouble Pi = 3.141592653

28、6;void myDisplay(void)int i;/ glShadeModel(GL_FLAT);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_TRIANGLE_FAN);glColor3f(1.0f, 1.0f, 1.0f);glVertex2f(0.0f, 0.0f);for(i=0; i/ 太阳、地球和月亮/ 假设每个月都是 30 天/ 一年 12 个月,共是 360 天static int day = 200; / day 的变化:从 0 到 359void myDisplay(void)/*这里的内容照搬上一课的,只因为使用了双缓冲,补上最后这

29、句*/glutSwapBuffers();void myIdle(void)/* 新的函数,在空闲时调用,作用是把日期往后移动一天并重新绘制,达到动画效果 */+day;if( day = 360 )day = 0;myDisplay();int main(int argc, char *argv)glutInit(glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); / 修改了参数为 GLUT_DOUBLEglutInitWindowPosition(100, 100);glutInitWindowSize(400, 400);glutCreateWind

30、ow(“太阳,地球和月亮“); / 改了窗口标题glutDisplayFunc(glutIdleFunc( / 新加入了这句glutMainLoop();return 0;3.关于垂直同步代码是写好了,但相信大家还有疑问。某些朋友可能在运行时发现,虽然 CPU 几乎都用上了,但运动速度很快,根本看不清楚,另一些朋友在运行时发现 CPU 使用率很低,根本就没有把空闲时间完全利用起来。但对于上面那段代码来说,这些现象都是合理的。这里就牵涉到关于垂直同步的问题。大家知道显示器的刷新率是比较有限的,一般为 60120Hz,也就是一秒钟刷新 60120 次。但如果叫计算机绘制一个简单的画面,例如只有一个

31、三角形,则一秒钟可以绘制成千上万次。因此,如果最大限度的利用计算机的处理能力,绘制很多幅画面,但显示器的刷新速度却跟不上,这不仅造成性能的浪费,还可能带来一些负面影响(例如,显示器只刷新到一半时,需要绘制的内容却变化了,由于显示器是逐行刷新的,于是显示器上半部分和下半部分实际上是来自两幅画面) 。采用垂直同步技术可以解决这一问题。即,只有在显示器刷新时,才把绘制好的图象传输出去供显示。这样一来,计算机就不必去绘制大量的根本就用不到的图象了。如果显示器的刷新率为85Hz,则计算机一秒钟只需要绘制 85 幅图象就足够,如果场景足够简单,就会造成比较多的CPU 空闲。几乎所有的显卡都支持“垂直同步”

32、 这一功能。垂直同步也有它的问题。如果刷新频率为 60Hz,则在绘制比较简单的场景时,绘制一幅图画需要的时间很段,帧速可以恒定在 60FPS(即 60 帧/秒) 。如果场景变得复杂,绘制一幅图画的时间超过了 1/60 秒,则帧速将急剧下降。如果绘制一幅图画的时间为 1/50,则在第一个 1/60 秒时,显示器需要刷新了,但由于新的图画没有画好,所以只能显示原来的图画,等到下一个 1/60 秒时才显示新的图画。于是显示一幅图画实际上用了 1/30 秒,帧速为 30FPS。 (如果不采用垂直同步,则帧速应该是 50FPS)如果绘制一幅图画的时间更长,则下降的趋势就是阶梯状的:60FPS,30FPS

33、 ,20FPS,(60/1,60/2,60/3,)如果每一幅图画的复杂程度是不一致的,且绘制它们需要的时间都在 1/60 上下。则在 1/60 时间内画完时,帧速为 60FPS,在 1/60 时间未完成时,帧速为 30FPS,这就造成了帧速的跳动。这是很麻烦的事情,需要避免它要么想办法简化每一画面的绘制时间,要么都延迟一小段时间,以作到统一。回过头来看前面的问题。如果使用了大量的 CPU 而且速度很快无法看清,则打开垂直同步可以解决该问题。当然如果你认为垂直同步有这样那样的缺点,也可以关闭它。至于如何打开和关闭,因操作系统而异了。具体步骤请自己搜索之。当然,也有其它办法可以控制动画的帧速,或者

34、尽量让动画的速度尽量和帧速无关。不过这里面很多内容都是与操作系统比较紧密的,况且它们跟 OpenGL 关系也不太大。这里就不做介绍了。4、计算帧速不知道大家玩过 3D Mark 这个软件没有,它可以运行各种场景,测出帧速,并且为你的系统给出评分。这里我也介绍一个计算帧速的方法。根据定义,帧速就是一秒钟内播放的画面数目(FPS)。我们可以先测量绘制两幅画面之间时间 t,然后求它的倒数即可。假如 t=0.05s,则 FPS 的值就是 1/0.05=20。理论上是如此了,可是如何得到这个时间呢?通常 C 语言的 time 函数精确度一般只到一秒,肯定是不行了。clock 函数也就到十毫秒左右,还是有

35、点不够。因为 FPS 为 60 和 FPS 为 100 的时候,t 的值都是十几毫秒。你知道如何测量一张纸的厚度吗?一个粗略的办法就是:用很多张纸叠在一起测厚度,计算平均值就可以了。我们这里也可以这样办。测量绘制 50 幅画面(包括垂直同步等因素的等待时间)需要的时间 t,由 t=t*50 很容易的得到 FPS=1/t=50/t下面这段代码可以统计该函数自身的调用频率,(原理就像上面说的那样),程序并不复杂,并且这并不属于 OpenGL 的内容,所以我不打算详细讲述它。#include double CalFrequency()static int count;static double sa

36、ve;static clock_t last, current;double timegap;+count;if( count double FPS = CalFrequency();printf(“FPS = %fn“, FPS);最后的一步,也被我们解决了虽然做法不太雅观,没关系,以后我们还会改善它的。时间过得太久,每次给的程序都只是一小段,一些朋友难免会出问题。现在,我给出一个比较完整的程序,供大家参考。#include #include #include / 太阳、地球和月亮/ 假设每个月都是 12 天/ 一年 12 个月,共是 360 天static int day = 200; /

37、 day 的变化:从 0 到 359double CalFrequency()static int count;static double save;static clock_t last, current;double timegap;+count;if( count = 360 )day = 0;myDisplay();int main(int argc, char *argv)glutInit(glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);glutInitWindowPosition(100, 100);glutInitWindowSize(400, 400);glutCreateWindow(“太阳,地球和月亮“);glutDisplayFunc(glutIdleFunc(glutMainLoop();return 0;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报