版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本章内容编写图形程序学习OpenGL程序的基本组成开发绘制点、直线、折线和多边形的基本图形工具学会用鼠标和键盘控制程序1本章内容编写图形程序12.0vcopengl编程
指导例子熟悉代码,多次练习见图形学第二课opengl-console.doc,创建一个控制台的应用程序,本书采用的编程方式见图形学第二课opengl-nehe-win.doc,创建一个win32的应用程序,nehe教程(共48课)采用的编程方式见图形学第三课opengl-nehe-mfc.doc,创建一个mfc的应用程序,本人推荐采用的编程方式,需要学习vc和mfc。22.0vcopengl编程
指导例子熟2.1生成图像初步通过练习(编写和测试各种不同图形的程序)可以很快掌握计算机图形学。先从简单的任务开始,一旦掌握,就试着变变花样,看看有哪些变化,试着进一步绘制更复杂的场景。最好的学习办法是认真、仔细地学习每行代码或等式。当你这么做时,就能理解每个新概念。每个图形程序都以一些初始化工作为开始,由此建立程序所需要的显示模型和坐标系。32.1生成图像初步通过练习(编写和测试各种不同图形的程序2.1生成图像初步初始化时,将显示器设置为“图形模式”,并且建立了坐标系,坐标x和y以像素为单位,x向右递增,y向下递增。42.1生成图像初步初始化时,将显示器设置为“图形模式”,2.1.1设备无关的编程
和OpenGLOpenGL程序可以在不同的图形环境里编译和运行,即它是与设备无关的编程。OpenGL是一个开源图形库,可在下载。OpenGL常被称为应用程序接口(API)。这个接口是程序员可以调用的例程,先从OpenGL绘制简单的二维物体开始,然后学习绘制三维图形。52.1.1设备无关的编程
和OpenGLO2.1.2窗口的编程事件驱动编程:事件包括单击鼠标,按下键盘上的按键,或者重新调整窗口的大小。程序员将程序组织成回调函数的集合,这些回调函数一有事件发生就执行。即什么也不做,等待事件发生,事件发生后再做指定的事。OpenGL有一个GLUT库,它用来打开窗口,管理菜单和处理事件等。注册回调函数:有一种方法将每种类型的事件与要求的回调函数关联起来,这个方法称之为注册回调函数。程序中用到的每一个事件类型都必须用回调函数注册,该回调函数的名字和定义由程序员选择。62.1.2窗口的编程事件驱动编程:事件包括单击鼠标,按下2.1.2窗口的编程下面是使用GLUT库,名为myMouse的回调函数例子,它方便地注册了与鼠标关联的事件:glutMouseFunc(myMouse);glutMouseFunc是GLUT库的固有函数,但是回调函数myMouse是程序员定义的,并由程序员编写代码,处理每个可能感兴趣的鼠标动作。4种主要的OpenGL库:1)基本GL库,OpenGL库的基础。它提供OpenGL的基本函数。每个OpenGL函数都以字符GL开头。72.1.2窗口的编程下面是使用GLUT库,名为myMou2.1.2窗口的编程2)GLUT库:GL实用工具包。它用来打开窗口,开发和管理菜单,以及管理事件等。3)GLU库:GL实用库,它提供高级例程,处理矩阵操作和绘制二次曲面如球和圆柱体;4)GLUI库:用户接口库,提供控制工具和菜单。glutDisplayFunc(myDisplay):重绘窗口调用myDisplay回调函数;glutReshapeFun(myReshape):对屏幕窗口的形状进行调整;82.1.2窗口的编程2)GLUT库:GL实用工具包。它用voidmain(){glutDisplayFunc(myDisplay);//注册重绘函数glutReshapeFunc(myReshape);//注册改变窗口形状函数glutMouseFunc(myMouse);//注册鼠标动作函数glutMotionFunc(myMouse);//注册鼠标移动函数glutKeyboardFunc(myKeyboard);//注册键盘动作函数//可能初始化其他工作glutMainLoop();//进入主循环等待事件发生}9voidmain()92.1.3如何打开一个窗口画图glutInit:该函数初始化工具包glutDisplayMode(GLUT_SINGLE|GLUT_RGB):分配单个显示缓存glutInitWindowSize(640,480):该函数指定屏幕的初始尺寸,宽640像素,高480像素。glutCreateWindow(“myfirstattempt”):该函数打开并显示屏幕窗口吗,并显示标题。代码如下:102.1.3如何打开一个窗口画图glutInit:该函数voidmain(intargc,char**argv){glutInit(&argc,argv);//初始化工具包glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);//设置显示模式glutInitWindowSize(640,480);//设置窗口大小glutInitWindowPosition(100,150);//设置窗口在屏幕上的位置glutCreateWindow("myfirstattempt");//打开屏幕窗口11voidmain(intargc,char**arg//注册回调函数glutDisplayFunc(myDisplay);glutReshapeFunc(myReshape);glutMouseFunc(myMouse);glutKeyboardFunc(myKeyboard);myInit();//必要的其他初始化工作glutMainLoop();//进入循环}12//注册回调函数122.2OpenGL的基本图形元素屏幕坐标系:宽为640,高为640。x坐标从左边界0增加到右边界639,y坐标从0增加到上边的479。132.2OpenGL的基本图形元素屏幕坐标系:宽为640,高绘制三个点:glBegin(GL_POINTS);glVertex2i(100,50);glVertex2i(100,130);glVertex2i(150,130);glEnd();或者用浮点值代替整数值:glBegin(GL_POINTS);glVertex2d(100.0,50.0);glVertex2d(100.0,130.0);glVertex2d(150.0,130.0);glEnd();14绘制三个点:14OpenGL数据类型:命令后缀和参数数据类型OpenGL状态:OpenGL是由许多状态变量组成的状态机,这些状态包括点的大小、绘图的颜色额屏幕窗口大小等,点的大小可以用glPointSize()来设置,画图的颜色用这条语句设置:glColor3f(red,green,blue);坐标系的建立:1515voidmyInit(void){glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0,640.0,0,480.0);}myInit设置坐标系,设置透视投影变换。gluOrtho2D设置正交投影。组合在一起:建立起一个完整的OpenGL程序glFlush()函数保证所有的数据被完全处理并显示。16voidmyInit(void)162.2.1几个点丛绘制的例子点丛是由大量点组成的某种图案。下面有几个点丛例子。172.2.1几个点丛绘制的例子点丛是由大量点组成的某种图案例子2.2.1北斗星群
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc例子2.2.2Sierpinski(塞平斯基)垫片见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc算法如下:第k个点表示为P(k)=(x(k),y(k)),每一个点都依赖于前一个点P(k-1),过程如下:(1)选择三个固定的点T0,T1,T2,构成一个三角形,称为每个Sierpinski垫片的父三角形;(2)随机选择父三角形顶点T0,T1,T2中的一点作为要绘制的初始点P0。迭代下面的(3)~(5)步,直到图案填充完毕。(3)随机选择T0,T1,T2中的一点,称为T。(4)构造下一个点P(k),作为T和前一个已建好的点P(k-1)之间的之间点,即P(k)=(P(k-1)+T)/2(5)用drawDot()绘制P(k)。18例子2.2.1北斗星群18例子2.2.2Sierpinski(塞平斯基)垫片//初始值P(0),在for循环外面GLintPointpoint=T[index];//初始值drawDot(point.x,point.y);//画初始点for(inti=0;i<1000;i++)//画1000点{index=rand()%3;//P(k)=(P(k-1)+T)/2point.x=(point.x+T[index].x)/2;point.y=(point.y+T[index].y)/2;drawDot(point.x,point.y);}19例子2.2.2Sierpinski(塞平斯基)垫片19例子2.2.3用点集绘制函数
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc绘制某个数学函数f(x),当x在0~4变化时的曲线。当绘制曲线时,因为0~4之间的x值位于屏幕窗口左下角的前4个像素上,而且f(.)的负值位于窗口的下面,根本看不到。需要进行平移和缩放使曲线正好出现在合适的屏幕窗口。将世界坐标系转换为窗口坐标系。x方向缩放:假定屏幕窗口为screenWidth,单位为像素,现在x的范围为0~4,故sx=x*screenWidth/4.0;y方向缩放:假定屏幕窗口为screenHeight,单位为像素,f(x)的取值在–1.0和1.0之间,缩放screenHeight/2,平移为screenHeight/2,sy=(y+1.0)*screenHeight/2.0;sx,sy为屏幕坐标,x,y为窗口坐标;转换方程为:sx=A*x+Bsy=C*y+D20例子2.2.3用点集绘制函数20constintscreenWidth=640;//widthofscreenwindowinpixelsconstintscreenHeight=480;//heightofscreenwindowinpixelsGLdoubleA,B,C,D;//valuesusedforscalingandshiftingvoidmyInit(void){glClearColor(1.0,1.0,1.0,0.0);//backgroundcoloriswhiteglColor3f(0.0f,0.0f,0.0f);//drawingcolorisblackglPointSize(2.0);//a'dot'is2by2pixelsglMatrixMode(GL_PROJECTION);//set"camerashape"glLoadIdentity();gluOrtho2D(0.0,(GLdouble)screenWidth,0.0,(GLdouble)screenHeight);A=screenWidth/4.0;//x方向的缩放量AB=0.0;//x方向的平移量BC=D=screenHeight/2.0;//y方向的缩放量C和平移量D}voidmyDisplay(void){glClear(GL_COLOR_BUFFER_BIT);//clearthescreenglBegin(GL_POINTS);for(GLdoublex=0;x<4.0;x+=0.005){Gldoublefunc=exp(-x)*cos(2*3.14159265*x);//sx=A*x+B,sy=C*y+DglVertex2d(A*x+B,C*func+D);}glEnd();glFlush();//sendalloutputtodisplay}21constintscreenWidth=640;/例子2.2.3用点集绘制函数
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.docf(x)=sin(x),f(x)=sin(2x),x从-π到π,y从-1到1A=screenWidth/6.28;//x方向的缩放量A,屏幕宽度/x方向的变化范围B=screenWidth/2.0;//x方向的平移量B,屏幕宽度的一半C=screenHeight/2.0;//y方向的缩放量C,屏幕高度/y方向的变化范围
D=screenHeight/2.0;//y方向的平移量D,屏幕的高度的一半for(GLdoublex=-3.14;x<3.14;x+=0.005){Gldoublefunc=sin(x);Gldoublefunc1=sin(2x);//sx=A*x+B,sy=C*y+DglVertex2d(A*x+B,C*func+D);glVertex2d(A*x+B,C*func1+D);}
22例子2.2.3用点集绘制函数222.3OpenGL中的直线绘制绘制线段glBegin(GL_LINES);//useconstantGL_LINEShereglVertex2i(40,100);glVertex2i(202,96);glEnd();或者编写一个函数voiddrawLineInt(GLintx1,GLinty1,GLintx2,GLinty2){glBegin(GL_LINES);glVertex2i(x1,y1);glVertex2i(x2,y2);glEnd();}232.3OpenGL中的直线绘制绘制线段232.3.1绘制折线和多边形折线就是一系列头尾相连的线段的集合,也称为线带。glBegin(GL_LINE_STRIP);//drawanopenpolylineglVertex2i(20,10);glVertex2i(50,10);glVertex2i(20,80);glVertex2i(50,80);glEnd();glFlush();绘制多边形,只需要用GL_LINE_LOOP替换GL_LINE_STRIP即可,即是闭合的。f(x)=300-100cos(2px/100)+30cos(4px/100)+6cos(6px/100)<CalculateconstantsA,B,CandDforscalingandshifting>glBegin(GL_LINE_STRIP);for(x=0;x<=300;x+=3)glVertex2d(A*x+B,C*f(x)+D);glEnd();glFlush;242.3.1绘制折线和多边形24例子2.3.2绘制存储在文件中的折线
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc,文件dino.dat:21//文件中折线的数量4//第一条折线的点数169118//第一条折线的第一个点174120//1791241781265//第二条折线的点数29886//第二条折线的第一个点30492310104314114314119293243510439...等等
25例子2.3.2绘制存储在文件中的折线25例子2.3.3参数化图形
voidhardwiredHouse(void){glBegin(GL_LINE_LOOP);//绘制房子外形glVertex2i(40,40);glVertex2i(40,90);glVertex2i(70,120);glVertex2i(100,90);glVertex2i(100,40);glEnd();glBegin(GL_LINE_STRIP);//绘制烟囱glVertex2i(50,100);glVertex2i(50,120);glVertex2i(60,120);glVertex2i(60,110);glEnd();...//绘制门...//绘制窗户}voidparameterizedHouse(GLintPointpeak,GLintwidth,GLintheight)//房子的顶点由peak给定,房子的大小由width和height给定{glBegin(GL_LINE_LOOP);glVertex2i(peak.x,peak.y);//绘制房子外形glVertex2i(peak.x+width/2,peak.y-3*height/8);glVertex2i(peak.x+width/2peak.y-height);glVertex2i(peak.x-width/2,peak.y-height);glVertex2i(peak.x-width/2,peak.y-3*height/8);glEnd();drawchimneyinthesamefashiondrawthedoordrawthewindow}第一个方法将由几条折线组成的简易房子的每个端点位置被强制写入代码中,而第二种方法则是利用参数来绘制,用参数指定房屋顶点的位置和房子的宽度和高度,比较灵活。
26例子2.3.3参数化图形26例子2.3.4构造一个折线绘图器
一些应用程序需要用一个列表存储折线的顶点。这个列表可能是数组或者链表,可以采用类或者结构体保存数组,对于大量数据,c++STL(标准模板库)容器可能是一个安全的数据结构。structGLintPoint{GLintx,y;};classGLintPointArray{constintMAX_NUM=100;public:intnum;GLintPointpt[MAX_NUM];};voiddrawPolyLine(GlintPointArraypoly,intclosed){glBegin(closed?GL_LINE_LOOP:GL_LINE_STRIP);for(inti=0;i<poly.num;i++)glVertex2i(poly.pt[i].x,poly.pt[i].y);glEnd();glFlush();}
27例子2.3.4构造一个折线绘图器272.3.2使用moveTo()和lineTo()绘制线段
OpenGL绘制线段很容易。可以用一组命令GL_LINES,...,GL_END,也可以用drawLine()函数。moveTo和lineTo函数可以管理一个虚拟画笔,它的位置叫当前位置或CP(currentposition)。CP像一个OpenGL的状态变量,如画画的颜色和背景一样,它在程序的控制之下。moveTo(x,y)//设定CP到(x,y)lineTo(x,y)//在CP和(x,y)之间画一条线,更新CP。使用下面的代码,容易绘制基于点的列表(x0,y0),(x1,y1),...,(xn-1,yn-1)的折线。moveto(x[0],y[0]);for(inti=1;i<n;i++)lineto(x[i],y[i]);可以直接在OpenGL建立moveTo和lineTo这两个函数,并保持CP
282.3.2使用moveTo()和lineTo()绘制线段moveTo和lineTo函数实现为:GLintPointCP;//全局位置变量//<<<<<<<<<<<<<moveto>>>>>>>>>>>>>>voidmoveto(GLintx,GLinty){CP.x=x;CP.y=y;//更新CP}//<<<<<<<<<<<<lineTo>>>>>>>>>>>>>>>>>voidlineto(GLintx,GLinty){glBegin(GL_LINES);//绘制曲线glVertex2i(CP.x,CP.y);glVertex2i(x,y);glEnd();glFlush();CP.x=x;CP.y=y;//更新CP}
29moveTo和lineTo函数实现为:292.3.3绘制边校正的矩形多边形的特例是边校正的矩形,因为它的边与坐标轴平行,OpenGL提供了一个现成的函数glRecti()。glRecti(GLintx1,GLinty1,GLintx2,GLinty2);//以对角点(x1,y1)and(x2,y2)绘制矩形该函数基于两个给定点绘制边校正矩形,并以当前颜色填充矩形。glClearColor(1.0,1.0,1.0,0.0);//白色背景glClear(GL_COLOR_BUFFER_BIT);//清除glColor3f(0.6,0.6,0.6);//浅灰glRecti(20,20,100,70);glColor3f(0.2,0.2,0.2);//深灰glRecti(70,50,150,130);glFlush();a)随机生成的矩形雪花b)不同灰度等级的棋盘voiddrawFlurry(intnum,intnumColors,intWidth,intHeight)//drawnumrandomrectanglesinaWidthbyHeightrectangle{for(inti=0;i<num;i++){GLintx1=random(Width);//placecornerrandomlyGLinty1=random(Height);GLintx2=random(Width);//pickthesizesoitfitsGLinty2=random(Height);GLfloatlev=random(10)/10.0;//randomvalue,inrange0to1glColor3f(lev,lev,lev);//setthegraylevelglRecti(x1,y1,x2,y2);//drawtherectangle}glFlush();}302.3.3绘制边校正的矩形30练习练习2.3.1画棋盘写出例程checkerboard(intsize),这64个正方形的边长都设为size个像素。为这些正方形选择两种好看的颜色。解:第ij个正方形的左下角位于(i*size,j*size)处(i=0,..,7;j=0..7).使用下面的代码,可以将颜色在(r1,g1,b1)和(r2,g2,b2)间变换。if((i+j)%2==0)//如果i+j是偶数glColor3f(r1,g1,b1);elseglColor3f(r2,g2,b2);练习练习2.3.1画棋盘写出例程checkerboard(intsize),这64个正方形的边长都设为size个像素。为这些正方形选择两种好看的颜色。解:第ij个正方形的左下角位于(i*size,j*size)处(i=0,..,7;j=0..7).使用下面的代码,可以将颜色在(r1,g1,b1)和(r2,g2,b2)间变换。if((i+j)%2==0)//如果i+j是偶数glColor3f(r1,g1,b1);elseglColor3f(r2,g2,b2);31练习练习312.3.5填充多边形OpenGL支持用图案或色彩填充一般的多边形,那么这个多边形是凸的。
凸多边形:如果多边形中任意两点的连线完全位于多边形内部,那么这个多边形是凸的。要绘制凸多边形,使用顶点列表,把顶点列表放在glBegin(GL_POLYGON)和glEnd()之间。可以用点画线图案填充多边形,会把图像贴在多边形上,作为应用纹理的。C,E,F是凸的多边形。322.3.5填充多边形322.3.6OpenGL中的其他图形元素除了点、线和多边形,OpenGL还支持绘制五种其他元素332.3.6OpenGL中的其他图形元素33//使用鼠标指定一个矩形{staticGLintPointcorner[2];staticintnumCorners=0;//initialvalueis0if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN){corner[numCorners].x=x;corner[numCorners].y=screenHeight-y;//flipycoordinatenumCorners++;//haveanotherpointif(numCorners==2){glRecti(corner[0].x,corner[0].y,corner[1].x,corner[1].y);numCorners=0;//backto0corners}}elseif(button==GLUT_RIGHT_BUTTON&&state==GLUT_DOWN)glClear(GL_COLOR_BUFFER_BIT);//clearthewindowglFlush();}34//使用鼠标指定一个矩形342.4与鼠标和键盘的交互当用户按下或释放鼠标按钮、移动鼠标或者松开或按下键盘时,就会产生一个相关事件。程序员可以用每类事件注册一个回调函数
glutMouseFunc(myMouse):利用按下或者鼠标按钮时发生事件来注册myMouse()·glutMotionFunc(myMovedMouse)利用鼠标移动注册函数myMovedMouse()
glutKeyboardFunc(myKeyboard)
利用按下或者松开键盘按键时注册函数myKeyBoard()2.4.1用鼠标交互注册一个函数到glutMouseFunc(myMouse),myMouse()的名字可以任意,有四个参数:voidmyMouse(intbutton,intstate,intx,inty);button的值如下:GLUT_LEFT_BUTTON,GLUT_MIDDLE_BUTTON,或GLUT_RIGHT_BUTTON;而state的值可能为:GLUT_UP或GLUT_DOWN。x和y为事件发生时鼠标的位置。352.4与鼠标和键盘的交互352.4.1用鼠标绘制点当用户每次按下鼠标时,就会在屏幕窗口上鼠标所在的位置绘制出一个点。由于鼠标位置的y值是距离屏幕顶端的像素值,故不在(x,y),而是在(x,screenHeight–y)上绘制,其中screenHeight为屏幕的高度。voidmyMouse(intbutton,intstate,intx,inty){if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN)drawDot(x,screenHeight-y);elseif(button==GLUT_RIGHT_BUTTON&&state==GLUT_DOWN)exit(-1);}2.4.2用鼠标指定一个矩形想让用户画尺寸大小由鼠标输入的长方形。用户在两个点上单击鼠标,指定边校正矩形的两个角点,就可以画出矩形。用户右键点击鼠标即可清屏。362.4.1用鼠标绘制点36//使用鼠标指定一个矩形{staticGLintPointcorner[2];staticintnumCorners=0;//initialvalueis0if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN){corner[numCorners].x=x;corner[numCorners].y=screenHeight-y;//flipycoordinatenumCorners++;//haveanotherpointif(numCorners==2){glRecti(corner[0].x,corner[0].y,corner[1].x,corner[1].y);numCorners=0;//backto0corners}}elseif(button==GLUT_RIGHT_BUTTON&&state==GLUT_DOWN)glClear(GL_COLOR_BUFFER_BIT);//clearthewindowglFlush();}37//使用鼠标指定一个矩形372.4.3用鼠标控制Sierpinski垫片使用户用鼠标指定初始三角形的三个顶点,并置于数组corners[]中,代码为staticGLintPointcorners[3];staticintnumCorners=0;if(button==GLUT_LEFT_BUTTON&&state==GLUT_DOWN){corner[numCorners].x=x;corner[numCorners].y=screenHeight-y;//flipycoordinateif(++numCorners==3){Sierpinski(corners);//drawthegasketnumCorners=0;//backto0corners}}382.4.3用鼠标控制Sierpinski垫片38鼠标移动鼠标移动会产生一个鼠标事件:glutMotionFunc(myMovedMouse);注册函数myMovedMouse(intx,inty);有两个参数,即事件发生时鼠标所在的位置。glutPassiveMotionFunc(myPassiveMotion);这个函数是没有按下鼠标按钮的情况下,鼠标在窗口内移动时调用。该函数可以绘制并显示橡皮矩形:随着用户移动鼠标,矩形将相应地变大或变小。下面的程序是用户单击鼠标建立矩形的一个角点,然后不按下鼠标时移动鼠标,调用myPassiveMotion(intx,inty),这决定了矩形的第二个角点。glutPostRedisplay()调用myDisplay()函数。代码见见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc39鼠标移动392.4.2键盘交互按下键盘上的某个按键时就会产生一个键盘事件,并放入消息队列。回调函数myKeyboard()通过glutKeyboardFunc(myKeyboard)来注册这种事件,该函数的原型为:voidmyKeyboard(unsignedintkey,intx,inty);key值就是键的ASCII值,x和y为鼠标所在的位置。实例代码为:voidmyKeyboard(unsignedchartheKey,intmouseX,intmouseY){GLintx=mouseX;GLinty=screenHeight-mouseY;//反转y的值switch(theKey){case‘p’:drawDot(x,y);//drawadotatthemousepositionbreak;caseGLUT_KEY_LEFT:List[++last].x=x;//addapointList[last].y=y;break;case‘E’:exit(-1);//terminatetheprogramdefault:break;//donothing}}402.4.2键盘交互402.5程序中的菜单设计与使用如果一个图形程序能够提供菜单功能,对用户来说,使用就比较方便了。建议使用vc。
设计代码和使用GLUT菜单创建菜单使用voidglutCreateMenu(),菜单项使用一个带参数的函数glutCreateMenu(ProcessMenuEvents)来定义,必须编写ProcessMenuEvents函数。使用函数glutAddMenuEntry()可以在菜单中添加选项,一旦菜单设计好,可以用glutAttachMenu(GLUT_RIGHT_BUTTON)建立与鼠标按钮的联系。代码见书P69.412.5程序中的菜单设计与使用如果一个图形程序能够提供菜单功案例分析
为了巩固所学知识,最好的方式就是使用学过的知识,在最初的学习中更是如此,因为一开始学习计算机图形学,不了解程序的编写是必须克服的障碍。为了强调这一点,每章都有案例,其中介绍的编程项目不仅本身有趣,而且浓缩了该章中所提及的知识。难易程度:I:简单的练习,课堂即可完成。.II:教难任务,可能需要几天才能完成。III:复杂任务,可能需要两周完成。案例2.1伪随机点云(难度II)rand()函数不是随机产生的,而是通过一个很有规律的机制产生的:每个数ni都是由它前一个数ni-1,通过一个等式确定:ni=(ni-1*A+B)modN
其中A,B,和N是适当选择的常数。一组有效的值为:A=1103515245,B=12345,和N=32767.这个数限制在0到N-1.散乱点图,调用rand(N),它会返回一个0,...,N-1之间的数。glBegin(GL_POINTS);for(inti=0;i<num;i++)//doitnumtimesglVertex2i(random(N),random(N));glEnd();选择不同的A,B,N的值生成(x,y)对。42案例分析42案例分析
案例2.2迭代函数系统介绍(难度II)绘制Sierpinski垫片时的操作就是迭代函数(IFS)的一个例子。许多计算机生成的有趣图形(如分形、Mandelbrot集合等)都是基于迭代函数变化生成的。如:•f(.)=2(.);倍增器,倍增它的参数;•f(.)=cos(.);•f(.)=4(.)(1-(.))•f(.)=(.)2+cIFS产生的数字序列:d0d1=f(d0)d2=f(f(d0))d3=f(f(f(d0)))dk=f[k](d0)dk=f(d(k-1))对于给定的d0,k=1,2,3,...,.项目1:预报冰雹系列写一个程序,绘制上面公式对k所产生的数值序列。用户给出1到4,000,000,000之间的初始值y0。每个yk值被画成点图(k,yk)。43案例分析43案例2.2迭代函数项目2:姜饼人这是混沌理论中的常见图形。glLoadIdentity(); glTranslatef(-30,-30,-90); glScaled(0.15,0.15,0.15); floatx,y; staticfloatold_x=115,old_y=121; staticfloatR=40,S=3; glBegin(GL_POINTS); for(inti=0;i<1000;i++) { x=R*(1+2*S)-old_y+abs(int(old_x-S*R));//TheIFSforx y=old_x; old_x=x; old_y=y; glVertex2f(x,y);//Outputthatmagicvertex } glEnd(); glFlush();这里R=M=40,S=L=3,p_x=old_x,p_y=old_y,q_x=x,q_y=y;44案例2.2迭代函数44案例2.3黄金分割(难度I)部分代码:1)phi0=1.0;for(i=0;i<5000;i++){phi=sqrt(1+sqrt(phi0));phi0=phi;}2)phi0=1.0;for(i=0;i<5000;i++){phi=1.0+1.0/(1.0+phi0));phi0=phi;}45案例2.3黄金分割45案例2.4如何创建和使用折线段文件(难度I)复杂图形恐龙dino.dat由一个很大的折线集合构成。折线数据一般都是存在一个文件里。(1)编写一个程序,可以从文件中读取折线数据并依次绘制每条折线。编写一个折线数据,将文件中的数据用你编写的程序绘制出来。设计你的程序,确保画折线时不需要每次都读取折线数据文件,可以用数组或链表存储折线数据;(2)扩展上面的程序,使其能够接受其他文件格式。例如程序可以接受差分编码的x和y坐标。(3)对前面的文件作修改,使文件中每条折线都和一个颜色值相关,使颜色值与折线包含点数的数值在一行上。(4)修改折线例程,使它在折线的点数为负数时绘制一个封闭多边形。46案例2.4如何创建和使用折线段文件46案例2.5迷宫(难度III)绘制一个100行和150列的长方形迷宫,目标是从左边界的开口穿过迷宫到达右边界的开口。(1)生成并显示一个具有R行和C列的矩形迷宫;(2)找到并显示一条从迷宫起点通向终点的路径。怎样表示一个迷宫呢?一种方法是,对每个单元格都做出声明,无论其北墙完整还是其东墙完整,建议使用下面的数据结构:charnorthWall[R][C],eastWall[R][C];见书P7947案例2.5迷宫474848本章内容编写图形程序学习OpenGL程序的基本组成开发绘制点、直线、折线和多边形的基本图形工具学会用鼠标和键盘控制程序49本章内容编写图形程序12.0vcopengl编程
指导例子熟悉代码,多次练习见图形学第二课opengl-console.doc,创建一个控制台的应用程序,本书采用的编程方式见图形学第二课opengl-nehe-win.doc,创建一个win32的应用程序,nehe教程(共48课)采用的编程方式见图形学第三课opengl-nehe-mfc.doc,创建一个mfc的应用程序,本人推荐采用的编程方式,需要学习vc和mfc。502.0vcopengl编程
指导例子熟2.1生成图像初步通过练习(编写和测试各种不同图形的程序)可以很快掌握计算机图形学。先从简单的任务开始,一旦掌握,就试着变变花样,看看有哪些变化,试着进一步绘制更复杂的场景。最好的学习办法是认真、仔细地学习每行代码或等式。当你这么做时,就能理解每个新概念。每个图形程序都以一些初始化工作为开始,由此建立程序所需要的显示模型和坐标系。512.1生成图像初步通过练习(编写和测试各种不同图形的程序2.1生成图像初步初始化时,将显示器设置为“图形模式”,并且建立了坐标系,坐标x和y以像素为单位,x向右递增,y向下递增。522.1生成图像初步初始化时,将显示器设置为“图形模式”,2.1.1设备无关的编程
和OpenGLOpenGL程序可以在不同的图形环境里编译和运行,即它是与设备无关的编程。OpenGL是一个开源图形库,可在下载。OpenGL常被称为应用程序接口(API)。这个接口是程序员可以调用的例程,先从OpenGL绘制简单的二维物体开始,然后学习绘制三维图形。532.1.1设备无关的编程
和OpenGLO2.1.2窗口的编程事件驱动编程:事件包括单击鼠标,按下键盘上的按键,或者重新调整窗口的大小。程序员将程序组织成回调函数的集合,这些回调函数一有事件发生就执行。即什么也不做,等待事件发生,事件发生后再做指定的事。OpenGL有一个GLUT库,它用来打开窗口,管理菜单和处理事件等。注册回调函数:有一种方法将每种类型的事件与要求的回调函数关联起来,这个方法称之为注册回调函数。程序中用到的每一个事件类型都必须用回调函数注册,该回调函数的名字和定义由程序员选择。542.1.2窗口的编程事件驱动编程:事件包括单击鼠标,按下2.1.2窗口的编程下面是使用GLUT库,名为myMouse的回调函数例子,它方便地注册了与鼠标关联的事件:glutMouseFunc(myMouse);glutMouseFunc是GLUT库的固有函数,但是回调函数myMouse是程序员定义的,并由程序员编写代码,处理每个可能感兴趣的鼠标动作。4种主要的OpenGL库:1)基本GL库,OpenGL库的基础。它提供OpenGL的基本函数。每个OpenGL函数都以字符GL开头。552.1.2窗口的编程下面是使用GLUT库,名为myMou2.1.2窗口的编程2)GLUT库:GL实用工具包。它用来打开窗口,开发和管理菜单,以及管理事件等。3)GLU库:GL实用库,它提供高级例程,处理矩阵操作和绘制二次曲面如球和圆柱体;4)GLUI库:用户接口库,提供控制工具和菜单。glutDisplayFunc(myDisplay):重绘窗口调用myDisplay回调函数;glutReshapeFun(myReshape):对屏幕窗口的形状进行调整;562.1.2窗口的编程2)GLUT库:GL实用工具包。它用voidmain(){glutDisplayFunc(myDisplay);//注册重绘函数glutReshapeFunc(myReshape);//注册改变窗口形状函数glutMouseFunc(myMouse);//注册鼠标动作函数glutMotionFunc(myMouse);//注册鼠标移动函数glutKeyboardFunc(myKeyboard);//注册键盘动作函数//可能初始化其他工作glutMainLoop();//进入主循环等待事件发生}57voidmain()92.1.3如何打开一个窗口画图glutInit:该函数初始化工具包glutDisplayMode(GLUT_SINGLE|GLUT_RGB):分配单个显示缓存glutInitWindowSize(640,480):该函数指定屏幕的初始尺寸,宽640像素,高480像素。glutCreateWindow(“myfirstattempt”):该函数打开并显示屏幕窗口吗,并显示标题。代码如下:582.1.3如何打开一个窗口画图glutInit:该函数voidmain(intargc,char**argv){glutInit(&argc,argv);//初始化工具包glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);//设置显示模式glutInitWindowSize(640,480);//设置窗口大小glutInitWindowPosition(100,150);//设置窗口在屏幕上的位置glutCreateWindow("myfirstattempt");//打开屏幕窗口59voidmain(intargc,char**arg//注册回调函数glutDisplayFunc(myDisplay);glutReshapeFunc(myReshape);glutMouseFunc(myMouse);glutKeyboardFunc(myKeyboard);myInit();//必要的其他初始化工作glutMainLoop();//进入循环}60//注册回调函数122.2OpenGL的基本图形元素屏幕坐标系:宽为640,高为640。x坐标从左边界0增加到右边界639,y坐标从0增加到上边的479。612.2OpenGL的基本图形元素屏幕坐标系:宽为640,高绘制三个点:glBegin(GL_POINTS);glVertex2i(100,50);glVertex2i(100,130);glVertex2i(150,130);glEnd();或者用浮点值代替整数值:glBegin(GL_POINTS);glVertex2d(100.0,50.0);glVertex2d(100.0,130.0);glVertex2d(150.0,130.0);glEnd();62绘制三个点:14OpenGL数据类型:命令后缀和参数数据类型OpenGL状态:OpenGL是由许多状态变量组成的状态机,这些状态包括点的大小、绘图的颜色额屏幕窗口大小等,点的大小可以用glPointSize()来设置,画图的颜色用这条语句设置:glColor3f(red,green,blue);坐标系的建立:6315voidmyInit(void){glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0,640.0,0,480.0);}myInit设置坐标系,设置透视投影变换。gluOrtho2D设置正交投影。组合在一起:建立起一个完整的OpenGL程序glFlush()函数保证所有的数据被完全处理并显示。64voidmyInit(void)162.2.1几个点丛绘制的例子点丛是由大量点组成的某种图案。下面有几个点丛例子。652.2.1几个点丛绘制的例子点丛是由大量点组成的某种图案例子2.2.1北斗星群
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc例子2.2.2Sierpinski(塞平斯基)垫片见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc算法如下:第k个点表示为P(k)=(x(k),y(k)),每一个点都依赖于前一个点P(k-1),过程如下:(1)选择三个固定的点T0,T1,T2,构成一个三角形,称为每个Sierpinski垫片的父三角形;(2)随机选择父三角形顶点T0,T1,T2中的一点作为要绘制的初始点P0。迭代下面的(3)~(5)步,直到图案填充完毕。(3)随机选择T0,T1,T2中的一点,称为T。(4)构造下一个点P(k),作为T和前一个已建好的点P(k-1)之间的之间点,即P(k)=(P(k-1)+T)/2(5)用drawDot()绘制P(k)。66例子2.2.1北斗星群18例子2.2.2Sierpinski(塞平斯基)垫片//初始值P(0),在for循环外面GLintPointpoint=T[index];//初始值drawDot(point.x,point.y);//画初始点for(inti=0;i<1000;i++)//画1000点{index=rand()%3;//P(k)=(P(k-1)+T)/2point.x=(point.x+T[index].x)/2;point.y=(point.y+T[index].y)/2;drawDot(point.x,point.y);}67例子2.2.2Sierpinski(塞平斯基)垫片19例子2.2.3用点集绘制函数
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc绘制某个数学函数f(x),当x在0~4变化时的曲线。当绘制曲线时,因为0~4之间的x值位于屏幕窗口左下角的前4个像素上,而且f(.)的负值位于窗口的下面,根本看不到。需要进行平移和缩放使曲线正好出现在合适的屏幕窗口。将世界坐标系转换为窗口坐标系。x方向缩放:假定屏幕窗口为screenWidth,单位为像素,现在x的范围为0~4,故sx=x*screenWidth/4.0;y方向缩放:假定屏幕窗口为screenHeight,单位为像素,f(x)的取值在–1.0和1.0之间,缩放screenHeight/2,平移为screenHeight/2,sy=(y+1.0)*screenHeight/2.0;sx,sy为屏幕坐标,x,y为窗口坐标;转换方程为:sx=A*x+Bsy=C*y+D68例子2.2.3用点集绘制函数20constintscreenWidth=640;//widthofscreenwindowinpixelsconstintscreenHeight=480;//heightofscreenwindowinpixelsGLdoubleA,B,C,D;//valuesusedforscalingandshiftingvoidmyInit(void){glClearColor(1.0,1.0,1.0,0.0);//backgroundcoloriswhiteglColor3f(0.0f,0.0f,0.0f);//drawingcolorisblackglPointSize(2.0);//a'dot'is2by2pixelsglMatrixMode(GL_PROJECTION);//set"camerashape"glLoadIdentity();gluOrtho2D(0.0,(GLdouble)screenWidth,0.0,(GLdouble)screenHeight);A=screenWidth/4.0;//x方向的缩放量AB=0.0;//x方向的平移量BC=D=screenHeight/2.0;//y方向的缩放量C和平移量D}voidmyDisplay(void){glClear(GL_COLOR_BUFFER_BIT);//clearthescreenglBegin(GL_POINTS);for(GLdoublex=0;x<4.0;x+=0.005){Gldoublefunc=exp(-x)*cos(2*3.14159265*x);//sx=A*x+B,sy=C*y+DglVertex2d(A*x+B,C*func+D);}glEnd();glFlush();//sendalloutputtodisplay}69constintscreenWidth=640;/例子2.2.3用点集绘制函数
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.docf(x)=sin(x),f(x)=sin(2x),x从-π到π,y从-1到1A=screenWidth/6.28;//x方向的缩放量A,屏幕宽度/x方向的变化范围B=screenWidth/2.0;//x方向的平移量B,屏幕宽度的一半C=screenHeight/2.0;//y方向的缩放量C,屏幕高度/y方向的变化范围
D=screenHeight/2.0;//y方向的平移量D,屏幕的高度的一半for(GLdoublex=-3.14;x<3.14;x+=0.005){Gldoublefunc=sin(x);Gldoublefunc1=sin(2x);//sx=A*x+B,sy=C*y+DglVertex2d(A*x+B,C*func+D);glVertex2d(A*x+B,C*func1+D);}
70例子2.2.3用点集绘制函数222.3OpenGL中的直线绘制绘制线段glBegin(GL_LINES);//useconstantGL_LINEShereglVertex2i(40,100);glVertex2i(202,96);glEnd();或者编写一个函数voiddrawLineInt(GLintx1,GLinty1,GLintx2,GLinty2){glBegin(GL_LINES);glVertex2i(x1,y1);glVertex2i(x2,y2);glEnd();}712.3OpenGL中的直线绘制绘制线段232.3.1绘制折线和多边形折线就是一系列头尾相连的线段的集合,也称为线带。glBegin(GL_LINE_STRIP);//drawanopenpolylineglVertex2i(20,10);glVertex2i(50,10);glVertex2i(20,80);glVertex2i(50,80);glEnd();glFlush();绘制多边形,只需要用GL_LINE_LOOP替换GL_LINE_STRIP即可,即是闭合的。f(x)=300-100cos(2px/100)+30cos(4px/100)+6cos(6px/100)<CalculateconstantsA,B,CandDforscalingandshifting>glBegin(GL_LINE_STRIP);for(x=0;x<=300;x+=3)glVertex2d(A*x+B,C*f(x)+D);glEnd();glFlush;722.3.1绘制折线和多边形24例子2.3.2绘制存储在文件中的折线
见计算机图形学课件2016\计算机图形学(Opengl版第三版)书源代码word版\2.2.1-threeDots.doc,文件dino.dat:21//文件中折线的数量4//第一条折线的点数169118//第一条折线的第一个点174120//1791241781265//第二条折线的点数29886//第二条折线的第一个点30492310104314114314119293243510439...等等
73例子2.3.2绘制存储在文件中的折线25例子2.3.3参数化图形
voidhardwiredHouse(void){glBegin(GL_LINE_LOOP);//绘制房子外形glVertex2i(40,40);glVertex2i(40,90);glVertex2i(70,120);glVertex2i(100,90);glVertex2i(100,40);glEnd();glBegin(GL_LINE_STRIP);//绘制烟囱glVertex2i(50,100);glVertex2i(50,120);glVertex2i(60,120);glVertex2i(60,110);glEnd();...//绘制门...//绘制窗户}voidparameterizedHouse(GLintPointpeak,GLintwidth,GLintheight)//房子的顶点由peak给定,房子的大小由width和height给定{glBegin(GL_LINE_LOOP);glVertex2i(peak.x,peak.y);//绘制房子外形glVertex2i(peak.x+width/2,peak.y-3*height/8);glVertex2i(peak.x+width/2peak.y-height);glVertex2i(peak.x-width/2,peak.y-height);glVertex2i(peak.x-width/2,peak.y-3*height/8);glEnd();drawchimneyinthesamefashiondrawthedoordrawthewindow}第一个方法将由几条折线组成的简易房子的每个端点位置被强制写入代码中,而第二种方法则是利用参数来绘制,用参数指定房屋顶点的位置和房子的宽度和高度,比较灵活。
74例子2.3.3参数化图形26例子2.3.4构造一个折线绘图器
一些应用程序需要用一个列表存储折线的顶点。这个列表可能是数组或者链表,可以采用类或者结构体保存数组,对于大量数据,c++STL(标准模板库)容器可能是一个安全的数据结构。structGLintPoint{GLintx,y;};classGLintPointArray{constintMAX_NUM=100;public:intnum;GLintPointpt[MAX_NUM];};voiddrawPolyLine(GlintPointArraypoly,intclosed){glBegin(closed?GL_LINE_LOOP:GL_LINE_STRIP);for(inti=0;i<poly.num;i++)glVertex2i(poly.pt[i].x,poly.pt[i].y);glEnd();glFlush();}
75例子2.3.4构造一个折线绘图器272.3.2使用moveTo()和lineTo()绘制线段
OpenGL绘制线段很容易。可以用一组命令GL_LINES,...,GL_END,也可以用drawLine()函数。moveTo和lineTo函数可以管理一个虚拟画笔,它的位置叫当前位置或CP(currentposition)。CP像一个OpenGL的状态变量,如画画的颜色和背景一样,它在程序的控制之下。moveTo(x,y)//设定CP到(x,y)lineTo(x,y)//在CP和(x,y)之间画一条线,更新CP。使用下面的代码,容易绘制基于点的列表(x0,y0),(x1,y1),...,(xn-1,yn-1)的折线。moveto(x[0],y[0]);for(inti=1;i<n;i++)lineto(x[i],y[i]);可以直接在OpenGL建立moveTo和lineTo这两个函数,并保持CP
762.3.2使用moveTo()和lineTo()绘制线段moveTo和lineTo函数实现为:GLintPointCP;//全局位置变量//<<<<<<<<<<<<<moveto>>>>>>>>>>>>>>voidmoveto(GLintx,GLinty){CP.x=x;CP.y=y;//更新CP}//<<<<<<<<<<<<lineTo>>>>>>>>>>>>>>>>>voidlineto(GLintx,GLinty){glBegin(GL_LINES);//绘制曲线glVertex2i(CP.x,CP.y);glVertex2i(x,y);glEnd();glFlush();CP.x=x;CP.y=y;//更新CP}
77moveTo和lineTo函数实现为:292.3.3绘制边校正的矩形多边形的特例是边校正的矩形,因为它的边与坐标轴平行,OpenGL提
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 高血压性脑出血重症管理共识2026
- 图表美化与排版技巧
- 高中社会热点跨学科说课稿
- 证券从业资格考试冲刺资料
- 左心衰患者运动康复指导
- 护理质量评估与反馈机制建立
- 2026年景观台阶说课稿
- 4.1 《望海潮》课件 2025-2026学年统编版高二语文选择性必修下册
- 17 太空生活趣事多 课件(内嵌视频)2025-2026学年统编版二年级下册语文
- 初中安全自救“防地震”主题班会说课稿2025
- 互联网营销师培训知识课件
- 撤回执行申请书
- 磁流体密封技术
- 公路中分带开口施工方案
- 《人工智能通识课》全套教学课件
- 档案数字化管理师理论知识考核试卷及答案
- 蒙汉文并用管理办法
- 工程混凝土知识培训课件
- 《焊条电弧焊》课件-项目二 焊条电弧焊基本技能
- 苏教版一年级数学下册期末《图形与几何》专项试卷附答案
- 供应室带教老师课件
评论
0/150
提交评论