《2022年Android_D_OpenGLES基础教程 .pdf》由会员分享,可在线阅读,更多相关《2022年Android_D_OpenGLES基础教程 .pdf(35页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、开始Android 3D 游戏开发教程 Part I-VI 本帖最后由huzht 于 2010-4-25 07:58 编辑这几篇 Android 3D 游戏开发的文章原文出自一位德国人Martin 在 写的文章,有lixinso 翻译为中文。第一部分首先介绍OpenGL 相关的术语,并引导你开始3D 开发的第一步。这个关于3D 游戏的系列的叫做Vortex . 这个教程主要focus 在 3D 编程上,其他的东西比如菜单和程序生命周期虽然是代码的一部分,但是在这里不会被提到。首先开始介绍OpenGL 的术语。顶点 Vertex 顶点是3D 空间中的一个点,也是许多对象的基础元素。在OpenGL
2、 中你可以生命少至二维坐标(X,Y) ,多至四维(X,Y ,Z,W). w 轴是可选的,默认的值是1.0. Z 轴也是可选的,默认为0. 在这个系列中,我们将要用到3 个主要的坐标 X,Y,Z,因为 W 一般都是被用来作为占位符。vertex 的复数是vertices(这对非英语母语的人来说比较重要,因为这容易产生歧义) 。所有的对象都是用vertices 作为它们的点,因为点就是vertex。三角形 Triangle 三角形需要三个点才能创建。因此在OpenGL 中,我们使用3个顶点来创建一个三角形。多边形 Polygon 多边形是至少有3 个连接着的点组成的一个对象。三角形也是一个多边形。
3、图元 Primitives 一个 Primitive 是一个三维的对象,使用三角形或者多边形创建。形象的说,一个有50000 个顶点的非常精细的模型是一个 Primitive ,同样一个只有500 个顶点的低模也叫做一个Primitive 。现在我们可以开始变成了。创建一个工程交Vortex,activity 也是这个名字。我们的工程应该大概是这个样子的:package com.droidnova.android.games.vortex; import android.app.Activity; import android.os.Bundle; public class Vortex ex
4、tends Activity private static final String LOG_TAG = Vortex.class.getSimpleName(); private VortexView _vortexView; Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); _vortexView = new V ortexView(this); setContentView(_vortexView); 复制代码如上图所示,我们已经添加了View。让
5、我们看一下VortexView 类。package com.droidnova.android.games.vortex; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 35 页 - - - - - - - - - import android.content.Context; import android.opengl.GLSurfaceView; public class VortexView extends GLSurfaceView private static
6、 final String LOG_TAG = VortexView.class.getSimpleName(); private VortexRenderer _renderer; public VortexView(Context context) super(context); _renderer = new VortexRenderer(); setRenderer(_renderer); 复制代码如上所示,我们继承了GLSurfaceView 是因为它会帮助我们画3D 图像。接下来看VortexRenderer 类。一个Renderer包含画一帧所必需的所有东西。引用自这儿refer
7、ences 。Renderer 负责 OpenGL call 来 render 一个帧。来看一下这个类: package com.droidnova.android.games.vortex; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class VortexRenderer implements GLSurfaceView.Renderer privat
8、e static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float _red = 0.9f; private float _green = 0.2f; private float _blue = 0.2f; Override public void onSurfaceCreated(GL10 gl, EGLConfig config) / Do nothing special. Override public void onSurfaceChanged(GL10 gl, int w, int h
9、) gl.glViewport(0, 0, w, h); Override public void onDrawFrame(GL10 gl) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 35 页 - - - - - - - - - / define the color we want to be displayed as the clipping wall gl.glClearColor(_red, _green, _blue, 1.0f); / clear the
10、color buffer to show the ClearColor we called above. gl.glClear(GL10.GL_COLOR_BUFFER_BIT); 复制代码好,我们做了什么?首先我们实现了GLSurfaceView.Renderer 这个接口, 主要是实现3 个方法: onSurfaceCreated(), onSurfaceChanged() 和onDrawFrame() 。这些方法很容易理解,第一个在surface 创建以后调用,第二个是在surface 发生改变以后调用,例如从竖屏切换到横屏的时候,最后一个方法是当任何时候调用一个画图方法的时候。从 11
11、 行到 13 行,我们用浮点数来定义RGB 颜色系统中的每一个颜色。在 28 行,我们通过glClearColor() 方法为底色定义了颜色。底色是在我们能看到的所有东西的后面,所以所有在底色后面的东西都是不可见的。可以想象这种东西为浓雾,挡住了所有的东西。然后我们将要为之设置距离来show 一下它怎么用的。那时候你就一定会明白它是怎么存在的了。为了让颜色变化可见,我们必须调用glClear()以及颜色缓冲的Mask 来清空 buffer,然后为我们的底色使用新的底色。为了能看到它在起作用,我们这里为MotionEvent 创建一个response,使用它来改变颜色。首先在VortexRend
12、erer中来创建一个设置颜色的函数。public void setColor(float r, float g, float b) _red = r; _green = g; _blue = b; 复制代码下面是 VortexView 类中创建的方法来处理MotionEvent 。public boolean onTouchEvent(final MotionEvent event) queueEvent(new Runnable() public void run() _renderer.setColor(event.getX() / getWidth(), event.getY() / g
13、etHeight(), 1.0f); ); return true; 复制代码我们创建了一个匿名的Runnable 对象,这里的run()方法调用renderer 中的 setColor 方法。这有会根据MotionEvent坐标做一些小的计算。现在我们已经有了一个小小的程序来使用OpenGl 来改变我们的背景色了。在德语中我们叫这种小case为“ Mit Kanonen auf Spatzen schie?en”,翻译过来应该是“ 你在车轮上打死了一只苍蝇” 。这说的恰到好处,这只是一个最最最小的例子,要学习OpenGL,你现在要准备更多更多的东西。这部分最后提一下OpenGL 的文档 do
14、cumentation for OpenGL 。这个东西虽然可用想不高,但是它最少是一个文档。Eclipse 工程源代码在这里下载(原地址): Vortex Part I 这里是几个截图: 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 35 页 - - - - - - - - - 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 35 页 - - -
15、 - - - - - - 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 35 页 - - - - - - - - - - 这个系列的第二部分是关于如何添加一个三角形并可以旋转它。第一件事情是初始化需要显示的三角形。我们来在VortexRenderer 类中添加一个方法initTriangle() 。/ new object variables we need / a raw buffer to hold indices private ShortBuffer _inde
16、xBuffer; / a raw buffer to hold the vertices private FloatBuffer _vertexBuffer; private short _indicesArray = 0, 1, 2; private int _nrOfVertices = 3; / code snipped private void initTriangle() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 35 页 - - - - - - - -
17、- / float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder(); _vertexBuffer = vbb.asFloatBuffer(); / short has 2 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder(); _indexBuffer = ibb.as
18、ShortBuffer(); float coords = -0.5f, -0.5f, 0f, / (x1, y1, z1) 0.5f, -0.5f, 0f, / (x2, y2, z2) 0f, 0.5f, 0f / (x3, y3, z3) ; _vertexBuffer.put(coords); _indexBuffer.put(_indicesArray); _vertexBuffer.position(0); _indexBuffer.position(0); 复制代码让我们从新的对象变量开始. _vertexBuffer 为我们的三角形保存坐标._indexBuffer 保存索引
19、. _nrOfVertices 变量定义需要多少个顶点.对于一个三角形来说,一共需要三个顶点. 这个方法首先为这里两个buffer 分配必须的内存(14-22 行). 接下来我们定义一些坐标(24-28 行) 后面的注释对用途给予了说明. 在 30 行,我们将 coords 数组填充给 _vertexBuffer . 同样在 31 行将 indices 数组填充给 _indexBuffer 。最后将两个buffer 都设置 position 为 0. 为了防止每次都对三角形进行初始化,我们仅仅在onDrawFrame() 之前的行数调用它一次。一个比较好的选择就是在onSurfaceCreat
20、ed()函数中 . Override public void onSurfaceCreated(GL10 gl, EGLConfig config) / preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); initTriangle(); 复制代码glEnableClientState() 设置 OpenGL 使用 vertex 数组来画。这是很重要的,因为如果不这么设置OpenGL 不知道如何处理我们的数据。接下来我们就要初始化我们的三角形。为什么我们不需使用不同的buffer? 在新的 onDrawFrame() 方法中我们必须
21、添加一些新的OpenGL 调用。Override public void onDrawFrame(GL10 gl) / define the color we want to be displayed as the clipping wall 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 35 页 - - - - - - - - - gl.glClearColor(_red, _green, _blue, 1.0f); / clear the color buffer
22、to show the ClearColor we called above. gl.glClear(GL10.GL_COLOR_BUFFER_BIT); / set the color of our element gl.glColor4f(0.5f, 0f, 0f, 0.5f); / define the vertices we want to draw gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); / finally draw the vertices gl.glDrawElements(GL10.GL_TRIANGLES
23、, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); 复制代码好,一步一步地看。glClearColor() 和 glClear() 在教程 I 部分已经提到过。 在第 10 行使用 glColor4f(red, green, blue, alpha) 设置三角形为暗红色. 在第 13 行,我们使用glVertexPointer() 初始化 Vertex Pointer. 第一个参数是大小, 也是顶点的维数。 我们使用的是x,y,z三维坐标。第二个参数,GL_FLOAT 定义 buffer 中使用的数据类型。第三个变量是0,是因为我们的
24、坐标是在数组中紧凑的排列的,没有使用offset。最后哦胡第四个参数顶点缓冲。最后, glDrawElements() 将所有这些元素画出来。第一个参数定义了什么样的图元将被画出来。第二个参数定义有多少个元素,第三个是indices 使用的数据类型。最后一个是绘制顶点使用的索引缓冲。当最后测试这个应用的使用,你会看到一个在屏幕中间静止的三角形。当你点击屏幕的时候,屏幕的背景颜色还是会改变。现在往里面添加对三角形的旋转。下面的代码是写在VortexRenderer 类中的 . private float _angle; public void setAngle(float angle) _ang
25、le = angle; 复制代码glRotatef() 方法在 glColor4f() 之前被 onDrawFrame() 调用 . Override public void onDrawFrame(GL10 gl) / set rotation gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); / code snipped 复制代码这时候我们可以绕y 轴旋转。如果需要改变只需要改变glRotate()方法中的0f。这个参数中的值表示一个向量,标志三角形绕着旋转的坐标轴。名师资料总结 - - -精品资料欢迎下
26、载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 35 页 - - - - - - - - - 要让它可用,我们必须在VortexView 中的 onTouchEvent()中添加一个调用。public boolean onTouchEvent(final MotionEvent event) queueEvent(new Runnable() public void run() _renderer.setColor(event.getX() / getWidth(), event.getY() / getHe
27、ight(), 1.0f); _renderer.setAngle(event.getX() / 10); ); return true; 复制代码上面代码中除以10 是为了减小角度变换的速度。现在编译运行这个程序。如果你在屏幕的最左边点击,你会看到三角形轻微旋转。如果你将手指移到右边,旋转的速度就会变得很快。Eclipse 工程的源代码在这里下载(原链接): Vortex Part II 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 35 页 - - - - - -
28、- - - 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 35 页 - - - - - - - - - - 在这个系列的第三部分给你show 一下如何停止三角形的转动,并告诉你原来的旋转其实只是在三角形上进行的旋转,而不是在摄像机“camera”上进行的旋转。我们希望能对旋转进行更多的控制。为此,在每次调用onDrawFrame() 方法的时候都会重置这个矩阵。这会重设三角形的角度以便其总是可以旋转到给定的角度。Override public void onDrawF
29、rame(GL10 gl) / define the color we want to be displayed as the clipping wall gl.glClearColor(_red, _green, _blue, 1.0f); / reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); / clear the color buffer and the depth buffer to show the ClearColor / we called above. gl.gl
30、Clear(GL10.GL_COLOR_BUFFER_BIT); / code snipped 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 35 页 - - - - - - - - - 复制代码在 VortexView 类中,你应该删除“ 除以 10” 以便其可以旋转范围更大一些。_renderer.setAngle(event.getX(); 复制代码如果尝试了这些,你将会看到旋转只会根据触摸的到的位置来旋转。如果没有触摸屏幕,旋转不会发生改变。下一件事情:我们旋
31、转的是三角形本身,还是旋转的view/camera?为了验证它,最简单的办法是创建第二个不旋转的三角形进行对照。最快也是最笨的办法是copy&paste initTriangle() 方法为一个新的方法initStaticTriangle() ,copy&paste其中的两个buffer,copy&paste 并修改 onDrawFrame() 方法中的最后四行。不要忘记了改变第二个三角形的颜色以及改变第二个三角形的坐标,这样方便我们能看到两个三角形。我将每个地方的 0.5f 都改成了0.4f. 这里是整个的类:package com.droidnova.android.games.vorte
32、x; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class VortexRenderer implements GLSurfaceV
33、iew.Renderer private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float _red = 0f; private float _green = 0f; private float _blue = 0f; / a raw buffer to hold indices allowing a reuse of points. private ShortBuffer _indexBuffer; private ShortBuffer _indexBufferStatic;
34、/ a raw buffer to hold the vertices private FloatBuffer _vertexBuffer; private FloatBuffer _vertexBufferStatic; private short _indicesArray = 0, 1, 2; private int _nrOfVertices = 3; private float _angle; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 35 页 - -
35、- - - - - - - Override public void onSurfaceCreated(GL10 gl, EGLConfig config) / preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); initTriangle(); initStaticTriangle(); Override public void onSurfaceChanged(GL10 gl, int w, int h) gl.glViewport(0, 0, w, h); public void setAngle(float angle) _
36、angle = angle; Override public void onDrawFrame(GL10 gl) / define the color we want to be displayed as the clipping wall gl.glClearColor(_red, _green, _blue, 1.0f); / reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); / clear the color buffer to show the ClearColor we
37、 called above. gl.glClear(GL10.GL_COLOR_BUFFER_BIT); / draw the static triangle gl.glColor4f(0f, 0.5f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBufferStatic); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBufferStatic); / set rotation for the non-
38、static triangle gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); private void initTriangle() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - -
39、- - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 35 页 - - - - - - - - - / float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder(); _vertexBuffer = vbb.asFloatBuffer(); / short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVert
40、ices * 2); ibb.order(ByteOrder.nativeOrder(); _indexBuffer = ibb.asShortBuffer(); float coords = -0.5f, -0.5f, 0f, / (x1, y1, z1) 0.5f, -0.5f, 0f, / (x2, y2, z2) 0f, 0.5f, 0f / (x3, y3, z3) ; _vertexBuffer.put(coords); _indexBuffer.put(_indicesArray); _vertexBuffer.position(0); _indexBuffer.position
41、(0); private void initStaticTriangle() / float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder(); _vertexBufferStatic = vbb.asFloatBuffer(); / short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(Byt
42、eOrder.nativeOrder(); _indexBufferStatic = ibb.asShortBuffer(); float coords = -0.4f, -0.4f, 0f, / (x1, y1, z1) 0.4f, -0.4f, 0f, / (x2, y2, z2) 0f, 0.4f, 0f / (x3, y3, z3) ; _vertexBufferStatic.put(coords); _indexBufferStatic.put(_indicesArray); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - -
43、 - 名师精心整理 - - - - - - - 第 14 页,共 35 页 - - - - - - - - - _vertexBufferStatic.position(0); _indexBufferStatic.position(0); public void setColor(float r, float g, float b) _red = r; _green = g; _blue = b; 复制代码如果作了以上这些,你会看到只有一个三角形可以旋转。如果你想两个都可以旋转,只需要在 “draw the static triangle”这个注释的旁边也给它加上一行代码就可以了。编译并运行
44、这个程序,你可以看到绿色的三角形在旋转,同时红色的三角形还是呆在原来的地方。这也充分验证了我们的答案,我们旋转的只是三角形而不是整个场景。Eclipse 工程源代码在这里下载: Vortex Part III 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 15 页,共 35 页 - - - - - - - - - - 这个系列的第四部分讲如何给三角形添加颜色。在上一部分我们创建了第二个静态的三角形来验证我们旋转的是三角形而不是整个场景。这里我们将这个静态的三角形删除掉。删除掉ini
45、tStaticTriangle() 函数,删除两个buffer,_indexBufferStatic和_vertexBufferStatic 。同时也要删除原来初始静止三角形时用到的onDrawFrame() 中的最后四行。新的 onDrawFrame()方法如下:Override public void onDrawFrame(GL10 gl) / define the color we want to be displayed as the clipping wall gl.glClearColor(_red, _green, _blue, 1.0f); / reset the matri
46、x - good to fix the rotation to a static angle gl.glLoadIdentity(); / clear the color buffer to show the ClearColor we called above. gl.glClear(GL10.GL_COLOR_BUFFER_BIT); / set rotation for the non-static triangle 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 16 页,共
47、 35 页 - - - - - - - - - gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); 复制代码现在我们为保存颜色信息创建一个新的buffer。这个 _colorBuffer是一个对象变量,但是我们需要在ini
48、tTriangle() 方法中定义颜色并填充这个buffer. / code snipped / a raw buffer to hold the colors private FloatBuffer _colorBuffer; / code snipped private void initTriangle() / float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder(); _vertexBuffer = vbb.
49、asFloatBuffer(); / short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder(); _indexBuffer = ibb.asShortBuffer(); / float has 4 bytes, 4 colors (RGBA) * number of vertices * 4 bytes ByteBuffer cbb = ByteBuffer.allocateDirect(4 * _nrOfVertices
50、* 4); cbb.order(ByteOrder.nativeOrder(); _colorBuffer = cbb.asFloatBuffer(); float coords = -0.5f, -0.5f, 0f, / (x1, y1, z1) 0.5f, -0.5f, 0f, / (x2, y2, z2) 0.5f, 0.5f, 0f / (x3, y3, z3) ; float colors = 1f, 0f, 0f, 1f, / point 1 0f, 1f, 0f, 1f, / point 2 0f, 0f, 1f, 1f, / point 3 ; _vertexBuffer.pu