OpenGL实验教程 v30重点讲义资料Word格式.docx
《OpenGL实验教程 v30重点讲义资料Word格式.docx》由会员分享,可在线阅读,更多相关《OpenGL实验教程 v30重点讲义资料Word格式.docx(171页珍藏版)》请在冰豆网上搜索。
首先从开始->
所有程序->
MicrosoftVisualC++6.0菜单中打开VC,也可单击文件:
\ProgramFiles\MicrosoftVisualStudio\VisualC++6\Common\MSDev98\Bin\msdev.exe打开VC,在VC中选择File->
New->
Project,然后选择Win32ConsoleApplication,输入一个工程名,设为Pixel,然后按OK。
在弹出的对话框左边点ApplicationSettings,选择Aemptyproject并勾上,选择Finish。
在VC中选择File->
Files页,然后选择C++SourceFile,勾选“Addtoproject”,并在下面输入一个文件名,如为test1,然后按OK。
随后将实验示范代码拷入该文件即可.
实验1像素点的生成
1.实验目的:
熟悉编程环境;
了解光栅图形显示器的特点;
了解计算机绘图的特点;
利用VC+OpenGL作为开发平台设计程序,以能够在屏幕上生成任意一个像素点为本实验的结束。
2.实验内容:
(1)
了解和使用VC的开发环境,理解简单的OpenGL程序结构;
(2)
掌握OpenGL提供的基本图形函数,尤其是生成点的函数。
3.实验原理:
(1)基本语法
常用的程序设计语言,如C、C++、Pascal、Fortran和Java等,都支持OpenGL的开发。
这里只讨论C版本下OpenGL的语法。
OpenGL基本函数均使用gl作为函数名的前缀,如glClearColor();
实用函数则使用glu作为函数名的前缀,如gluSphere()。
OpenGL基本常量的名字以GL_开头,如GL_LINE_LOOP;
实用常量的名字以GLU_开头,如GLU_FILL。
一些函数如glColor*()(定义颜色值),函数名后可以接不同的后缀以支持不同的数据类型和格式。
如glColor3b(...)、glColor3d(...)、glColor3f(...)和glColor3bv(...)等,这几个函数在功能上是相似的,只是适用于不同的数据类型和格式,其中3表示该函数带有三个参数,b、d、f分别表示参数的类型是字节型、双精度浮点型和单精度浮点型,v则表示这些参数是以向量形式出现的。
OpenGL定义了一些特殊标识符,如GLfloat,GLvoid。
它们其实就是C中的float和void。
在gl.h文件中可以看到以下定义:
……
typedeffloatGLfloat;
typedefvoidGLvoid;
一些基本的数据类型都有类似的定义项。
(2)程序的基本结构
OpenGL程序的基本结构可分为三个部分:
第一部分是初始化部分。
主要是设置一些OpenGL的状态开关,如颜色模式(RGBA或ALPHA)的选择,是否作光照处理(若有的话,还需设置光源的特性),深度检验,裁剪等等。
这些状态一般都用函数glEnable(...),glDisable(…)来设置,…表示特定的状态。
第二部分设置观察坐标系下的取景模式和取景框位置大小。
主要利用了三个函数:
函数voidglViewport(left,top,right,bottom):
设置在屏幕上的窗口大小,四个参数描述屏幕窗口四个角上的坐标(以象素表示);
函数voidglOrtho(left,right,bottom,top,near,far):
设置投影方式为正交投影(平行投影),其取景体积是一个各面均为矩形的六面体;
函数voidgluPerspective(fovy,aspect,zNear,zFar):
设置投影方式为透视投影,其取景体积是一个截头锥体。
第三部分是OpenGL的主要部分,使用OpenGL的库函数构造几何物体对象的数学描述,包括点线面的位置和拓扑关系、几何变换、光照处理等等。
以上三个部分是OpenGL程序的基本框架,即使移植到使用MFC的Windows程序中,也是如此。
只是由于Windows自身有一套显示方式,需要进行一些必要的改动以协调这两种不同显示方式。
(3)状态机制
OpenGL的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。
例如,当前颜色就是一个状态变量,在这个状态改变之前,绘制的每个象素都将使用该颜色,直到当前颜色被设置为其它颜色为止。
OpenGL中大量地使用了这种状态机制,如颜色模式、投影模式、单双显示缓存区的设置、背景色的设置、光源的位置和特性等等。
许多状态变量可以通过glEnable()、glDisable()这两个函数来设置成有效或无效状态,如是否设置光照、是否进行深度检测等;
在被设置成有效状态之后,绝大部分状态变量都有一个缺省值。
通常情况下,可以用下列四个函数来获取某个状态变量的值:
glGetBooleanv()、glGetDouble()、glGetFloatv()和glGetIntegerv()。
究竟选择哪个函数应该根据所要获得的返回值的数据类型来决定。
还有些状态变量有特殊的查询函数,如glGetLight*()、glGetError()和glPolygonStipple()等。
另外,使用glPushAttrib()和glPopAttrib()函数,可以存储和恢复最近的状态变量的值。
只要有可能,都应该使用这些函数,因为它们比其它查询函数的效率更高。
4.实验代码:
一个简单的OpenGL程序如下:
(注意,如果需要编译并运行,需要正确安装GLUT,安装方法如预备知识中所述)
#include
<
GL/glut.h>
void
myDisplay(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f,1.0f,1.0f);
glRectf(-0.5f,-0.5f,0.5f,0.5f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(0.0f,1.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.8f,-0.5f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(-0.8f,-0.5f);
glEnd();
glPointSize(3);
glBegin(GL_POINTS);
glVertex2f(-0.4f,-0.4f);
glVertex2f(0.0f,0.0f);
glVertex2f(0.4f,0.4f);
glFlush();
}
int
main(int
argc,
char
*argv[])
glutInit(&
argv);
glutInitDisplayMode(GLUT_RGB
|
GLUT_SINGLE);
glutInitWindowPosition(100,
100);
glutInitWindowSize(400,
400);
glutCreateWindow("
HelloOpengl!
"
);
glutDisplayFunc(myDisplay);
glutMainLoop();
return
0;
该程序的作用是在一个黑色的窗口中央画一个矩形、三角形和三个点。
下面对各行语句进行说明:
首先,需要包含头文件#include
,这是GLUT的头文件。
本来OpenGL程序一般还要包含<
GL/gl.h>
和<
GL/glu.h>
,但GLUT的头文件中已经自动将这两个文件包含了,不必再次包含;
然后看main函数。
*argv[]),这个是带命令行参数的main函数。
注意main函数中的各语句,除了最后的return之外,其余全部以glut开头。
这种以glut开头的函数都是GLUT工具包所提供的函数,下面对用到的几个函数进行介绍;
1)glutInit,对GLUT进行初始化,这个函数必须在其它的GLUT使用之前调用一次。
其格式比较固定,一般都是glutInit(&
argv)就行;
2)glutInitDisplayMode,设置显示方式,其中GLUT_RGB表示使用RGB颜色,与之对应的还有GLUT_INDEX(表示使用索引颜色)。
GLUT_SINGLE表示使用单缓冲,与之对应的还有GLUT_DOUBLE(使用双缓冲)。
更多信息,以后的实验教程会有讲解介绍;
3)glutInitWindowPosition,设置窗口在屏幕中的位置;
4)glutInitWindowSize,设置窗口的大小;
5)glutCreateWindow,根据前述设置的信息创建窗口。
参数将被作为窗口的标题。
注意:
窗口被创建后,并不立即显示到屏幕上。
需要调用glutMainLoop才能看到窗口;
6)glutDisplayFunc,设置一个函数,当需要进行画图时,这个函数就会被调用。
(暂且这样理解);
7)glutMainLoop,进行一个消息循环。
(现在只需知道这个函数可以显示窗口,并且等待窗口关闭后才会返回。
)
在glutDisplayFunc函数中,我们设置了“当需要画图时,请调用myDisplay函数”。
于是myDisplay函数就用来画图。
观察myDisplay中的三个函数调用,发现它们都以gl开头。
这种以gl开头的函数都是OpenGL的标准函数,下面对用到的函数进行介绍:
1)glClearColor(0.0,0.0,0.0,0.0):
将清空颜色设为黑色(为什么会有四个参数?
);
2)glClear(GL_COLOR_BUFFER_BIT):
将窗口的背景设置为当前清空颜色;
3)glRectf,画一个矩形。
四个参数分别表示了位于对角线上的两个点的横、纵坐标;
4)glFlush,保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)。
5.思考题
左图是示范程序的结果,能否在原有结果基础上添加三条直线组成新的三角形?
(如右图所示)
请在实验报告的实验结果将改动结果写出.
示范程序结果
注:
本博客实验教程的配套教材为《计算机图形学》(徐文鹏编)已由机械工业出版社于2009年2月出版。
实验2
直线生成算法的实现
理解基本图形元素光栅化的基本原理,掌握一种基本图形元素光栅化算法,利用OpenGL实现直线光栅化的DDA算法。
根据所给的直线光栅化的示范源程序,在计算机上编译运行,输出正确结果;
指出示范程序采用的算法,以此为基础将其改造为中点线算法或Bresenham算法,写入实验报告;
(3)
根据示范代码,将其改造为圆的光栅化算法,写入实验报告;
(4)
了解和使用OpenGL的生成直线的命令,来验证程序运行结果。
示范代码原理参见教材直线光栅化一节中的DDA算法。
下面介绍下OpenGL画线的一些基础知识和glutReshapeFunc()函数。
(1)数学上的直线没有宽度,但OpenGL的直线则是有宽度的。
同时,OpenGL的直线必须是有限长度,而不是像数学概念那样是无限的。
可以认为,OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定。
这里的线由一系列顶点顺次连结而成,有闭合和不闭合两种。
前面的实验已经知道如何绘“点”,那么OpenGL是如何知道拿这些顶点来做什么呢?
是一个一个的画出来,还是连成线?
或者构成一个多边形?
或是做其它事情呢?
为了解决这一问题,OpenGL要求:
指定顶点的命令必须包含在glBegin函数之后,glEnd函数之前(否则指定的顶点将被忽略),并由glBegin来指明如何使用这些点。
例如:
glBegin(GL_POINTS);
glVertex2f(0.0f,
0.0f);
glVertex2f(0.5f,
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等,每种方式的大致效果见下图:
图B-2OpenGL几何图元类型
(声明:
该图片来自www.opengl.org,该图片是《OpenGL编程指南》一书的附图,由于该书的旧版(第一版,1994年)已经流传于网络,希望没有触及到版权问题。
(2)首次打开窗口、移动窗口和改变窗口大小时,窗口系统都将发送一个事件,以通知程序员。
如果使用的是GLUT,通知将自动完成,并调用向glutReshapeFunc()注册的函数。
该函数必须完成下列工作:
Ÿ
重新建立用作新渲染画布的矩形区域;
定义绘制物体时使用的坐标系。
如:
voidReshape(intw,inth)
glViewport(0,0,(GLsizei)w,
(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,(GLdouble)w,0.0,(GLdouble)h);
在GLUT内部,将给该函数传递两个参数:
窗口被移动或修改大小后的宽度和高度,单位为像素。
glViewport()调整像素矩形,用于绘制整个窗口。
接下来三个函数调整绘图坐标系,使左下角位置为(0,
0),右上角为(w,h)。
#include<
voidLineDDA(intx0,inty0,intx1,inty1/*,intcolor*/)
x,dy,dx,y;
floatm;
dx=x1-x0;
dy=y1-y0;
m=dy/dx;
y=y0;
glColor3f(1.0f,1.0f,0.0f);
glPointSize
(1);
for(x=x0;
x<
=x1;
x++)
glVertex2i(x,(int)(y+0.5));
y+=m;
}
voidmyDisplay(void)
glRectf(25.0,25.0,75.0,75.0);
glPointSize(5);
glVertex2f(0.0f,0.0f);
LineDDA(0,0,200,300);
glBegin(GL_LINES);
glVertex2f(100.0f,0.0f);
glVertex2f(180.0f,240.0f);
voidInit()
glShadeModel(GL_FLAT);
intmain(intargc,char*argv[])
argc,argv);
glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(400,400);
HelloWorld!
Init();
glutReshapeFunc(Reshape);
return0;
glShadeModel选择平坦或光滑渐变模式。
GL_SMOOTH为缺省值,为光滑渐变模式,GL_FLAT为平坦渐变模式。
扩展:
平滑直线绘制
math.h>
voiddisplay(){
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH,GL_NICEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glLineWidth
(1);
glBegin(GL_LINES);
for(inti=0;
i<
180;
i++){
glVertex2f(0,0);
glVertex2f(0.5*cos(2*i*3.14159/180),0.5*sin(2*i*3.14159/180));
}
glEnd();
glutSwapBuffers();
intmain(intargc,char*argv[])
glutInit(&
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);
glutInitWindowSize(1000,480);
glutInitWindowPosition(50,50);
glutCreateWindow("
ModelTransform"
glutDisplayFunc(display);
glClearColor(0,0,0,0);
glShadeModel(GL_SMOOTH);
glutMainLoop();
return0;
效果:
思考:
为什么圆不圆?
实验3线段裁剪
了解二维图形裁剪的原理(点的裁剪、直线的裁剪、多边形的裁剪),利用VC+OpenGL实现直线的裁剪算法。
理解直线裁剪的原理(Cohen-Surtherland算法)
利用VC+OpenGL实现直线的编码裁剪算法,在屏幕上用一个封闭矩形裁剪任意一条直线。
调试、编译、修改程序。
编码裁剪算法的主要思想是:
对于每条线段,分为三种情