OpenGL ES的1.0
本教程演示了如何创建一个简单的Android应用程序使用的OpenGL ES 1.0 API来执行一些基本图形操作。您将学习如何:
- 使用
GLSurfaceView
和GLSurfaceView.Renderer
创建活动 - 创建和绘制图形对象
- 定义一个投影屏幕几何纠正
- 定义相机视图
- 旋转的图形对象
- 使图形触摸互动
Android框架的OpenGL ES 1.0/1.1和OpenGL ES 2.0 API的支持。您应该仔细考虑的OpenGL ES API(1.0/1.1或2.0)的版本是最适合您的需要 。欲了解更多信息,请参阅 选择OpenGL API的版本。如果您希望使用的OpenGL ES 2.0,OpenGL ES 2.0的教程。
在开始之前,您应该了解如何创建一个基本的Android应用程序 。如果你不知道如何创建一个应用程序,后续的Hello World教程将使你熟悉这个过程。
创建与GLSurfaceView活动
要开始使用OpenGL,你必须实现一个GLSurfaceView
和 GLSurfaceView.Renderer
。GLSurfaceView
是主视图类型的应用程序,使用OpenGL和GLSurfaceView.Renderer
控制在该视图中绘制的是什么。(欲了解更多有关这些类的信息,请参阅与 OpenGL的3D文件。)
使用GLSurfaceView
创建活动:
- 启动一个新的Android项目,该项目目标的Android 1.6(API等级4)或更高。
- 将项目命名为HelloOpenGLES10和确保它包括一个称为
HelloOpenGLES10
活动 。 - 修改
HelloOpenGLES10
类如下:package com.example.android.apis.graphics; import android.app.Activity; import android.content.Context; import android.opengl.GLSurfaceView; import android.os.Bundle; public class HelloOpenGLES10 extends Activity { private GLSurfaceView mGLView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a GLSurfaceView instance and set it // as the ContentView for this Activity. mGLView = new HelloOpenGLES10SurfaceView(this); setContentView(mGLView); } @Override protected void onPause() { super.onPause(); // The following call pauses the rendering thread. // If your OpenGL application is memory intensive, // you should consider de-allocating objects that // consume significant memory here. mGLView.onPause(); } @Override protected void onResume() { super.onResume(); // The following call resumes a paused rendering thread. // If you de-allocated graphic objects for onPause() // this is a good place to re-allocate them. mGLView.onResume(); } } class HelloOpenGLES10SurfaceView extends GLSurfaceView { public HelloOpenGLES10SurfaceView(Context context){ super(context); // Set the Renderer for drawing on the GLSurfaceView setRenderer(new HelloOpenGLES10Renderer()); } }
-
注:你会得到一个
HelloOpenGLES10Renderer
类引用的编译错误。这是预期的结果;下一步将修复这个错误。如上图所示,此活动使用单一
GLSurfaceView作为其视图
。请注意,这个活动实现暂停和恢复的关键生命周期的回调 。HelloOpenGLES10SurfaceView类的代码
在上面的例子中仅仅是GLSurfaceView的一个实例
,因此不是例子必须的。不过,如果你想您的应用程序能够监测和响应触摸屏事件(我们猜测你会这样做),你做你必须继承GLSurfaceView来添加触摸事件侦听器,您将会在Reponding to Touch Events中学习如何做。为了在
GLSurfaceView绘制图形
,你必须定义一个继承GLSurfaceView.Renderer的类
。在接下来的步骤,您将创建一个renderer类来完成这个OpenGL应用程序。 - 创建一个新的
文件
HelloOpenGLES10Renderer类,它实现GLSurfaceView.Renderer 接口 :package com.example.android.apis.graphics; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class HelloOpenGLES10Renderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Set the background frame color gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); } public void onDrawFrame(GL10 gl) { // Redraw background color gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); } }
-
这继承
GLSurfaceView.Renderer接口使用OpenGL绘图方法而必须
提供的最简代码结构 :-
onSurfaceCreated()
被调用一次当GLSurfaceView
创建后。 -
onDrawFrame()
当GLSurfaceView重绘时被调用
。 -
onSurfaceChanged()
被调用当GLSurfaceView发生的几何
的变化,例如当设备的屏幕方向的变化。
如需有关这些方法的更多信息,请参阅与 OpenGL的 3D文档。
-
上面的代码示例创建了一个简单的Android应用程序显示一个灰色的屏幕,使用OpenGL ES 1.0调用。虽然这个应用程序没有做任何事情非常有趣,通过创建这些类,你必须奠定开始绘制与OpenGL ES 1.0的图形元素所需的基础。
如果你是熟悉的OpenGL ES API的,这些类应该给你足够的信息来使用的OpenGL ES 1.0 API和创建图形。但是,如果您需要更多的帮助开始使用OpenGL的头下一节一些更多的提示。
画上GLSurfaceView的形状
一旦你已经实现了
GLSurfaceView.Renderer
,下一步就是绘制它的东西 。本节说明如何定义和画一个三角形。定义一个三角形
OpenGL允许您使用在三维空间的坐标来定义对象。所以,在你可以画一个三角形之前,你必须定义它的坐标。在OpenGL中,要做到这一点的典型方式是定义坐标的顶点数组。
默认情况下,OpenGL ES的假设了一个坐标系,就是[0,0,0](X,Y,Z)指定为GLSurfaceView框架的
中心
,[1,1,0]是框架的右上角和[ - 1,-1,0]是框架的左下角。定义一个三角形的顶点数组:
- 在你的
HelloOpenGLES10Renderer
类中,添加新的成员变量,包含一个三角形状的 顶点:private FloatBuffer triangleVB;
- 创建一个方法,
initShapes()用以
填充该成员变量:
这种方法定义了一个三边相等的立体三角形。private void initShapes(){ float triangleCoords[] = { // X, Y, Z -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 }; // initialize vertex Buffer for triangle ByteBuffer vbb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) triangleCoords.length * 4); vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer triangleVB.position(0); // set the buffer to read the first coordinate }
- 修改
onSurfaceCreated()
方法来初始化您的三角形:public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Set the background frame color gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // initialize the triangle vertex array initShapes(); }
注意:形状和其他静态对象在onSurfaceCreated()初始化一次,以便获得最佳性能 。避免在
onDrawFrame()初
新的对象,因为这会导致系统重新创建每个重绘对象,从而减慢您的应用程序。
现在已经定义了一个三角形的形状,但如果您运行的应用程序后,什么也没有出现。什么?您应该告诉OpenGL绘制三角形,你将会在下一节学会如何做。
画出三角形
你必须告诉OpenGL您使用的顶点数组,在你可以画出你的三角形之前。在这些步骤后,你可以调用绘图API来显示三角形。
绘制三角形:
-
glEnableClientState()
方法必须添加到onSurfaceCreated()的底部
,使得可以使用顶点数组。
经过这一步,您已经准备在OpenGL视图中绘制三角形的对象。// Enable use of vertex arrays gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- 下面的代码添加到您的
onDrawFrame()
方法末尾,以绘制的三角形。// Draw the triangle gl.glColor4f(0.63671875f, 0.76953125f, 0.22265625f, 0.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
- 运行的应用程序!你的程序应该是这个样子:
有这个例子中的几个问题。首先,它没有给你友好的印象。其次,三角形是有点压扁,还有当您更改设备的屏幕方向时将改变其形状。它的形状歪斜的原因,是因为这个对象渲染在一个不是正方形的屏幕上。您将在下一节中使用投影和相机视图解决这一问题。
最后,因为三角形是固定的,系统在完全相同的地方重绘对象多次,这方法不是最有效地利用OpenGL图形的方法。在Add Motion节,你会做出这种形状的旋转,从而证明这种使用方法的处理能力 。
应用投影和相机视图
显示图形的基本问题之一是显示,Android设备通常不是方形的,默认情况下,OpenGL的希望您画在一个正方形上,以便能够在不是方形的屏幕上使用统一的坐标系统。为了解决这个问题,你可以申请一个OpenGL投影模式和相机视图(眼点)来改变图形对象的坐标,使他们有正确的比例在任何显示器上 。关于OpenGL的坐标映射的详细信息,请查阅 Mapping Coordinates for Drawn Objects.
运用投影和相机视图转换到您的三角形:
- 修改您的
onSurfaceChanged()
的方法使支持GL10.GL_PROJECTION模式,计算屏幕比例和应用作为改造对象的坐标的比例。public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); // make adjustments for screen ratio float ratio = (float) width / height; gl.glMatrixMode(GL10.GL_PROJECTION); // set matrix to projection mode gl.glLoadIdentity(); // reset the matrix to its default state gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); // apply the projection matrix }
- 接下来,修改您
的onDrawFrame(
)方法来套用
GL_MODELVIEW模式,并使用 GLU.gluLookAt ()设置一个眼点。public void onDrawFrame(GL10 gl) { // Redraw background color gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Set GL_MODELVIEW transformation mode gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // reset the matrix to its default state // When using GL_MODELVIEW, you must set the view point GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Draw the triangle ... }
- 运行更新的应用程序,你应该看到像这样:
现在,您已经应用了这种转变,三角形的三个边都相等的,而不是早期版本中的压扁的三角形 。
添加运动
虽然十分激动用OpenGL ES创建了一个静态图形对象,没准你想至少有一些对象移动 。在本节中,您将添加运动到您的三角形旋转。
要添加您的三角形旋转:
- 修改您
onDrawFrame()
方法,以旋转的三角形对象 :public void onDrawFrame(GL10 gl) { ... // When using GL_MODELVIEW, you must set the view point GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Create a rotation for the triangle long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); // Draw the triangle ... }
- 运行应用程序和您的三角形应该围绕其中心旋转。
响应触摸事件
像旋转的三角形那样按照预设程序使物体移动是可以得到一些注意,但你是否想用户拥有与OpenGL图形的交互呢?在本节中,您将学习如何监听触摸事件,让用户能够和你的 HelloOpenGLES10SurfaceView对象
交互
。使您的OpenGL应用程序触摸互动的关键是继承
GLSurfaceView覆盖onTouchEvent
()方法来监听触摸事件。然而,在你开始之前,你要修改的渲染类显示三角形的旋转角度。之后,您将修改HelloOpenGLES10SurfaceView
处理触摸事件并把这些数据传递到您的渲染类。使您的旋转三角形响应触摸事件:
- 修改您
的
HelloOpenGLES10Renderer类,加入一个新的公共成员,
使您的HelloOpenGLES10SurfaceView类是能够传递新的旋转值到您的渲染:public float mAngle;
- 在您
onDrawFrame()
方法,注释掉代码产生的一个角度,更换新角度
变量mAngle
。// Create a rotation for the triangle (Boring! Comment this out:) // long time = SystemClock.uptimeMillis() % 4000L; // float angle = 0.090f * ((int) time); // Use the mAngle member as the rotation value gl.glRotatef(mAngle, 0.0f, 0.0f, 1.0f);
- 在你的
HelloOpenGLES10SurfaceView
类中,添加下面的成员变量 。private final float TOUCH_SCALE_FACTOR = 180.0f / 320; private HelloOpenGLES10Renderer mRenderer; private float mPreviousX; private float mPreviousY;
-
HelloOpenGLES10SurfaceView
构造方法,设置mRenderer
成员让您拥有一个处理旋转输入和设置渲染模式RENDERMODE_WHEN_DIRTY的类
。public HelloOpenGLES10SurfaceView(Context context){ super(context); // set the mRenderer member mRenderer = new HelloOpenGLES10Renderer(); setRenderer(mRenderer); // Render the view only when there is a change setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); }
- 在你的
HelloOpenGLES10SurfaceView
类中,重写onTouchEvent(
)方法来侦听触摸事件并将它们传递给你的渲染 。@Override public boolean onTouchEvent(MotionEvent e) { // MotionEvent reports input details from the touch screen // and other input controls. In this case, you are only // interested in events where the touch position changed. float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - mPreviousX; float dy = y - mPreviousY; // reverse direction of rotation above the mid-line if (y > getHeight() / 2) { dx = dx * -1 ; } // reverse direction of rotation to left of the mid-line if (x < getWidth() / 2) { dy = dy * -1 ; } mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR; requestRender(); } mPreviousX = x; mPreviousY = y; return true; }
注:触摸事件返回该像素的坐标于OpenGL坐标是不一样的 。触摸坐标[0,0]是屏幕的左下角和[max_X,max_Y]是屏幕右上角的最高值。为了配合触摸事件OpenGL的图形对象,您必须转换触摸坐标为OpenGL的坐标。
- 运行应用程序和你的手指或光标在屏幕上拖动,旋转的三角形。
对于OpenGL的触摸事件功能的另一个例子,请看TouchRotateActivity.
-
-