注:近三篇转载中的视锥体部分结合着来看,再参照老罗的3d变换,基本可以初步理解和完成相关视锥体调整。
使用OpenGL开发Android应用详解系列三
【原创】转载请注明出处 我一家网 http://www.5yijia.com
前面两节主要介绍了一下OpenGL的基本概念,以及在Android开发中引入OpenGL时,Android项目的基本构成情况。这一节开始,我们通过具体的实例,来进行简单3D图形的描画。
注:代码基础还是采用上一节: 使用OpenGL开发Android应用详解系列二中使用的代码结构。
通过OpenGL来绘制简单立体图形
在进行绘制立体图形之前,我们先来了解一些相关术语,如果这些概念理解透彻的话,对Android中使用OpenGL开发很有帮助。
1. 相关术语理解
这里的术语包括: 坐标系,视锥体(frustum)和视窗(viewport)
(1)坐标系
为了制定三维空间内物体的位置,需要引入坐标系。下面是OpenGL的三维坐标系(注:和DirectX完全相反)
通过坐标系我们可以定义实际的物体,但是物体定义好了以后应该放在什么位置,接下来就是frustum来定义了.
2. 视锥体(frustum)
或者叫做视景,他主要用来定义物体可以表示的空间领域。
视锥体的视线方向是 -Z方向,也就是说,他是坐标轴中Z轴的反方向。请看下图:
通过从-Z方向作为视点来查看的话,视角(看的角度)与离得较近的切面,以及较远切面之间是成比例关系的.
(3)视窗(viewport)
上面图中那个蓝色的切面就是viewport,OpenGL中经常叫的视窗(viewport)。
2.OpenGL开发中使用视窗和视锥体
glViewport用来指定从标准设备的Window坐标的转换矩阵。矩阵变换主要指的是放大,缩小,旋转,截断,平行移动,线性变换的组合操作。
glViewport(GLint x,GLint y,GLsizei width,GLsizei height)为其函数原型。
参数说明:
x,y 以像素为单位,指定了视窗的左下角位置。
width,height 表示这个视窗矩形的宽度和高度,根据窗口的实时变化重绘窗口。
上面那个笑脸图像就是以左边图形的左下角的(x,y)坐标来投影右边图像的宽和高(width,height)。由于左边和右边的设备类别不同,导致投影后的图像变形了。为了防止变形,我们需要通过视锥体(frustum)的宽高比(aspect ratio),来让实际的设备保持和原有设备同样的比例。
在上面章节中讲过,设备大小或者纵横的方向改变时,会调用onSurfaceChanged方法,为了防止变形,我们可以通过下面的代码来控制.
@Override public void onSurfaceChanged(GL10 arg0, int width, int height) { arg0.glViewport(0, 0, width, height); arg0.glMatrixMode(GL10.GL_PROJECTION); arg0.glLoadIdentity(); GLU.gluPerspective(arg0, 45f,(float) width / height, 1f, 50f); // TODO Auto-generated method stub }
如果是直接绘制图形的话,opengl是没有空间的位置观念的,因为它根本不能分辨物体的前后关系。所以为了让OpenGL有前后位置的概念,我们需要使用深度缓冲区。
下面的代码设置Depth Test有效。
@Override public void onSurfaceCreated(GL10 arg0, EGLConfig config) { arg0.glEnable(GL10.GL_DEPTH_TEST); arg0.glDepthFunc(GL10.GL_LEQUAL); // TODO Auto-generated method stub }
3. 正方体的定义
接下来我们来定义一个正方体
package com.wuyijia.opengltest; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; public class MyOpenGLCube { //浮点缓冲区:批量get private final FloatBuffer mVertexBuffer; /** * 定义正方体坐标 */ public MyOpenGLCube(){ float vertices[] = { //正方体前面 -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, //正方体后面 -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, //正方体左面 -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, //正方体右面 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, //正方体上面 -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, //正方体底面 -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f }; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); } /** * 绘制正方体 * @param gl */ public void draw(GL10 gl){ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer); //正方体前面 gl.glNormal3f(0, 0, 1.0f); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //正方体后面 gl.glNormal3f(0, 0, -1.0f); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4); //正方体左面 gl.glNormal3f(-1.0f, 0, 0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4); //正方体右面 gl.glNormal3f(1.0f, 0, 0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4); //正方体上面 gl.glNormal3f(0, 1.0f, 0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4); //正方体底面 gl.glNormal3f(0, -1.0f, 0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4); } }
上面的类定义了一个以屏幕中心为正方形原点,边长为1的正方体。
4. 渲染正方体
接下来我们在onDrawFrame函数中渲染刚才我们定义的正方体。
MyOpenGLCube myOpenGLCube = new MyOpenGLCube(); @Override public void onDrawFrame(GL10 arg0) { arg0.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); arg0.glMatrixMode(GL10.GL_MODELVIEW); arg0.glLoadIdentity(); arg0.glTranslatef(0, 0, -3f); myOpenGLCube.draw(arg0); // TODO Auto-generated method stub }
OK,正方体渲染完成后,运行我们的应用,效果图如下:
我们发现运行的效果并不是立方体,而是一个正方形。原因是我们没有旋转立方体的缘故,所以看上去就像一个正方形。加上下面的代码再看一下效果。
@Override public void onDrawFrame(GL10 arg0) { arg0.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); arg0.glMatrixMode(GL10.GL_MODELVIEW); arg0.glLoadIdentity(); arg0.glTranslatef(0, 0, -3f); //旋转正方体 arg0.glRotatef(30f, 0, 1, 0); myOpenGLCube.draw(arg0); // TODO Auto-generated method stub }
运行后效果如下:
终于看到立体效果了。
转自:http://www.5yijia.com/?p=95