基于OpenGL的3D旋转魔方实现汇总.docx
- 文档编号:11184488
- 上传时间:2023-02-25
- 格式:DOCX
- 页数:18
- 大小:26.96KB
基于OpenGL的3D旋转魔方实现汇总.docx
《基于OpenGL的3D旋转魔方实现汇总.docx》由会员分享,可在线阅读,更多相关《基于OpenGL的3D旋转魔方实现汇总.docx(18页珍藏版)》请在冰豆网上搜索。
基于OpenGL的3D旋转魔方实现汇总
华中科技大学电子科学与技术系
课程设计报告
(2010--2011年度第2学期)
名称:
软件课程设计
题目:
基于OpenGL的3D旋转魔方实现
院系:
班级:
学号:
学生姓名:
指导教师:
设计周数:
成绩:
日期:
年月日
1.1目的………………………………………………………………………………………………..……2
1.2内容………………………………………………………………………………………………..……2
1.3取得的成果……………………………………………………………………………………………2
2.1程序原理………………………………………………………………………………………………3
2.2程序流程………………………………………………………………………………………………4
2.3数据结构………………………………………………………………………………………………13
2.4重要函数………………………………………………………………………………………………13
3.程序分析与结果演示…………………………………………………………16
3.1成果演示………………………………………………………………………………………………16
3.2程序分析………………………………………………………………………………………………17
4.出现过的问题……………………………………………………………………18
5.心得和小节………………………………………………………………………19
1.课程设计介绍
1.1目的
21世纪是高科技时代,是信息技术时代,而计算机技术无疑会引领各行各业,为我们带来一个全新的时代。
作为新世纪的接班人,我们必须拥有良好的计算机应用能力,才能跟上世界发展的大流,不至于在激烈的竞争中被淘汰。
而程序作为计算机的灵魂,因此编程能力对当代大学生来说至关重要。
通过本课程单元的学习,可以对软件工程项目从整体上有一个较清晰的了解和认识;可以提高自身软件编程能力,培养对计算机编程兴趣,培养良好的编程习惯。
同时编程时的态度和方法对我们今后的学习和工作也有重要影响。
所以整体看来软件课程设计这门课程提高了我们计算机使用水平,培养了我们良好的学习态度,对我们个人的发展而言有着重要的意义。
1.2内容
(1)巩固和加强c语言相关编程知识,学会用VisualC++6.0进行c语言编程。
(2)掌握程序设计流程和思想,模块化结构分析以及程序设计流程,初步培养需求分析、软件测试、调试的能力。
(3)掌握win32相关编程知识,了解windows程序内部运行机制。
(4)掌握OpenGL贴图技术原理与函数实现,掌握OpenGL几何的移动、旋转等模式变化的原理。
(5)掌握魔方图形构造原理,在掌握二阶魔方构造原理的基础上,构造出三阶魔方并实现其旋转。
1.3取得的成果
在理解和掌握老师所给的范例程序的基础上,借助Win32平台进行了一系列调试和学习,熟练掌握了Win32Application开发流程。
同时也学习和了解了OpenGL的基本知识,掌握了一些OpenGL的重要技术与重要函数的使用,编写了一些简单的OpenGL程序。
在比较透彻的了解了二阶魔方的构造原理后,成功地构造出了三阶魔方,换上了自己班级同学的图片,并且在一个小立方体的六个面上贴上了不同的图片。
能够比较完美的实现三阶魔方各个层面的随机旋转,并且把窗口背景设置为红色。
为了使程序更加有趣,我在程序中导入了刘德华的《爱你一万年》这首歌,使魔方在旋转的同时能够播放歌曲。
除此之外,我还实现了一种三阶魔方自由移动的屏保效果:
即三阶魔方在旋转的同时能够在屏幕内部自由移动,并且在边缘无限次的反弹。
在魔方平移的过程中同样可以通过四个方向键来控制魔方的移动。
当松开方向键后,魔方会继续按照先前的方式自由移动。
2.程序分析
2.1程序原理
(1)OpenGL
OpenGL是为OpenGraphicsLibrary的简称,它是3D绘图工业标准,广泛地应用于计算机3D绘图领域。
它是个专业的开放的3D程序接口,是一个功能强大,调用方便的底层3D图形库。
它独立于窗口系统和操作系统,以它为基础开发的应用程序可以十分方便地在各种平台间移植;OpenGL可以与VisualC++紧密接口,便于实现机械手的有关计算和图形算法,可保证算法的正确性和可靠性;它具有七大功能:
建模、变换、颜色模式设置、光照和材质设置、纹理映射、位图显示和图象增强和双缓存动画功能。
OpenGL使用简便,效率高。
本项目是在VisualC++6.0开发环境下,使用OpenGL函数库,绘制魔方并实现魔方贴图、随机旋转、以及键盘控制等功能。
采用基本图形的绘图函数及定位函数,添加相应纹理来实现魔方模型的绘制。
通过读取载入BMP文件,应用纹理贴图技术来完成对魔方旋转面的处理。
通过OpenGL中对图形的旋转和平移函数来实现对魔方整体的旋转和平移。
(2)旋转
在建立好空间三维模型后,要实现魔方体每一层面的旋转。
而魔方体每一层面的旋转归结于每一个小立方体的旋转。
每个小立方体的旋转又最终归结于每个点的旋转。
对于一个坐标为(x,y,z)的点,如果围绕z轴逆时针旋转角度为a,则旋转之后z坐标不变,x和y坐标分别变为x*cosa-y*sina,x*sina+y*cosa,如图1所示:
图1
这样,实现了每个点的旋转,针对每个立方体只需采用循环对8个点均采取旋转操作就可实现一个立方体的旋转。
(3)消息循环与定时器
由于程序在运行时CPU只能执行一个任务,然而此项目中魔方在旋转的同时要实现平移,所以需要用到Win32中的定时器功能。
此程序中要用到的定时器的函数原型为:
SetTimer(HWNDhWnd,UINTnIDEvent,UINTuElapse,TIMERPROClpTimerFunc)
HWNDhWnd为窗口句柄,使程序和定时器建立联系,UINTnIDEvent是 定时器ID,用于区分不同的定时器;UINTuElapse为定时器触发周期,意味着多长时间执行一次;,TIMERPROClpTimerFunc为该定时器执行时触发的函数。
所以控制好不同定时器的触发周期和触发函数,就能使魔方的各个层面的旋转和平移互不冲突。
2.2程序流程
(1)WinMain主函数
WinMain主函数是所有Win32程序的入口点。
在WinMain函数里窗体的建立和消息循环,在消息循环中实现键盘、鼠标输入事件处理响应。
在本程序中,要创建Window窗体和构建OpenGL设备绘图环境。
Window窗体创建步骤:
●窗体类注册:
RegisterClass
●设置显示分辨率:
ChangeDisplaySettings
●设置窗体大小:
AdjustWindowRectEx
●创建窗体:
CreateWindowEx
OpenGL绘图环境搭建:
●获取设备绘图环境(DC,DeviceContext):
hDC=GetDC(hWnd)
●选择绘图环境像素格式:
ChoosePixelFormat(hDC,&pfd),其中pfd为像素格式描述符,如果设置不对,OpenGL绘图失败,看不到正确的显示结果。
●设置绘图环境像素格式:
SetPixelFormat(hDC,PixelFormat,&pfd)
●获取OpenGL绘图环境:
hRC=wglCreateContext(hDC)
●设置OpenGL绘图环境:
wglMakeCurrent(hDC,hRC)
(2)三维建模
一个三阶魔方体由27个小立方体构成,每个小立方体由8个顶点组成,而每个顶点又有x,y,z三个方向上的坐标值。
这样由结构体的层层嵌套就可以对魔方体的每个小立方体、每个顶点进行操作。
typedefstruct
{
GLfloatp[3];//定义一个点的x,y,z坐标值
}stPoint;
typedefstruct
{
stPointCubePoint[8];//定义一个小立方体的8个顶点
}stCube;
如图2:
图2
stCubeCube[27];//定义魔方体的27个小立方体
其中一个难点是怎样根据各个点的坐标值构造出魔方体,其实只要定义好每个顶点的坐标就行了。
但是三阶魔方必须定义27*8=216个顶点的坐标值,而且很难用for循环实现,因为各个顶点的x,y,z坐标值几乎没有什么规律。
但是如果能够将一个小魔方体作为一个整体来看待,工作量似乎会减轻很多。
先在整个魔方体中间定义一个基准小立方体,则整个魔方体的各个小立方体均可以通过这个基准立方体的平移来实现,各个小立方体上各点的平移向量和小立方体中心的平移向量相同。
staticstPointCubePoint[8]=//定义好基准小立方体,边长为1
{-0.5f,-0.5f,0.5f},//0--
{0.5f,-0.5f,0.5f},//1
{0.5f,0.5f,0.5f},//2--
{-0.5f,0.5f,0.5f},//3
{-0.5f,-0.5f,-0.5f},//4--
{-0.5f,0.5f,-0.5f},//5
{0.5f,0.5f,-0.5f},//6--
{0.5f,-0.5f,-0.5f},//7
};
基准小立方体平移得到一个小立方体Cube[0]:
for(inti=0;i<8;i++)
{
Cube[0].CubePoint[i].p[0]=CubePoint[i].p[0]+1.0f;
Cube[0].CubePoint[i].p[1]=CubePoint[i].p[1]-1.0f;
Cube[0].CubePoint[i].p[2]=CubePoint[i].p[2]-1.0f;
}
其他26个立方体可通过同样的方法得到。
我认为这是整个程序中最难也是最麻烦的一点。
本程序是通过reset_model()这一函数来构造出整个魔方体的。
(3)OpenGL贴图实现
glGenTextures(1,&texture[i]),作用是利用载入的图像生成纹理。
glTexCoord2f(GLfloats,GLfloatt)函数用于绘制图形时指定纹理的坐标。
第一个参数是X坐标,0.0是纹理的左侧,0.5是纹理的中点,1.0是纹理的右侧。
第二个参数是Y坐标,0.0是纹理的底部,0.5是纹理的中点,1.0是纹理的顶部。
为了将纹理正确的映射到四边形上,必须将纹理的四个角与四边形的四个角相对应。
否则图形在四边形上显示时将会有错误。
如果要将一张格式合格的图片(采用的是256*256的图片)贴到正方体的一个面上,则需要联合使用glTexCoord2f和glVertex3f函数,如下:
glBegin(GL_QUADS);//发布四边形绘图指令
glTexCoord2f(0.0f,0.0f);glVertex3fv(CubePoint[0].p);
glTexCoord2f(1.0f,0.0f);glVertex3fv(CubePoint[1].p);
glTexCoord2f(1.0f,1.0f);glVertex3fv(CubePoint[2].p);
glTexCoord2f(0.0f,1.0f);glVertex3fv(CubePoint[3].p);
这样可以用同样的方法在其他5个面贴上相应的纹理图。
这样就可以得到6个面均贴好纹理图的小立方体。
glBindTexture是OpenGL核心函数库中的一个函数。
作用是选择生成的纹理。
glBindTexture(GL_TEXTURE_2D,texture[i]),作用是选择生成的纹理。
将texture[1]中的图片纹理绑定到程序中,然后就可以画出6个面均是该纹理的小立方体。
这样画出的小立方体的六个面上的图片是相同的。
以此类推,便可画出其他的小立方体。
而我在该程序中实现了将小立方体的六个面贴上不同的图片,原理与上面类似。
只需要在给每个面贴图片时都载入不同的纹理即可。
OpenGL贴图具体流程如下:
图3
(4)魔方整体旋转和平移
OpenGL中有给定的函数来实现图形的旋转和平移:
glTranslatef和glRotatef。
其中,glTranslatef用于平移和确定图形在界面上的位置。
glTranslatef(x,y,z)作用是将图形平移到坐标为(x,y,z)的点。
其中,x,y分别为屏幕横向和纵向。
z轴为垂直屏幕方向,沿屏幕向外为正方向。
屏幕中心默认坐标为(0,0,0)。
glRotatef函数控制魔方整体的旋转。
如glRotatef(xrot,1.0f,0.0f,0.0f);
控制魔方围绕向量为(1,0,0)的直线(即x轴)旋转xrot角度。
控制魔方整体绕y轴和z轴的旋转可以用同样的方法实现。
综合以上分析结果,便可以写出控制有纹理贴图的一阶立方体的旋转程序。
程序运行效果如下图4:
图4
(5)魔方各个层面的随机旋转和同步更新索引
27个立方体构成3X3魔方矩阵,对每个立方体进行编号。
魔方体某层的旋转是基于选中的9个立方体绕魔方体的中心轴变化的。
由此定义魔方体每个层面包含的子立方体集合初始索引编号:
constBYTESZP[9]={0,1,2,3,4,5,6,7,8};
constBYTESZS[9]={9,10,11,12,13,14,15,16,17};
constBYTESZM[9]={18,19,20,21,22,23,24,25,26};
constBYTESYM[9]={0,1,2,11,10,9,18,19,20};
constBYTESYS[9]={3,4,5,14,13,12,21,22,23};
constBYTESYP[9]={6,7,8,17,16,15,24,25,26};
constBYTESXM[9]={2,3,8,17,12,11,20,21,26};
constBYTESXS[9]={1,4,7,16,13,10,19,22,25};
constBYTESXP[9]={0,5,6,15,14,9,18,23,24};
预定义好这些层面目的是确定好旋转前后各个子立方体的相对位置。
为了记录魔方体旋转之后各子立方的绝对位置,为此专门定义了同上类似的数组结构:
BYTEZP[9]={0,1,2,3,4,5,6,7,8};
BYTEZS[9]={9,10,11,12,13,14,15,16,17};
BYTEZM[9]={18,19,20,21,22,23,24,25,26};
BYTEYM[9]={0,1,2,11,10,9,18,19,20};
BYTEYS[9]={3,4,5,14,13,12,21,22,23};
BYTEYP[9]={6,7,8,17,16,15,24,25,26};
BYTEXM[9]={2,3,8,17,12,11,20,21,26};
BYTEXS[9]={1,4,7,16,13,10,19,22,25};
BYTEXP[9]={0,5,6,15,14,9,18,23,24};
魔方体几何位置旋转本质上就是同步更新各层面内的小立方的索引表。
如,更新z方向上的第一层的小立方的索引表的部分代码如下:
for(i=0;i<27;i++)
{
for(j=0;j<9;j++)
if(is_equal(&Cube[i],&Static_Cube[SZM[j]])
{
ZM[k++]=i;
}
}
is_equal()是判断旋转后的魔方体Cube的那些子立方Cube[i]与静态的魔方体Static_Cube的SZM层面内的子立方重合,找出之后作为ZM的新成员。
最终能找到9个子立方作为新成员。
而在前面已经讨论了如何实现立方体围绕某个轴旋转的问题。
对某一层面内的9个小立方体均进行旋转操作,然后进行更新索引,就可以得到旋转之后的魔方体。
在本程序中我定义了Rotate_XM(),Rotate_XS(),Rotate_XP(),Rotate_YM(),Rotate_YS(),Rotate_YP(),Rotate_ZM(),Rotate_ZS(),Rotate_ZP()这9个函数,分别控制各个层面的旋转。
由于魔方体各个方向上不同层面不能同时旋转,所以要在程序中保证各个层面之间的旋转互不冲突。
在本程序中我定义了enable_XM_roatate(),enable_XS_roatate(),enable_XP_roatate(),enable_YM_roatate(),enable_YS_roatate(),enable_YP_roatate(),enable_ZM_roatate(),enable_ZS_roatate(),enable_ZP_roatate()这9个函数,用于判断某一方向上某一层面是否可以旋转。
并且设置了rotX,rotY,rotZ三个全局变量。
在以上9个函数中分别对这3个变量之一进行赋值。
在enable_XM_roatate(),enable_XS_roatate(),enable_XP_roatate()三个函数中分别将rotX赋值为1,2,3,在enable_YM_roatate(),enable_YS_roatate(),enable_YP_roatate()三个函数中分别将rotY赋值为1,2,3,在enable_ZM_roatate(),enable_ZS_roatate(),enable_ZP_roatate()三个函数中分别将rotZ赋值为1,2,3,同时保证rotX,rotY,rotZ一次只有一个不为零,这样就保证了各个层面旋转之间互相不冲突。
(6)背景音乐的添加
我在该程序中载入了背景音乐,方法比较简单。
#include
#pragmacomment(lib,"WINMM.LIB")//导入winmm.lib库,实现对多媒体编程的支持
在所建工程的文件夹中新建名为sound的文件夹,并在其中放入wav格式的音乐《爱你一万年》。
编写函数loadsound(),其调用函数PlaySound("sound\\爱你一万年.wav",NULL,SND_LOOP|SND_ASYNC|SND_FILENAME)加载爱你一万年背景音乐。
(7)定时器的调用
我认为本程序中使用Win32系统的定时器很关键,因为程序只能一次执行一个线程,而魔方在自身旋转的同时还有各个层面的自由随机旋转,还加上自身的平移等,所以必须用到定时器。
在本程序中我用到了3个定时器:
:
:
SetTimer(hWnd,1,2,NULL),:
:
SetTimer(hWnd,2,1,TimerProc),:
:
SetTimer(hWnd,3,0.5,CubeWalk)。
定时器:
:
SetTimer(hWnd,1,2,NULL)的作用是把把其产生的消息加入消息队列中,由WM_TIMER接收并处理,以实现立方体的旋转。
定时器:
:
SetTimer(hWnd,2,1,TimerProc)的回调函数是TimerProc。
函数TimerProc用来产生随机数。
要控制各个层面的随机旋转,一共有18种情况,包括每个方向上每个层面上的正转和反转。
故在TimerProc函数中对随机数的情况进行判断:
intr=rand();
if(r%18==0)
enable_XM_roatate
(1);
以上是18中情况中的一种,使魔方的XM层能够旋转,即在enable_XM_roatate
(1)中进行如下赋值:
rotAngle=1
rotCount=0
rotX=1
rotDirect=1
在WM_TIMER中判断rotX,rotY,rotZ的值并进行相应的旋转操作。
本程序中的另外一个定时器:
:
SetTimer(hWnd,3,0.5,CubeWalk)作用是控制魔方的平移。
前面已经介绍了魔方自身的平移函数glTranslatef。
在程序的最开始定义变量RX和RY,
glTranslatef(RX,0.0f,0.0f);
glTranslatef(0.0f,RY,0.0f);
则RX和RY代表魔方中心的横纵坐标。
在回调函数CubeWalk中对RX和RY值进行处理和改变就可以控制魔方的平移了。
定时器的使用及其控制魔方的旋转和平移流程图如图5:
图5定时器使用流程图
2.3数据结构分析
在本程序中为了很好的表示一个魔方体并且便于更好地计算,采用结构体和数组层层嵌套的形式。
定义一个点的结构体,由3个坐标值构成:
typedefstruct{
GLfloatp[3];
}stPoint;
定义一个立方体的结构体,由8个顶点构成:
typedefstruct{
stPointCubePoint[8];
}stCube;
定义由27个小立方体构成的数组:
stCubeCube[27];
2.4重要函数分析
(1)窗口创建
程序中的函数GLvoidresizeScene(GLsizeiwidth,GLsizeiheight)目的是重置OpenGL窗口的大小,具体又包含以下几个函数:
glViewport(0,0,width,height)是OpenGL中的视口变化函数,作用是把视景体截取的图像按照怎样的高和宽显示到屏幕上。
glMatrixMode():
指定哪一个矩阵是当前矩阵。
glMatrixMode设置当前矩阵模式:
GL_MODEVIEW,对模型视景矩阵堆栈应用随后的矩阵操作。
GL_PROJECTION,对投影矩阵应用随后的矩阵操作。
glLoadIdentity()将矩阵清为单位矩阵,避免受其它矩阵操作的干扰,作用是将当前的用户坐标系的原点移到屏幕中心。
gluPerspective的作用是设置透视投影矩阵,即越远的东西看起来越小的效果。
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f)
45.0f表示将视角设置为45度,(GLfloat)width/(GLfloat)height表示窗口的纵横比,0.1f表示沿z轴方向的两裁面之间的距离的近处为0.1,100f表示沿z轴方向的两裁面之间的距离的远处为100,即图像在z轴方向上的坐标必须介于-0.1到-100之间,否则无法显示出来。
(2)初始化
在InitGL(GLvoid)中对窗体进行初始化效果:
glShadeModel(GL_SMOOTH)作用是启用阴影平滑。
glClearColor(0.5f,0.0f,0.0f,0.5f)作用是设置背景为红色。
glClearDepth(1.0f),作用是设置深度缓存。
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST),作用是做精细的透视修正。
loadsound(),作用是导入歌曲爱你一万年。
(3)OpenGL贴图
glGenTextures(1,&textur
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 OpenGL 旋转 魔方 实现 汇总