3D游戏图形学实验一分解.docx
《3D游戏图形学实验一分解.docx》由会员分享,可在线阅读,更多相关《3D游戏图形学实验一分解.docx(18页珍藏版)》请在冰豆网上搜索。
![3D游戏图形学实验一分解.docx](https://file1.bdocx.com/fileroot1/2023-1/23/715b7b93-b72d-450e-a606-1c6359c825b5/715b7b93-b72d-450e-a606-1c6359c825b51.gif)
3D游戏图形学实验一分解
《3D游戏图形学》
实验指导书
浙江理工大学
二0一五年十月
课程实验指导
一、实验总体方案
1.教学目标与基本要求
1)掌握本书所介绍的图形算法的原理。
2)掌握通过具体的平台实现图形算法的方法,培养学生使用现代图形系统API的能力。
3)通过实验培养具有开发一个基本图形软件包的能力。
2.实验平台
实验主要结合OpenGL设计程序,实现各种课堂教学中讲过的图形算法。
程序设计语言主要以C/C++为主,开发平台是VisualC++。
3.实验步骤
1)预习教材与实验指导的实验具体方案部分相关的算法理论及原理。
2)仿照教材与实验指导提供的算法,利用VC++OpenGL进行实验。
3)调试、编译、运行程序,运行通过后,可考虑对程序进行修改或改进。
二、实验预备知识
OpenGL作为当前主流的图形API之一,在一些场合具有比DirectX更优越的特性。
(1)与C语言紧密结合
OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenGL很容易理解和学习。
(2)强大的可移植性
微软的Direct3D虽然也是十分优秀的图形API,但它只适用于Windows系统,而OpenGL不仅适用于Windows,还可以用于Unix/Linux等其他系统,它甚至在大型计算机、各种专业计算机上都有应用。
并且,OpenGL的基本命令都做到了硬件无关,甚至是平台无关。
(3)高性能的图形渲染
OpenGL是一个工业标准,它的技术紧跟时代,现今各个显卡厂家无一不对OpenGL提供强力支持,激烈的竞争中使得OpenGL性能一直领先。
总之,OpenGL是一个非常优秀的图形软件接口。
下面对Windows下的OpenGL编程进行简单介绍。
以下几点是学习OpenGL前的准备工作。
1.选择一个编译环境
现在Windows系统的主流编译环境有VisualC++,C++Builder,Dev-C++等,它们都支持OpenGL。
这里选择VisualC++作为学习OpenGL的实验环境。
2.安装OpenGL工具包
1)将OpenGL工具包dll文件夹中的*.dll文件放到操作系统目录下面的system32文件夹(其路径一般为:
C:
\Windows\System32)。
2)打开VC,在VC中选择Tools→Options→Directories,然后在Showdirectoriesfor中选择Includefiles,在下面添加OpenGL工具包中Include文件夹的路径,如下图所示:
3)类似地,在Showdirectoriesfor中选择libraryfiles,在下面添加OpenGL工具包中lib文件夹的路径,然后按OK。
如下图所示:
3.建立一个OpenGL工程
打开VC后,在VC中选择File→New→Project,然后选择Win32ConsoleApplication,选择一个名字,然后按“OK”。
在弹出的对话框中点Anemptyproject,选择Finish。
然后向该工程添加一个源文件,选择一个名字。
三、实验报告要求
(1)有实验报告封面
(2)给出简要的设计思路(原理)。
(3)给出实现代码。
(4)给出实验结果的屏幕截图。
(5)实验的心得体会或建议。
实验一VC++6.0+OpenGL绘图环境及基本图形学算法
实验项目性质:
验证性实验
所属课程名称:
3D游戏图形学
实验计划学时:
3学时
一.实验目的
1、熟悉OpenGL的主要功能;
2、掌握OpenGL的绘图流程和原理;
3、掌握OpenGL核心函数的使用;
4、理解基本图形元素光栅的基本原理;
5、掌握直线和圆的多种生成算法。
二.实验内容
1、创建一个OpenGL工程,利用OpenGL库函数进行简单图形设计与绘制;
2、编程实现DDA算法和Bresenham算法生成直线;
3、编程实现中点算法生成圆。
三.实验原理
1、基本语法(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。
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的库函数构造几何物体对象的数学描述,包括点线面的位置和拓扑关系、几何变换、光照处理等。
3、OpenGL状态机制
OpenGL的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。
例如,当前颜色就是一个状态变量,在这个状态改变之前,绘制的每个像素都将使用该颜色,直到当前颜色被设置为其他颜色为止。
OpenGL中大量地使用了这种状态机制,如颜色模式、投影模式、单双显示缓存区的设置、背景色的设置、光源的位置和特性等。
许多状态变量可以通过glEnabel()、glDisable()这两个函数来设置成有效或无效状态,如是否设置光照、是否进行深度测试等;在被设置成有效状态之后,绝大部分状态变量都有一个缺省值。
四.实验代码
1、如预备知识所述,安装OpenGL工具包,创建一个OpenGL工程,在源文件上输入如下代码:
#include
#include
//初始化OpenGL场景
voidmyinit(void)
{
glClearColor(0.0,0.0,0.0,0.0);//将背景置成黑色
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0,200.0,0.0,150.0);
glShadeModel(GL_FLAT);//设置明暗处理
}
//用户的绘图过程
voiddisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//清除缓存
glBegin(GL_LINES);//开始画一根白线
glColor3f(1.0f,1.0f,1.0f);//设置颜色为白色
//设置第一根线的两个端点,请注意:
OpenGL坐标系的原点是在屏幕左下角
glVertex2f(10.0f,50.0f);
glVertex2f(110.0f,50.0f);
glColor3f(1.0f,0.0f,0.0f);//设置颜色为红色
//设置第二根线的两个端点
glVertex2f(110.0f,50.0f);
glVertex2f(110.0f,150.0f);
glEnd();//画线结束
glFlush();//绘图结束
}
//
//主过程:
//初始化Windows的窗口界面
//并初始化OpenGL场景,绘图
intmain(intargc,char**argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);//初始化窗口的显示模式
glutInitWindowSize(400,300);//设置窗口的尺寸
glutInitWindowPosition(100,120);//设置窗口的位置
glutCreatWindow(“”);//创建一个名为“”的窗口
glutDisplayFunc(Display);//设置当前窗口的显示回调函数
myinit();//完成窗口初始化
glutMainLoop();//启动主GLUT事件处理循环
return(0);
}
glaux.lib
glu32.lib
glut32.lib
opengl32.lib
注:
glShadeModel选择平坦或光滑渐变模式。
GL_SMOOTH为缺省值,为光滑渐变模式,GL_FLAT为平坦渐变模式。
该程序是在一个黑色的窗口中画两条线,分别用白色和红色绘制。
首先,需要包含头文件#include,这是GLUT的头文件。
本来OpenGL程序一般还要包含和,但GLUT的头文件中已经自动将这两个文件包含了,不必再次包含。
然后是main函数,其使用GLUT库实现窗口管理。
我们关注的是display()函数,它是我们真正绘图的地方。
函数glColor3f()以RGB方式设置颜色,格式为:
glColor3f(red,green,blue),每种颜色值在(0.0,1.0)之间。
函数glVertex2f(x,y)设置二维顶点。
函数glBegin(UINTState)、glEnd()是最基本的作图函数,下面对它作一介绍。
如上所述,OpenGL是一个状态机,glBegin(UINTState)可以设定如下状态:
GL_POINTS画点
GL_LINES画线,每两个顶点(Vertex)为一组
GL_LINE_STRIP画线,把若干个顶点顺次连成折线
GL_LINE_LOOP画线,把若干个顶点顺次连成封闭折线
GL_TRIANGLES画三角形,每三个顶点为一组
GL_QUADS画四边形,每四个顶点为一组
GL_POLYGON画多边形
还有GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN,GL_QUADS_STRIP等等。
大家把每一种状态都试一试。
另外,程序可以有多组glBegin()、glEnd()并列的形式,如:
......
glBegin(GL_LINES);
......
glEnd();
glBegin(GL_QUADS);
......
glEnd();
......
除了上述的基本图元外,函数glRectf(x1,y1,x2,y2)可以画一个矩形,但这个函数不能放在glBegin()和glEnd()之间,下面的两句程序是画一个蓝色的矩形。
glColor3f(0.0f,0.0f,1.0f);
glRectf(10.0f,10.0f,50.0f,50.0f);
要求:
利用OpenGL库函数绘制基本图形,输出绘制结果。
voiddisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//清除缓存
glBegin(GL_LINES);//开始画一根白线
glColor3f(1.0f,1.0f,1.0f);//设置颜色为白色
//设置第一根线的两个端点,请注意:
OpenGL坐标系的原点是在屏幕左下角
glVertex2f(10.0f,50.0f);
glVertex2f(110.0f,50.0f);
glColor3f(1.0f,0.0f,0.0f);//设置颜色为红色
//设置第二根线的两个端点
glVertex2f(110.0f,50.0f);
glVertex2f(110.0f,150.0f);
glEnd();
glColor3f(0.0f,0.0f,1.0f);
glRectf(10.0f,10.0f,50.0f,50.0f);
//画线结束
glFlush();//绘图结束
}
2、创建一个新的OpenGL工程,在源文件上输入如下代码:
//DDA算法
#include
voidLineDDA(intx0,inty0,intx1,inty1)
{
intx,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++)
{
glBegin(GL_POINTS);
glVertex2i(x,(int)(y+0.5));
glEnd();
y+=m;
}
}
voidmyDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f,0.0f,0.0f);
glRectf(25.0,25.0,75.0,75.0);
glPointSize(5);
glBegin(GL_POINTS);
glColor3f(0.0f,1.0f,0.0f);glVertex2f(0.0f,0.0f);
glEnd();
LineDDA(0,0,200,300);
glBegin(GL_LINES);
glColor3f(1.0f,0.0f,0.0f);glVertex2f(100.0f,0.0f);
glColor3f(0.0f,1.0f,0.0f);glVertex2f(180.0f,240.0f);
glEnd();
glFlush();
}
voidInit()
{
glClearColor(0.0,0.0,0.0,0.0);
glShadeModel(GL_FLAT);
}
voidReshape(intw,inth)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,(GLdouble)w,0.0,(GLdouble)h);
}
intmain(intargc,char*argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(400,400);
glutCreateWindow("HelloWorld!
");
Init();
glutDisplayFunc(myDisplay);
glutReshapeFunc(Reshape);
glutMainLoop();
return0;
}
介绍一下glutReshapeFunc()函数:
首次打开窗口、移动窗口和改变窗口大小时,窗口系统都将发送一个事件,以通知程序员。
如果使用的是GLUT,通知将自动完成,并调用向glutReshapeFunc()注册的函数。
该函数必须完成下列工作:
a.重新建立用作新渲染画布的矩形区域;
b.定义绘制物体时使用的坐标系。
如:
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)。
要求:
1)根据所给的直线光栅化的示范源程序,在计算机上编译运行,输出正确结果(示范代码有错误,指出并改正);
示范代码展示:
voidLineDDA(intx0,inty0,intx1,inty1)
{
floatx,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++)
{
glBegin(GL_POINTS);
glVertex2i(x,(int)(y+0.5));
glEnd();
y+=m;
}
}
2)根据示范程序采用的算法,以此为基础将其改造为Bresenham算法,写入实验报告;
voidBresenhamline(intx0,inty0,intx1,inty1,intcolor)
{
intx,y,dx,dy;
floatk,e;
dx=x1-x0,dy=y1-y0,k=dy/dx;
e=-0.5,x=x0,y=y0;
for(inti=0;i<=dx;i++)
{
glPointSize(2.0f);
glBegin(GL_POINTS);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(x,y);
glEnd();
x=x+1,e=e+k;
if(e>=0)
{
y++,e=e-1;
}
}
}
3)根据示范代码,将其改造为圆的光栅化算法,要求实现圆的中点算法,并利用八分法画出整圆。
圆的光栅化算法:
voidsetRound(GLintxc,GLintyc,GLintN){
for(inti=0;i{
intx=xc,y=yc;
glLineWidth(4.0f);
glBegin(GL_POINTS);
glColor3f(0,1.0f,1.0f);
glVertex2f(x+R*cos(2*Pi/N*i),y+R*sin(2*Pi/N*i));
glEnd();
}
}
圆的中点算法:
voidDrawCirle(intradius)
{
intx,y;
floatd;
x=0;
y=radius;
d=5-4*radius;
floatdeltaE=12;
floatdeltaSE=20-8*radius;
glPointSize(3);
glBegin(GL_POINT);
glColor3f(0.0,1.0,0.0);
glVertex2i(x,y);
glEnd();
while(y>x)
{
if(d<=0)
{
d+=deltaE;
deltaSE+=8;
}
else
{
d+=deltaSE;
deltaSE+=16;
y--;
}
deltaE+=8;
x++;
glBegin(GL_POINTS);
glVertex2i(x,y);
glVertex2i(x,-y);
glVertex2i(-x,y);
glVertex2i(-x,-y);
glVertex2i(y,x);
glVertex2i(y,-x);
glVertex2i(-y,x);
glVertex2i(-y,-x);
glEnd();
}
}