分类: 嵌入式
2011-12-18 13:38:04
这几篇Android 3D游戏开发的文章原文出自一位德国人Martin在droidnova.com写的文章,有lixinso翻译为中文。
这个系列的第六部分主要是关于如何创建正确的视角,因为如果没有正确的视角,3D就没有任何意义。
在开始之前我们需要先讨论一下OpenGL提供的这两种view:正交和投影。
正交Orthographic (无消失点投影)
正
交视图无法看到一个物体是远离自己还是正在我们面前。为什么?因为它不会根据距离收缩。所以如果你如果你画一个固定大小的物体在视点前面,同时画一个同样
大小的物体在第一个物体的远后方,你无法说那个物体是第一个。因为两个都是一样的大小,根距离无关。他们不会随着距离而收缩。
透视Perspective (有消失点投影)
透
视视图和我们从眼睛看到的视图是一样的。例如,一个高个子的人站在你面前,你看上去是很高的。如果这个人站在100米以外,他甚至还没有你的拇指大。他看
上去会随着距离而缩小,但是我们实际上都知道,它依然是个高个子。这种效果叫做透视。上面例子中提到的两个物体,第二个物体将会显示地更小,所以我们可以
区分哪个是离我们近的物体,那个是离我们远的物体。
因为我的例子或许让你会迷惑。我再次推荐这个blog帖子:iPhone development: OpenGL ES From the Ground Up, Part 3: Viewports in Perspective,这里面使用或者轨道作为例子。
我 们想创建的第一个view是哟娜orthographic。这个创建过程只需要一次,就是在每次surface被created的时候。所以我们需要改一 下代码。一些在onDrawFrame()中的方法将要被转移到onSurfaceCreated()方法中。这样他们只会当程序启动或者旋转的时候被执 行。
你可以看到我们没有将glClear()和glLoadIdentity()方法从onDrawFrame()移动到onSurfaceCreate中。原因很简单:他们会在每一帧都被绘制。
因为我们需要使用屏幕大小来计算屏幕的比例,所以我们引入了两个对象变量:_width和_height。我们需要在onSurfaceChanged()方法中设置,在每次旋转改变的时候调用。
现在我们已经有了启动一个视点所需要的所有东西。我们需要改变一下onSurfaceChanged()方法。
Wow,现在有了好多的新代码,别怕,我们一步一步来。
在 第三行,我们可以看到glMatrixMode(),使用GL10.GL_RPOJECTION作为参数,在第8行我们可以再次看到这个方法,但是是使用 GL10.GL_MODELVIEW作为变量。这样使用的原因是3-8行中间的代码。在这几行中,4-7行设置了我们的视点,所以我们设置了我们的投影。 在9-17行,我们设置了我们的模型环境。在这中上下文中,两种调用使用不同的参数应该是可理解的。Tips:经常可以试着去删除掉一些代码行来看一下结 果。这样比较容易明白哪些代码行是起什么作用的。
第四行计算下一样需要的屏幕比率。在这行中(6行)我们设置我们的视点来做orthographic view。这些参数是为边界设定,顺序是这样的:left, right, bottom, top, zNear, zFar。
在第7行我们设置了视点。我们知道这个方法的用法因为我们已经在onSurfaceChanged()中使用过了。
在第8行我们切换了MatrixMode到GL10.GL_MODELVIEW,设置OpenGL接受关于改变model绘制方式的调用。
第
9行我们调用了glEnable()并使用参数GL10.GL_DEPTH_TEST.这使OpenGL
ES检查对象的z-order。如果我们没有enable它,我们将看到最后被绘制的对象一直显示在最前面。这意味着,及时即使这个物体本来应该被更近更
大的物体遮盖,我们依然可以看到它。
其它代码行我们已经在前面的几篇中已经介绍过了。
透视视图与之相同,只是不 同的地方是不是使用glOrthof()而是使用glFrustumf(). glFrustumf()函数的参数和glOrthof()的参数略有不同。因为我们没有缩小物体,但是我们定义的锥体将被漏斗状切开。看一下这个图片来 了解一下glOrthof()和glFrustumf()的区别。
回到我们的代码:
Information: 我们要记住计算变量大小(5行),我们将会看到当我们讨论矩阵的时候它为什么可以用。
在第8行我们使用glFrustumf()替代glOrthof().这是我们在orthographic view 和perspective view之前切换时需要做的所有的变化。
但是,hey,还有最后一项我们就能看到结果了。OK,让我们改一下onDrawFrame()方法吧。
OK,我们改变了什么?
第三行,我们修改了参数确保depth buffer也会被清除。
在第9行,我们开始一个循环来创建10个物体。
在 第10行,我们看到glLoadIdentity(). 现在它在这儿重设矩阵。 这是必须做的因为我们要使用glRotatef()和glTranslatef()来修改我们的物体。但是为了确保我们只修改了当前循环到的物体,我们调 用glLoadIndentify()。所以我们重设每一个对之前物提的glTranslatef()和glRotatef()调用。
在 11行我们看到新的glTranslatef()方法,将我们的物体移动到另外一个位置。在这个例子中,我们不修改x轴上的位置。但是我们修改y轴上的 -1.0f,这表示它接近我们的屏幕低端。最后的计算你可以看到,只是修改z轴的位置,表示物体的神对。第一个物体在-2.5f,第二个在-4.0f等 等。所以我们将10个物体摆放在了屏幕的中央。
如果你使用glFrustumf()调用,你看到的是这种结果:
如果你切换glFrustumf()和glOrthof() ,你将会看到这种结果:
Hey等一下,为什么只看到一个物体?因为在正交情况下我们没有任何的投影。所以每一个独体的物体都有相同的大小所以没有缩放,所以没有任何消失的点,所以实际上所有的物体都在第一个物体的后面。
源代码: Vortex Part VI