基于opengl的3d旋转魔方_第1页
基于opengl的3d旋转魔方_第2页
基于opengl的3d旋转魔方_第3页
基于opengl的3d旋转魔方_第4页
基于opengl的3d旋转魔方_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、华中科技大学电子科学与技术系课程设计最终报告( 2011- 2012年度第 2 学期)名 称: 软件课程设计 题 目:基于OpenGL的3D旋转魔方实现院 系: 电子科学与技术系 班 级: 电子1006班 学 号: U201014113 学生姓名: 黄铁森 指导教师: 杨文涛 设计周数: 15周 成 绩: 日期: 2012 年 5 月 22 日目 录一、课程设计介绍31.1、OpenGL ES基础知识31.2、课程设计任务分析及成果3二、程序功能的实现42.1 基本思路444442.2 应用程序功能详细说明5三、方案设计7773.2.1 GLSurfaceView绘制场景788810四、程序清

2、单114.1 资源清单114.2 类图11五、调试过程出现的问题及解决办法135.1 只能加载一面纹理135.2 六个面贴图只要第一个正常135.3 用线程控制魔方的单层旋转145.4 代码精简145.5 将二阶与三阶放在同一个程序中145.6 镜像技术的实现155.7 想做个花样魔方教程15六、个人体会及建议166.1 OpenGL ES缺乏教程166.2 基础很重要166.3 没有捷径可走17七、参考资料17一、课程设计介绍1.1、OpenGL ES基础知识 OpenGL ES (为OpenGL for Embedded System的缩写) 为适用于嵌入式系统的一个免费二维和三维图形库,

3、是桌面版本OpenGL 的一个子集。 OpenGL ES 定义了一个在移动平台上能够支持OpenGL最基本功能的精简标准,以适应如 ,PDA或其它消费者移动终端的显示系统。Khronos Group 定义和管理了OpenGL ES标准。 OpenGL ES 支撑绘制的几何图形分为三类:点,线段,三角形。也就是说OpenGL ES 只能绘制这三种基本几何图形。任何错杂的2D或是3D图形都是经由过程这三种几何图形机关而成的。为了提高性能,OpenGL ES通常将顶点,颜色等值存放在java.nio 包中定义的Buffer类中。 OpenGL ES供给了两类办法来绘制一个空间几何图形:1、publi

4、c abstract void glDrawArrays(int mode, int first, int count) 应用VetexBuffer 来绘制,顶点的次序由vertexBuffer中的次序指定。2、public abstract void glDrawElements(int mode, int count, int type, Buffer indices) ,可以从头定义顶点的次序,顶点的次序由indices Buffer 指定。对应顶点除了可认为其定义坐标外,还可以指定色彩,材质,法线,纹理等 。 对应的传入色彩,顶点,材质,法线的办法如下:glColorPointer(i

5、nt size,int type,int stride,Buffer pointer)glVertexPointer(int size, int type, int stride, Buffer pointer)glTexCoordPointer(int size, int type, int stride, Buffer pointer)glNormalPointer(int type, int stride, Buffer pointer) OpenGL ES 内部存放图形数据的Buffer有COLOR ,DEPTH (深度信息)等,在绘制图形只前一般须要清空COLOR 和 DEPTH B

6、uffer。1.2、课程设计任务分析及成果 本课程设计要求:基于OpenGL的3D魔方实现:在3D空间中实现2X2魔方,各个魔方单元格显示不同的贴图,魔方体能在3D空间自由旋转、每层也能旋转。 Android系统使用OpenGL的标准接口来支持3D图像功能,因此我对课程设计的定位是android平台上基于OPENGL ES的旋转魔方实现。经过几个星期的努力,我已经建立好功能,生成了RotateCube.apk的android 程序,安装在 上后,能看到二阶以及三阶的旋转魔方。并添加了一些设置菜单,背景音乐,星空效果等。二、程序功能的实现2.1 基本思路 立方体有六个面,定义一个六维数组存放六个

7、面的顶点坐标,然后定义一个一维数组存放单个面对的纹理坐标,在draw函数里面分六次,每一次绘出一个有纹理贴图的面,六次之后有不同纹理贴图的立方体就绘制出来了。 定义好一个立方体的坐标之后,之后所有的立方体可以看做是最初立方体在 坐标空间平移而来的,考虑到坐标平移,以及方便在立方体的对象实例化中方便定义平移出的立方体,我将vertex里面的顶点坐标定义为基本坐标+空间平移量的形式。为了方便平移以及使坐标更简单化,我将二阶里面坐标原点设置在基本立方体的期中 一个点上,在三阶里将坐标原点设置在基本立方体的体心上。 魔方单层的旋转可以说是这个题目的最难点,困扰了我好久,最后我想到了一个最“傻”的办法完

8、成了此功能。在单个立方体的绘制时候,我在每个面的绘制函数中添加该面分别绕XYZ轴旋转的代码,这样当我改变旋转的角度值时,每个面都跟着旋转了,看起来就像整个小立方体跟着旋转了。然后在对象实例化之后添加一个线程,控制不同立方体的旋转,只要在线程里面定义好要旋转的立方体,即可时魔方实现单层的旋转。如能实现屏幕上触摸控制视图方向是最好的事情,触摸控制立方体各个方向的转动需要计算手指在屏幕上的微小位移以及方向,然后对应到整个坐标系绕XYZ轴旋转的角度值中去。但是时间按有限,我用了三个seekbar分别控制坐标系分别绕XYZ轴转动的角度值。2.2 应用程序功能详细说明 点击程序图标进图主界面,有两个按钮供

9、选择,点击“二阶魔方”则进入到二阶旋转魔方的界面,点击“三阶魔方”则进入到三阶旋转魔方的界面。(如图1-1) 按 的菜单键可以弹出菜单(如图1-2),由于做的程序较简单,我只设置了三个菜单项:“设置”-点击设置即进入设置界面,可以对魔方的大小,旋转速度进行个性化设置;“关于”-点击关于弹出一个对话框,主要是我个人的关于程序的一下描述,以及对指导老师的感谢;“退出”-点击退出弹出对话框询问是否退出,点击确定就退出主程序。 图11 图12 图1-3 图1-4 图1-5二阶与三阶魔方每个面都可以按照设定好的路径自由旋转,每个小立方体都贴图,并且显示正常;增加设置菜单,能设置魔方的大小、魔方旋转的速度

10、。(如图1-3)三阶魔方旋转时候增加了背景音乐播放功能,并在三阶魔方的外层增加了一个星空类,有线程控制自动旋转,使魔方看起来像在天空中旋转一样。三阶魔方最上面有三个seekbar,分别控制整个魔方向xyz轴的转动,调整三个seekbar,可以对魔方进行全方位的观察。(如图1-5)三、方案设计调用传递 启动启动实例化旋转控制绘制三阶旋转魔方实例化旋转控制绘制二阶旋转魔方 3.2.1 GLSurfaceView绘制场景 编写OpenGL ES应用的起始点是从类GLSurfaceView开始,设置GLSurfaceView只需调用一个方法来设置OpenGLView用到的GLSurfaceView.R

11、enderer. mRenderer = new SceneRenderer(); / 创建场景渲染器setRenderer(mRenderer); / 设置渲染器 GLSurfaceView.Renderer定义了一个统一图形绘制的接口,覆写该方法时会自动生成三个函数: / Called when the surface is created or recreated. public void onSurfaceCreated(GL10 gl, EGLConfig config) / Called to draw the current frame. public void onDrawFr

12、ame(GL10 gl) / Called when the surface changed size. public void onSurfaceChanged(GL10 gl, int width, int height) onSurfaceCreated : 在这个方法中主要用来设置一些绘制时不常变化的参数,比如:背景色,是否打开 z-buffer等。 onDrawFrame: 定义实际的绘图操作。 onSurfaceChanged: 如果设备支持屏幕横向和纵向切换,这个方法将发生在横向纵向互换时。此时可以重新设置绘制的纵横比率。 以上内容是建立一个opengl es工程的基础。xt,

13、yt, zt,/ 0a+xt,yt,zt,/1a+xt,a+yt,zt,/2xt,a+yt,zt,/3xt,yt,-a+zt,/4a+xt,yt,-a+zt,/5a+xt,a+yt,-a+zt,/6xt,a+yt,-a+zt,/7二阶中定义的八个顶点分别如下(三阶与此类似,只不过坐标原点在立方体体心):0 ,0, 0,/ 0a,0,0,/1a,a,0,/20a,0,/30,0,-a,/4a,0,-a,/5a,a,-a,/60,a,-a,/7 相当于 按照向量(xt,yt,zt) 平移 一共三个状态,循环往复 new Thread() public void run() / 省去一些属性的定义w

14、hile (true) while (i t) /第一个状态for (int k = 0; k 4; k+)/需要旋转的各立方体mRenderer2.cubeArrayk.mAngleZ2 += angle;for (int k = 4; k = t) & (i = 2 * t & i = 3* t) i = i - 3 * t;/返回第一个状态.start();3.2.5加载纹理(0,0)(1,1)(0,1)(1,0)图片区域 纹理映射使用的是ST坐标,坐标允许范围是从0到1.定义纹理坐标:float texST = new float 0, 0, 0, 1, 1, 0, 1, 1, ;将坐

15、标值存放到Buffer中:ByteBuffer tbb = ByteBuffer.allocateDirect(texST.length * 4);tbb.order(ByteOrder.nativeOrder();/ 设置字节顺序mTextureBuffer = tbb.asFloatBuffer();/ 转换为int型缓冲mTextureBuffer.put(texST);/ 向缓冲区中放入顶点着色数据mTextureBuffer.position(0);/ 设置缓冲区起始位置开启并绑定纹理gl.glEnable(GL10.GL_TEXTURE_2D); / 开启纹理gl.glEnable

16、ClientState(GL10.GL_TEXTURE_COORD_ARRAY);/ 允许使用纹理ST坐标缓冲gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);/ 为画笔指定纹理ST坐标缓冲gl.glBindTexture(GL10.GL_TEXTURE_2D, MySurfaceView.frontId);/ 绑定纹理图片过大或过小的纹理映射方式:/当图片过小时的纹理映射方法gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LIN

17、EAR_MIPMAP_NEAREST);/当图片过大时的纹理映射方法gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR_MIPMAP_LINEAR);四、程序清单4.1 资源清单资源名大小(KB)像素用途32128*128三阶魔方上面贴图32128*128三阶魔方下面贴图32128*128三阶魔方左面贴图32128*128三阶魔方右面贴图32128*128三阶魔方后面贴图32128*128三阶魔方前面贴图171586*950程序主界面背景图片751512*1134程序图标16128*1

18、28二阶魔方上面贴图16128*128二阶魔方下面贴图16128*128二阶魔方左面贴图16128*128二阶魔方右面贴图16128*128二阶魔方后面贴图16128*128二阶魔方前面贴图/三阶魔方背景音乐4.2 类图MySurfaceView myGLSurfaceView;private SeekBar mSeekBarx;private SeekBar mSeekBary ;private SeekBar mSeekBarz ;MediaPlayer mediaPlayer;onCreate()onResume()onPause()MenuInflater mInflater;onCr

19、eate()onCreateOptionsMenu()onOptionsItemSelected()exitAler() 一共9个类,Main.java负责主界面,RotateCube与MySurfaceView与CubeVertex与Sky负责三阶魔方,RotateCubeTwo与MySurfaceViewTwo与CubeVertexTwo负责二阶魔方;Setting负责设置功能。onCreatey()MySurfaceViewTwo myGLSurfaceView2;onCreate()onResume()onPause()private FloatBuffer mVertexBuffer

20、;private FloatBuffer mVertexBuffer1;private FloatBuffer mVertexBuffer2;private FloatBuffer mVertexBuffer3;private FloatBuffer mVertexBuffer4;private FloatBuffer mVertexBuffer5;private FloatBuffer mTextureBuffer;int vCountpublic float mAngleX;public float mAngleY;public float mAngleZ;float a = 0.9f;C

21、ubeVertex()drawSelf()private SceneRenderer mRenderer;float cx = 10;float cy = 10;float cz = 40;float tx = 0;float ty = 0;float tz = 0;static int upId;static int downId;static int leftId;static int rightId;static int backId;static int frontId;public float size = 5;public long time = 10;public float s

22、eekx;public float seeky;public float seekz;MySurfaceView()initTexture() private FloatBuffer mVertexBuffer;private FloatBuffer mVertexBuffer1;private FloatBuffer mVertexBuffer2;private FloatBuffer mVertexBuffer3;private FloatBuffer mVertexBuffer4;private FloatBuffer mVertexBuffer5;private FloatBuffer

23、 mTextureBuffer;int vCountpublic float mAngleX2;public float mAngleY2;public float mAngleZ2;float a = f;CubeVertexTwo()drawSelfTwo()private SceneRenderer mRenderer;float cx = 10;float cy = 10;float cz = 40;float tx = 0;float ty = 0;float tz = 0;static int upId2;static int downId2;static int leftId2;

24、static int rightId2;static int backId2;static int frontId2;MySurfaceView2()initTexture()final float R=11.0f;Private FloatBuffer mVertexBuffer;private IntBuffer mColorBuffer;int vCount=0;float yAngle;int xOffset;int zOffset;float scale;Sky()drawSelf()五、调试过程出现的问题及解决办法5.1 只能加载一面纹理 加载一个面的纹理较简单,只需在CubeVe

25、rtex里面定义一个纹理坐标,然后将其存放进纹理缓冲器buffer里面,在绘制时,将纹理坐标与顶点坐标绑定。但是加载六面纹理我一直束手无策,尝试过用gl.glTexCoordPointer与gl.glBindTexture两个函数绑定六个不同纹理坐标以及纹理ID,但是却一直出错,百思不得其解时,我想到立方体是由6个面构成的,如果直接绘制一个立方体不能实现六个面的贴图,我可以一次绘制一个面,并给其贴图,依次将六个面绘制出了,那么六个面不同纹理的立方体也就出来了。于是我将原本一维的索引数组改为二维数组,并设置6个indexBuffer,存放需要绘制六个面的顶点索引缓存,然后再DrawSelf函数中

26、分六次绘制各个面。每次绘制都绑定不同的索引Buffer以及纹理ID。5.2 六个面贴图只要第一个正常 用了上面的办法之后,运行程序时,立方体的每个面都贴上了不同的图片,可以只要第一个显示的完全正常,其它五个面的图片都是扭曲的。刚开始我以为是后面几个的纹理坐标没有对应好,然后不断的改纹理坐标,或者修改索引数组,但是依旧只有第一个面贴图正常。上网查了一下gl.glTexCoordPointer,发现它的纹理坐标是与gl.glVertexPointer对应的,也就是说,其他五个面尽管贴上了不同的图片,但是他们都是从第一个面的几个顶点开始的,只不过在对应的那个面才显示出图片而已。 在这里就不得不说有关

27、openges的绘图方式了,可以直接利用顶点数组绘制,也可以设置好顶点后,再定义一个索引数组进行绘制。 我最初实现立方体的绘制使用了索引数组,觉得这样只需将立方体的八个顶点定义之后,只需在索引数组里面定义好完成每个面绘制需要的顶点即可,这样直观又简单,减少出错的可能。但是到了六个面纹理的地步,我才想到我可能不得不抛弃这种做法了。于是我删除了所有索引数组以及indexBuffer,将顶点坐标改为二维的,来分别定义绘制六个面时绘制每个面需要的四个点。将gl.gldraweliments? (具有顶点对应功能)改为drawarray(此时我已经不再需要索引了),运行程序发现每个面都正常贴好了图片。5

28、.3 用线程控制魔方的单层旋转 最初设置二阶魔方的旋转时,设定了魔方单层的三种旋转状态,但是当第三种状态结束之后魔方就停止旋转了,我想魔方能够一直保持这三种状态旋转下去,但是做了很多修改都没用(java里面没有C里面的if end语句)。想到我只是对线程部分代码做修改,每修改一次还有上传到模拟器上安装运行很耗时,于是自己新建WhileDemo.java写了一个与线程部分类似的小程序在电脑上面不断调试。这样有许多好处,一个不必改动我工程里面的代码,免得代码太多,出现不必要的错误;二个新建java在代码编辑器里面修改,在cmd命令符里面编译运行,速度快。理清逻辑之后做了几次修改就完成了ABC的循环

29、输出。于是在工程里面根据WhileDemo的内容做了相应改动,发现二阶魔方已经能够循环旋转了。5.4 代码精简基本的一些功能都完成之后我回过头来看自己代码,发现效率很低,有很多冗余。于是对代码对了很多精简,首先我在onSurfaceCreated里面将实例化的立方体放在数组中,于是就可以在onDrawFrame里面用数组完成多个立方体的绘制了。在立方体层与层旋转的线程中,与本是一个个列出需要旋转的层对应的立方体,之后用几个for循环,对代码精简了不少。 在cubevertex里面我声明了mVertexBuffer一直到mVertexBuffer5共六个buffer,然后在drawself里面绘

30、制六次才将立方体绘制出来,我打算设置mVertexBufferi,用循环来精简代码,但一直出错,上网查了一下gl.glVertexPointer里面对数据格式有要求,原来这是精简不了的(起码目前对我来说)。未解决部分:在立方体的实例化时,本想利用循环实例化对象得到new CubeVertex(i-1, j-1, k-1),这样仅一个语句就可以实例化所想要的对象,但是一直出错,至今没有找到解决办法。5.5 将二阶与三阶放在同一个程序中三阶的工作完成之后,之前写的二阶的代码一直舍不得丢掉,毕竟是自己花费了好多时间写出来的,所以就想到将二阶与三阶都集合到一个工程里面,提供一个选择界面,可以任意选择展

31、示二阶或者三阶魔方。在原来工程的src里面新建一个文件夹专门安放二阶的class,新建一个文件夹安放主界面class,点击主界面的button调用相关的class,然后实现选择功能,但是调用必须在二阶的class里面import原来的文件夹,这样就造成原来类中的函数与二阶类中函数冲突(我都没有改名),这么一想我还不如直接将二阶与三阶的各函数命名不同,然后就安放在同一个文件夹里面,于是就这么简单地实现了能够二阶与三阶进行选择的功能。5.6 镜像技术的实现在书中看到一例实现镜像技术后,觉得自己的魔方也有必要加入镜像技术,因为在转魔方的时候往往不仅需要看到正对着的面,还需要看到背面的情况。于是我依葫

32、芦画瓢,在CubeVertex里面新定义一个DrawSelfMirror方法,仅比DrawSelf多两句gl.glRotatef(180, 1, 1, 1);/将立方体旋转至背面。gl.glTranslatef(3 ,3 3,);/将此方法绘制的立方体移动一个距离。这样确实能够在屏幕上面绘制两个魔方,当看到界面时候我有点小兴奋,以为构想能够实现,但是当转完一圈之后发现mirror绘制出来的魔方被分开了,思考了一下,原来不管怎样,我的坐标原点只有一个,而我定义魔方旋转的方法都是绕着坐标原点进行的。因为最初的这个简陋的思路,导致在后续的完善中出现一些不能解决的问题。如果有时间的话,我一定会换一种思

33、路。5.7 想做个花样魔方教程 当所有的都做好之后再看自己的魔方旋转,觉得仅仅几个被固定的层简单旋转没有什么意思,出了自己有点做出小成就感之外对别人没有任何用处,联想到魔方的一些花样玩法,觉得自己可以通过在线程里面设定好旋转的层以及方向,旋转出各种花样,然后将此软件做成一个魔方花样软件,供用户学习魔方花样玩法时使用。由于花样的旋转过程中各个小立方块的位置问题很麻烦,于是自己将买来的魔方每一个小立方体上面都贴了相应标号。最先准备做一个“回”的花样,可是再魔方旋转了两次之后设置第三次旋转时候,线程里面mAngleX与mAngleZ,好像没有区别了一样,无论设置哪个,最后的效果都是对mAngleX进

34、行设置,直接导致我需要某层绕Z轴旋转,但是它却只绕X轴旋转。这个问题困扰了好久,一直得不到解决。时间也有限,于是我做个魔方花样玩法教程软件的想法就被搁浅了。六、个人体会及建议6.1 OpenGL ES缺乏教程关于OpenGL介绍的书还是能找到,可是介绍OpenGL ES的我就一直找不到了。网上介绍android上opengles编程的也仅仅止步于三角形,少有涉及到立方体的也一般是一个面的纹理贴图,有几个六个面贴图的例子,也与自己最初的方法有悖,只能放弃而自己找解决办法。我整个开发过程都一直是在前行中摸索,摸索中前行。在网上找一切可能有用的资源,然后研究思考;天天泡在图书馆203,把关于android上游戏开发的几本书都摆在面前

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论