首页 opengl入门教程

opengl入门教程

举报
开通vip

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

opengl入门教程
OpenGL入门 教程 人力资源管理pdf成真迷上我教程下载西门子数控教程protel99se入门教程fi6130z安装使用教程 1.第一课: 说起编程作图,大概还有很多人想起TC的#include 吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率、16色来做吗?显然是不行的。 本帖的目的是让大家放弃TC的老旧图形接口,让大家接触一些新事物。 OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性。 1、与C语言紧密结合。 OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenGL是容易理解和学习的。如果你曾经接触过TC的graphics.h,你会发现,使用OpenGL作图甚至比TC更加简单。 2、强大的可移植性。 微软的Direct3D虽然也是十分优秀的图形API,但它只用于Windows系统(现在还要加上一个XBOX游戏机)。而OpenGL不仅用于Windows,还可以用于Unix/Linux等其它系统,它甚至在大型计算机、各种专业计算机(如:医疗用显示设备)上都有应用。并且,OpenGL的基本命令都做到了硬件无关,甚至是平台无关。 3、高性能的图形渲染。 OpenGL是一个工业 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 ,它的技术紧跟时代,现今各个显卡厂家无一不对OpenGL提供强力支持,激烈的竞争中使得OpenGL性能一直领先。 总之,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工具包 GLUT不是OpenGL所必须的,但它会给我们的学习带来一定的方便,推荐安装。 Windows环境下的GLUT下载地址:(大小约为150k) http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip 无法从以上地址下载的话请使用下面的连接: http://upload.programfan.com/upfile/200607311626279.zip Windows环境下安装GLUT的步骤: 1、将下载的压缩包解开,将得到5个文件 2、在“我的电脑”中搜索“gl.h”,并找到其所在文件夹(如果是VisualStudio2005,则应该是其安装目录下面的“VC\PlatformSDK\include\gl文件夹”)。把解压得到的glut.h放到这个文件夹。 3、把解压得到的glut.lib和glut32.lib放到静态函数库所在文件夹(如果是VisualStudio2005,则应该是其安装目录下面的“VC\lib”文件夹)。 4、把解压得到的glut.dll和glut32.dll放到操作系统目录下面的system32文件夹内。(典型的位置为:C:\Windows\System32) 第三步,建立一个OpenGL工程 这里以VisualStudio2005为例。 选择File->New->Project,然后选择Win32 Console Application,选择一个名字,然后按OK。 在谈出的对话框左边点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 main(int argc, char *argv[]) {     glutInit(&argc, argv);     glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);     glutInitWindowPosition(100, 100);     glutInitWindowSize(400, 400);     glutCreateWindow("第一个OpenGL程序");     glutDisplayFunc(&myDisplay);     glutMainLoop();     return 0; } 该程序的作用是在一个黑色的窗口中央画一个白色的矩形。下面对各行语句进行说明。 怎么样?代码还不算长吧? 首先,需要包含头文件#include ,这是GLUT的头文件。 本来OpenGL程序一般还要包含,但GLUT的头文件中已经自动将这两个文件包含了,不必再次包含。 然后看main函数。 int main(int argc, char *argv[]),这个是带命令行参数的main函数,各位应该见过吧?没见过的同志们请多翻翻书,等弄明白了再往下看。 注意main函数中的各语句,除了最后的return之外,其余全部以glut开头。这种以glut开头的函数都是GLUT工具包所提供的函数,下面对用到的几个函数进行介绍。 1、glutInit,对GLUT进行初始化,这个函数必须在其它的GLUT使用之前调用一次。其 格式 pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载 比较死板,一般照抄这句glutInit(&argc, argv)就可以了。 2、glutInitDisplayMode,设置显示方式,其中GLUT_RGB表示使用RGB颜色,与之对应的还有GLUT_INDEX(表示使用索引颜色)。GLUT_SINGLE表示使用单缓冲,与之对应的还有GLUT_DOUBLE(使用双缓冲)。更多信息,请自己Google。当然以后的教程也会有一些讲解。 3、glutInitWindowPosition,这个简单,设置窗口在屏幕中的位置。 4、glutInitWindowSize,这个也简单,设置窗口的大小。 5、glutCreateWindow,根据前面设置的信息创建窗口。参数将被作为窗口的标题。注意:窗口被创建后,并不立即显示到屏幕上。需要调用glutMainLoop才能看到窗口。 6、glutDisplayFunc,设置一个函数,当需要进行画图时,这个函数就会被调用。(这个说法不够准确,但准确的说法可能初学者不太好理解,暂时这样说吧)。 7、glutMainLoop,进行一个消息循环。(这个可能初学者也不太明白,现在只需要知道这个函数可以显示窗口,并且等待窗口关闭后才会返回,这就足够了。) 在glutDisplayFunc函数中,我们设置了“当需要画图时,请调用myDisplay函数”。于是myDisplay函数就用来画图。观察myDisplay中的三个函数调用,发现它们都以gl开头。这种以gl开头的函数都是OpenGL的标准函数,下面对用到的函数进行介绍。 1、glClear,清除。GL_COLOR_BUFFER_BIT表示清除颜色,glClear函数还可以清除其它的东西,但这里不作介绍。 2、glRectf,画一个矩形。四个参数分别表示了位于对角线上的两个点的横、纵坐标。 3、glFlush,保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)。其作用跟fflush(stdout)类似。 2.第二课: 本次课程所要讲的是绘制简单的几何图形,在实际绘制之前,让我们先熟悉一些概念。 一、点、直线和多边形 我们知道数学(具体的说,是几何学)中有点、直线和多边形的概念,但这些概念在计算机中会有所不同。 数学上的点,只有位置,没有大小。但在计算机中,无论计算精度如何提高,始终不能表示一个无穷小的点。另一方面,无论图形输出设备(例如,显示器)如何精确,始终不能输出一个无穷小的点。一般情况下,OpenGL中的点将被画成单个的像素(像素的概念,请自己搜索之~),虽然它可能足够小,但并不会是无穷小。同一像素上,OpenGL可以绘制许多坐标只有稍微不同的点,但该像素的具体颜色将取决于OpenGL的实现。当然,过度的注意细节就是钻牛角尖,我们大可不必花费过多的精力去研究“多个点如何画到同一像素上”。 同样的,数学上的直线没有宽度,但OpenGL的直线则是有宽度的。同时,OpenGL的直线必须是有限长度,而不是像数学概念那样是无限的。可以认为,OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定。 多边形是由多条线段首尾相连而形成的闭合区域。OpenGL 规定 关于下班后关闭电源的规定党章中关于入党时间的规定公务员考核规定下载规定办法文件下载宁波关于闷顶的规定 ,一个多边形必须是一个“凸多边形”(其定义为:多边形内任意两点所确定的线段都在多边形内,由此也可以推导出,凸多边形不能是空心的)。多边形可以由其边的端点(这里可称为顶点)来确定。(注意:如果使用的多边形不是凸多边形,则最后输出的效果是未定义的——OpenGL为了效率,放宽了检查,这可能导致显示错误。要避免这个错误,尽量使用三角形,因为三角形都是凸多边形) 可以想象,通过点、直线和多边形,就可以组合成各种几何图形。甚至于,你可以把一段弧看成是很多短的直线段相连,这些直线段足够短,以至于其长度小于一个像素的宽度。这样一来弧和圆也可以表示出来了。通过位于不同平面的相连的小多边形,我们还可以组成一个“曲面”。 二、在OpenGL中指定顶点 由以上的讨论可以知道,“点”是一切的基础。 如何指定一个点呢?OpenGL提供了一系列函数。它们都以glVertex开头,后面跟一个数字和1~2个字母。例如: glVertex2d glVertex2f glVertex3f glVertex3fv 等等。 数字表示参数的个数,2表示有两个参数,3表示三个,4表示四个(我知道有点罗嗦~)。 字母表示参数的类型,s表示16位整数(OpenGL中将这个类型定义为GLshort),                   i表示32位整数(OpenGL中将这个类型定义为GLint和GLsizei),                   f表示32位浮点数(OpenGL中将这个类型定义为GLfloat和GLclampf),                   d表示64位浮点数(OpenGL中将这个类型定义为GLdouble和GLclampd)。                   v表示传递的几个参数将使用指针的方式,见下面的例子。 这些函数除了参数的类型和个数不同以外,功能是相同的。例如,以下五个代码段的功能是等效的: (一)glVertex2i(1, 3); (二)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是如何知道我想拿这些顶点来干什么呢?是一个一个的画出来,还是连成线?或者构成一个多边形?或者做其它什么事情? 为了解决这一问题,OpenGL要求:指定顶点的命令必须包含在glBegin函数之后,glEnd函数之前(否则指定的顶点将被忽略)。并由glBegin来指明如何使用这些点。 例如我写: glBegin(GL_POINTS);     glVertex2f(0.0f, 0.0f);     glVertex2f(0.5f, 0.0f); glEnd(); 则这两个点将分别被画出来。如果将GL_POINTS替换成GL_LINES,则两个点将被认为是直线的两个端点,OpenGL将会画出一条直线。 我们还可以指定更多的顶点,然后画出更复杂的图形。 另一方面,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 myDisplay(void) {     glClear(GL_COLOR_BUFFER_BIT);     glBegin( /* 在这里填上你所希望的模式 */ );         /* 在这里使用glVertex*系列函数 */         /* 指定你所希望的顶点位置 */     glEnd();     glFlush(); } 把这段代码改成你喜欢的样子,然后用它替换第一课中的myDisplay函数,编译后即可运行。 两个例子 例一、画一个圆 /* 正四边形,正五边形,正六边形,……,直到正n边形,当n越大时,这个图形就越接近圆 当n大到一定程度后,人眼将无法把它跟真正的圆相区别 这时我们已经成功的画出了一个“圆” (注:画圆的方法很多,这里使用的是比较简单,但效率较低的一种) 试修改下面的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; i const 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);     GLfloat         PointA[2] = { 0, a },         PointB[2] = { bx, by },         PointC[2] = { 0.5, cy },         PointD[2] = { -0.5, cy },         PointE[2] = { -bx, by };     glClear(GL_COLOR_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的值,观察变化情况 */ #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<1.0f/factor; x+=0.01f)     {         glVertex2f(x*factor, sin(x)*factor);     }     glEnd();     glFlush(); } 小结 本课讲述了点、直线和多边形的概念,以及如何使用OpenGL来描述点,并使用点来描述几何图形。 大家可以发挥自己的想象,画出各种几何图形,当然,也可以用GL_LINE_STRIP把很多位置相近的点连接起来,构成函数图象。如果有兴趣,也可以去找一些图象比较美观的函数,自己动手,用OpenGL把它画出来。 3.第三课: 1、关于点 点的大小默认为1个像素,但也可以改变之。改变的命令为glPointSize,其函数原型如下: void glPointSize(GLfloat size); size必须大于0.0f,默认值为1.0f,单位为“像素”。 注意:对于具体的OpenGL实现,点的大小都有个限度的,如果设置的size超过最大值,则设置可能会有问题。 例子: void myDisplay(void) {     glClear(GL_COLOR_BUFFER_BIT);     glPointSize(5.0f);     glBegin(GL_POINTS);         glVertex2f(0.0f, 0.0f);         glVertex2f(0.5f, 0.5f);     glEnd();     glFlush(); } 2、关于直线 (1)直线可以指定宽度: void glLineWidth(GLfloat width); 其用法跟glPointSize类似。 (2)画虚线。 首先,使用glEnable(GL_LINE_STIPPLE);来启动虚线模式(使用glDisable(GL_LINE_STIPPLE)可以关闭之)。 然后,使用glLineStipple来设置虚线的样式。 void glLineStipple(GLint factor, GLushort pattern); pattern是由1和0组成的长度为16的序列,从最低位开始看,如果为1,则直线上接下来应该画的factor个点将被画为实的;如果为0,则直线上接下来应该画的factor个点将被画为虚的。 以下是一些例子: 声明:该图片来自www.opengl.org,该图片是《OpenGL编程指南》一书的附图,由于该书的旧版(第一版,1994年)已经流传于网络,我希望没有触及到版权问题。 示例代码: void myDisplay(void) {     glClear(GL_COLOR_BUFFER_BIT);     glEnable(GL_LINE_STIPPLE);     glLineStipple(2, 0x0F0F);     glLineWidth(10.0f);     glBegin(GL_LINES);         glVertex2f(0.0f, 0.0f);         glVertex2f(0.5f, 0.5f);     glEnd();     glFlush(); } 3、关于多边形 多边形的内容较多,我们将讲述以下四个方面。 (1)多边形的两面以及绘制方式。 虽然我们目前还没有真正的使用三维坐标来画图,但是建立一些三维的概念还是必要的。 从三维的角度来看,一个多边形具有两个面。每一个面都可以设置不同的绘制方式:填充、只绘制边缘轮廓线、只绘制顶点,其中“填充”是默认的方式。可以为两个面分别设置不同的方式。 glPolygonMode(GL_FRONT, GL_FILL);           // 设置正面为填充方式 glPolygonMode(GL_BACK, GL_LINE);            // 设置反面为边缘绘制方式 glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 设置两面均为顶点绘制方式 (2)反转 一般约定为“顶点以逆时针顺序出现在屏幕上的面”为“正面”,另一个面即成为“反面”。生活中常见的物体表面,通常都可以用这样的“正面”和“反面”,“合理的”被表现出来(请找一个比较透明的矿泉水瓶子,在正对你的一面沿逆时针画一个圆,并标明画的方向,然后将背面转为正面,画一个类似的圆,体会一下“正面”和“反面”。你会发现正对你的方向,瓶的外侧是正面,而背对你的方向,瓶的内侧才是正面。正对你的内侧和背对你的外侧则是反面。这样一来,同样属于“瓶的外侧”这个表面,但某些地方算是正面,某些地方却算是反面了)。 但也有一些表面比较特殊。例如“麦比乌斯带”(请自己Google一下),可以全部使用“正面”或全部使用“背面”来表示。 可以通过glFrontFace函数来交换“正面”和“反面”的概念。 glFrontFace(GL_CCW);  // 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针 glFrontFace(GL_CW);   // 设置CW方向为“正面”,CW即ClockWise,顺时针 下面是一个示例程序,请用它替换第一课中的myDisplay函数,并将glFrontFace(GL_CCW)修改为glFrontFace(GL_CW),并观察结果的变化。 void myDisplay(void) {     glClear(GL_COLOR_BUFFER_BIT);     glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充模式     glPolygonMode(GL_BACK, GL_LINE);  // 设置反面为线形模式     glFrontFace(GL_CCW);              // 设置逆时针方向为正面     glBegin(GL_POLYGON);              // 按逆时针绘制一个正方形,在左下方         glVertex2f(-0.5f, -0.5f);         glVertex2f(0.0f, -0.5f);         glVertex2f(0.0f, 0.0f);         glVertex2f(-0.5f, 0.0f);     glEnd();     glBegin(GL_POLYGON);              // 按顺时针绘制一个正方形,在右上方         glVertex2f(0.0f, 0.0f);         glVertex2f(0.0f, 0.5f);         glVertex2f(0.5f, 0.5f);         glVertex2f(0.5f, 0.0f);     glEnd();     glFlush(); } (3)剔除多边形表面 在三维空间中,一个多边形虽然有两个面,但我们无法看见背面的那些多边形,而一些多边形虽然是正面的,但被其他多边形所遮挡。如果将无法看见的多边形和可见的多边形同等对待,无疑会降低我们处理图形的效率。在这种时候,可以将不必要的面剔除。 首先,使用glEnable(GL_CULL_FACE);来启动剔除功能(使用glDisable(GL_CULL_FACE)可以关闭之) 然后,使用glCullFace来进行剔除。 glCullFace的参数可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分别表示剔除正面、剔除反面、剔除正反两面的多边形。 注意:剔除功能只影响多边形,而对点和直线无影响。例如,使用glCullFace(GL_FRONT_AND_BACK)后,所有的多边形都将被剔除,所以看见的就只有点和直线。 (4)镂空多边形 直线可以被画成虚线,而多边形则可以进行镂空。 首先,使用glEnable(GL_POLYGON_STIPPLE);来启动镂空模式(使用glDisable(GL_POLYGON_STIPPLE)可以关闭之)。 然后,使用glPolygonStipple来设置镂空的样式。 void glPolygonStipple(const GLubyte *mask); 其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面的颜色),最后一个字节表示了最右上方的8个像素是否镂空。 但是,如果我们直接定义这个mask数组,像这样: static GLubyte Mask[128] = {     0x00, 0x00, 0x00, 0x00,   //  这是最下面的一行     0x00, 0x00, 0x00, 0x00,     0x03, 0x80, 0x01, 0xC0,   //  麻     0x06, 0xC0, 0x03, 0x60,   //  烦     0x04, 0x60, 0x06, 0x20,   //  的     0x04, 0x30, 0x0C, 0x20,   //  初     0x04, 0x18, 0x18, 0x20,   //  始     0x04, 0x0C, 0x30, 0x20,   //  化     0x04, 0x06, 0x60, 0x20,   //  ,     0x44, 0x03, 0xC0, 0x22,   //  不     0x44, 0x01, 0x80, 0x22,   //  建     0x44, 0x01, 0x80, 0x22,   //  议     0x44, 0x01, 0x80, 0x22,   //  使     0x44, 0x01, 0x80, 0x22,   //  用     0x44, 0x01, 0x80, 0x22,     0x44, 0x01, 0x80, 0x22,     0x66, 0x01, 0x80, 0x66,     0x33, 0x01, 0x80, 0xCC,     0x19, 0x81, 0x81, 0x98,     0x0C, 0xC1, 0x83, 0x30,     0x07, 0xE1, 0x87, 0xE0,     0x03, 0x3F, 0xFC, 0xC0,     0x03, 0x31, 0x8C, 0xC0,     0x03, 0x3F, 0xFC, 0xC0,     0x06, 0x64, 0x26, 0x60,     0x0C, 0xCC, 0x33, 0x30,     0x18, 0xCC, 0x33, 0x18,     0x10, 0xC4, 0x23, 0x08,     0x10, 0x63, 0xC6, 0x08,     0x10, 0x30, 0x0C, 0x08,     0x10, 0x18, 0x18, 0x08,     0x10, 0x00, 0x00, 0x08   // 这是最上面的一行 }; 这样一堆数据非常缺乏直观性,我们需要很费劲的去分析,才会发现它表示的竟然是一只苍蝇。 如果将这样的数据保存成图片,并用专门的工具进行编辑,显然会方便很多。下面介绍如何做到这一点。 首先,用Windows自带的画笔程序新建一副图片,取名为mask.bmp,注意保存时,应该选择“单色位图”。在“图象”->“属性”对话框中,设置图片的高度和宽度均为32。 用放大镜观察图片,并编辑之。黑色对应二进制零(镂空),白色对应二进制一(不镂空),编辑完毕后保存。 然后,就可以使用以下代码来获得这个Mask数组了。 static GLubyte Mask[128]; FILE *fp; fp = fopen("mask.bmp", "rb"); if( !fp )     exit(0); // 移动文件指针到这个位置,使得再读sizeof(Mask)个字节就会遇到文件结束 // 注意-(int)sizeof(Mask)虽然不是什么好的写法,但这里它确实是正确有效的 // 如果直接写-sizeof(Mask)的话,因为sizeof取得的是一个无符号数,取负号会有问题 if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )     exit(0); // 读取sizeof(Mask)个字节到Mask if( !fread(Mask, sizeof(Mask), 1, fp) )     exit(0); fclose(fp); 好的,现在请自己编辑一个图片作为mask,并用上述方法取得Mask数组,运行后观察效果。 说明:绘制虚线时可以设置factor因子,但多边形的镂空无法设置factor因子。请用鼠标改变窗口的大小,观察镂空效果的变化情况。 #include  #include  void myDisplay(void) {     static GLubyte Mask[128];     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, 0.0f, 0.0f);  // 在左下方绘制一个有镂空效果的正方形     glDisable(GL_POLYGON_STIPPLE);     glRectf(0.0f, 0.0f, 0.5f, 0.5f);    // 在右上方绘制一个无镂空效果的正方形     glFlush(); } 小结 本课学习了绘制几何图形的一些细节。 点可以设置大小。 直线可以设置宽度;可以将直线画成虚线。 多边形的两个面的绘制方法可以分别设置;在三维空间中,不可见的多边形可以被剔除;可以将填充多边形绘制成镂空的样式。 了解这些细节会使我们在一些图象绘制中更加得心应手。 另外,把一些数据写到程序之外的文件中,并用专门的工具编辑之,有时可以显得更方便。 4.第四课: OpenGL支持两种颜色模式:一种是RGBA,一种是颜色索引模式。 无论哪种颜色模式,计算机都必须为每一个像素保存一些数据。不同的是,RGBA模式中,数据直接就代表了颜色;而颜色索引模式中,数据代表的是一个索引,要得到真正的颜色,还必须去查索引表。 1. RGBA颜色 RGBA模式中,每一个像素会保存以下数据:R值(红色分量)、G值(绿色分量)、B值(蓝色分量)和A值(alpha分量)。其中红、绿、蓝三种颜色相组合,就可以得到我们所需要的各种颜色,而alpha不直接影响颜色,它将留待以后介绍。 在RGBA模式下选择颜色是十分简单的事情,只需要一个函数就可以搞定。 glColor*系列函数可以用于设置颜色,其中三个参数的版本可以指定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表示将该种颜色用到最多。例如: glColor3f(1.0f, 0.0f, 0.0f);   表示不使用绿、蓝色,而将红色使用最多,于是得到最纯净的红色。 glColor3f(0.0f, 1.0f, 1.0f);   表示使用绿、蓝色到最多,而不使用红色。混合的效果就是浅蓝色。 glColor3f(0.5f, 0.5f, 0.5f);   表示各种颜色使用一半,效果为灰色。 注意:浮点数可以精确到小数点后若干位,这并不表示计算机就可以显示如此多种颜色。实际上,计算机可以显示的颜色种数将由硬件决定。如果OpenGL找不到精确的颜色,会进行类似“四舍五入”的处理。 大家可以通过改变下面代码中glColor3f的参数值,绘制不同颜色的矩形。 void 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表示最大的使用。 这些规则看似麻烦,但熟悉后实际使用中不会有什么障碍。 2、索引颜色 在索引颜色模式中,OpenGL需要一个颜色表。这个表就相当于画家的调色板:虽然可以调出很多种颜色,但同时存在于调色板上的颜色种数将不会超过调色板的格数。试将颜色表的每一项想象成调色板上的一个格子:它保存了一种颜色。 在使用索引颜色模式画图时,我说“我把第i种颜色设置为某某”,其实就相当于将调色板的第i格调为某某颜色。“我需要第k种颜色来画图”,那么就用画笔去蘸一下第k格调色板。 颜色表的大小是很有限的,一般在256~4096之间,且总是2的整数次幂。在使用索引颜色方式进行绘图时,总是先设置颜色表,然后选择颜色。 2.1、选择颜色 使用glIndex*系列函数可以在颜色表中选择颜色。其中最常用的可能是glIndexi,它的参数是一个整形。 void glIndexi(GLint c); 是的,这的确很简单。 2.2、设置颜色表 OpenGL并直接没有提供设置颜色表的方法,因此设置颜色表需要使用操作系统的支持。我们所用的Windows和其他大多数图形操作系统都具有这个功能,但所使用的函数却不相同。正如我没有讲述如何自己写代码在Windows下建立一个窗口,这里我也不会讲述如何在Windows下设置颜色表。 GLUT工具包提供了设置颜色表的函数glutSetColor,但我测试始终有问题。现在为了让大家体验一下索引颜色,我向大家介绍另一个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; i<8; ++i)         auxSetOneColor(i, (float)(i&0x04), (float)(i&0x02), (float)(i&0x01));     glShadeModel(GL_FLAT);     glClear(GL_COLOR_BUFFER_BIT);     glBegin(GL_TRIANGLE_FAN);     glVertex2f(0.0f, 0.0f);     for(i=0; i<=8; ++i)     {         glIndexi(i);         glVertex2f(cos(i*Pi/4), sin(i*Pi/4));     }     glEnd();     glFlush(); } int main(void) {     auxInitDisplayMode(AUX_SINGLE|AUX_INDEX);     auxInitPosition(0, 0, 400, 400);     auxInitWindow(L"");     myDisplay();     Sleep(10 * 1000);     return 0; } 其它部分大家都可以不管,只看myDisplay函数就可以了。首先,使用auxSetOneColor设置颜色表中的一格。循环八次就可以设置八格。 glShadeModel等下再讲,这里不提。 然后在循环中用glVertex设置顶点,同时用glIndexi改变顶点代表的颜色。 最终得到的效果是八个相同形状、不同颜色的三角形。 索引颜色虽然讲得多了点。索引颜色的主要优势是占用空间小(每个像素不必单独保存自己的颜色,只用很少的二进制位就可以代表其颜色在颜色表中的位置),花费系统资源少,图形运算速度快,但它编程稍稍显得不是那么方便,并且画面效果也会比RGB颜色差一些。“星际争霸”可能代表了256色的颜色表的画面效果,虽然它在一台很烂的PC上也可以运行很流畅,但以目前的眼光来看,其画面效果就显得不足了。 目前的PC机性能已经足够在各种场合下使用RGB颜色,因此PC程序开发中,使用索引颜色已经不是主流。当然,一些小型设备例如GBA、手机等,索引颜色还是有它的用武之地。 3、指定清除屏幕用的颜色 我们写:glClear(GL_COLOR_BUFFER_BIT);意思是把屏幕上的颜色清空。 但实际上什么才叫“空”呢?在宇宙中,黑色代表了“空”;在一张白纸上,白色代表了“空”;在信封上,信封的颜色才是“空”。 OpenGL用下面的函数来定义清楚屏幕后屏幕所拥有的颜色。 在RGB模式下,使用glClearColor来指定“空”的颜色,它需要四个参数,其参数的意义跟glColor4f相似。 在索引颜色模式下,使用glClearIndex来指定“空”的颜色所在的索引,它需要一个参数,其意义跟glIndexi相似。 void myDisplay(void) {     glClearColor(1.0f, 0.0f, 0.0f, 0.0f);     glClear(GL_COLOR_BUFFER_BIT);     glFlush(); } 呵,这个还真简单~ 4、指定着色模型 OpenGL允许为同一多边形的不同顶点指定不同的颜色。例如: #include  const GLdouble Pi = 3.1415926536; 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<=8; ++i)     {         glColor3f(i&0x04, i&0x02, i&0x01);         glVertex2f(cos(i*Pi/4), sin(i*Pi/4));     }     glEnd();     glFlush(); } 在默认情况下,OpenGL会计算两点顶点之间的其它点,并为它们填上“合适”的颜色,使相邻的点的颜色值都比较接近。如果使用的是RGB模式,看起来就具有渐变的效果。如果是使用颜色索引模式,则其相邻点的索引值是接近的,如果将颜色表中接近的项设置成接近的颜色,则看起来也是渐变的效果。但如果颜色表中接近的项颜色却差距很大,则看起来可能是很奇怪的效果。 使用glShadeModel函数可以关闭这种计算,如果顶点的颜色不同,则将顶点之间的其它点全部设置为与某一个点相同。(直线以后指定的点的颜色为准,而多边形将以任意顶点的颜色为准,由实现决定。)为了避免这个不确定性,尽量在多边形中使用同一种颜色。 glShadeModel的使用方法: glShadeModel(GL_SMOOTH);   // 平滑方式,这也是默认方式 glShadeModel(GL_FLAT);     // 单色方式 小结: 本课学习了如何设置颜色。其中RGB颜色方式是目前PC机上的常用方式。 可以设置glClear清除后屏幕所剩的颜色。 可以设置颜色填充方式:平滑方式或单色方式。 5. 第五课: 在前面绘制几何图形的时候,大家是否觉得我们绘图的范围太狭隘了呢?坐标只能从-1到1,还只能是X轴向右,Y轴向上,Z轴垂直屏幕。这些限制给我们的绘图带来了很多不便。 我们生活在一个三维的世界——如果要观察一个物体,我们可以: 1、从不同的位置去观察它。(视图变换) 2、移动或者旋转它,当然了,如果它只是计算机里面的物体,我们还可以放大或缩小它。(模型变换) 3、如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(剪裁)。(投影变换) 4、我们可能希望把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(视口变换) 这些,都可以在OpenGL中实现。 OpenGL变换实际上是通过矩阵乘法来实现。无论是移动、旋转还是缩放大小,都是通过在当前矩阵的基础上乘以一个新的矩阵来达到目的。关于矩阵的知识,这里不详细介绍,有兴趣的朋友可以看看线性代数(大学生的话多半应该学过的)。 OpenGL可以在最底层直接操作矩阵,不过作为初学,这样做的意义并不大。这里就不做介绍了。 模型变换和视图变换 从“相对移动”的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性。在OpenGL中,实现这两种功能甚至使用的是同样的函数。 由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode函数,像这样: glMatrixMode(GL_MODELVIEW); 通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。这也只需要一行代码: glLoadIdentity(); 然后,就可以进行模型变换和视图变换了。进行模型和视图变换,主要涉及到三个函数: glTranslate*,把当前矩阵和一个表示移动物体的矩阵相乘。三个参数分别表示了在三个坐标上的位移值。 glRotate*,把当前矩阵和一个表示旋转物体的矩阵相乘。物体将绕着(0,0,0)到(x,y,z)的直线以逆时针旋转,参数angle表示旋转的角度。 glScale*,把当前矩阵和一个表示缩放物体的矩阵相乘。x,y,z分别表示在该方向上的缩放比例。 注意我都是说“与XX相乘”,而不是直接说“这个函数就是旋转”或者“这个函数就是移动”,这是有原因的,马上就会讲到。 假设当前矩阵为单位矩阵,然后先乘以一个表示旋转的矩阵R,再乘以一个表示移动的矩阵T,最后得到的矩阵再乘上每一个顶点的坐标矩阵v。所以,经过变换得到的顶点坐标就是((RT)v)。由于矩阵乘法的结合率,((RT)v) = (R(Tv)),换句话说,实际上是先进行移动,然后进行旋转。即:实际变换的顺序与代码中写的顺序是相反的。由于“先移动后旋转”和“先旋转后移动”得到的结果很可能不同,初学的时候需要特别注意这一点。 OpenGL之所以这样设计,是为了得到更高的效率。但在绘制复杂的三维图形时,如果每次都去考虑如何把变换倒过来,也是很痛苦的事情。这里介绍另一种思路,可以让代码看起来更自然(写出的代码其实完全一样,只是考虑问题时用的方法不同了)。 让我们想象,坐标并不是固定不变的。旋转的时候,坐标系统随着物体旋转。移动的时候,坐标系统随着物体移动。如此一来,就不需要考虑代码的顺序反转的问题了。 以上都是针对改变物体的位置和方向来介绍的。如果要改变观察点的位置,除了配合使用glRotate*和glTranslate*函数以外,还可以使用这个函数:gluLookAt。它的参数比较多,前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表示了观察者认为的“上”方向。 2、投影变换 投影变换就是定
本文档为【opengl入门教程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_538200
暂无简介~
格式:doc
大小:559KB
软件:Word
页数:94
分类:互联网
上传时间:2011-11-29
浏览量:75