Android通过Open GraphicsLibrary(OpenGL)来支持高性能的2D和3D图像,特别是OpenGL ES API.OpenGL是一款跨平台的图像API,它为3D图像处理指定了标准的软件接口.OpenGL ES是OpenGL中应用于嵌入式设备的分支.Android支持多个版本的OpenGL ES API,包括:
OpenGL ES 1.0和1.1 – 支持Android1.0及更高.
OpenGL ES 2.0 – 支持Android2.2(API8)及更高.
OpenGL ES 3.0 – Android4.3(API 18)及更高.
OpenGL ES 3.1 – Android5.0(API 21)及更高.
Android通过自己的framework和NDK来支持OpenGL.这里主要介绍Androidframework接口,关于NDK的可以查看AndroidNDK.在Android framework中有两个基础类可以让我们使用OpenGL ESAPI创建和操作图形: GLSurfaceView和GLSurfaceView.Renderer.如果我们想要在Android APP中使用OpenGL,那么如何在activity中使用这两个类便成了我们第一个要了解的事情:
GLSurfaceView
这个类是一个View,它和SurfaceView很像,让我们可以使用OpenGL的API来绘制和处理对象.使用该类的时候只需要创建一个GLSurfaceView的实例,然后将我们的Renderer传给它.但是如果我们想要截获点击事件的话,就得继承GLSurfaceView类,并实现它的点击监听器才行.
GLSurfaceView.Renderer
该接口定义了在GLSurfaceView中绘制图形的方法.我们必须这个接口作为一个独立的类,并通过GLSurfaceView.setRenderer()方法关联给GLSurfaceView.GLSurfaceView.Renderer接口需要我们实现这些方法:
onSurfaceCreated()
当GLSurfaceView创建的时候,系统会调用该方法一次.使用该方法来处理那些只需要执行一次的操作,比如设置OpenGL环境参数,或者初始化OpenGL图像对象.
onDrawFrame()
系统将会在GLSurfaceView每次重绘的时候调用该方法.应该使用该方法来绘制或者重绘图像对象.
onSurfaceChanged()
当GLSurfaceView的几何形状发生改变的时候,系统调用该方法.包括GLSurfaceView的尺寸变化或者设备屏幕的方向发生变化.比如从横屏变竖屏,该方法就会被调用.使用该方法来响应GLSurfaceView的容器的改变.
一旦我们已经确定在一个View中使用OpenGL ES,我们可以使用下列这些类来调用OpenGL 的API:
OpenGL ES 1.0/1.1 API包:
OpenGL ES 2.0 API类:
OpenGL ES 3.0/3.1 API包:
如果我们的APP需要使用的OpenGL功能并非支持所有的设备,那么就必须包含这些需求在AndroidManifest.xml文件中.这是一些常用的OpenGL manifest声明:
<!-- Tell the system this app requires OpenGL ES 2.0.-->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
增加这个声明的目的是Google play需要限制我们的APP不能安装在那些不支持OpenGLES 2.0的设备上.如果APP只能支持OpenGL ES 3.0,那么我们则需要这样指定:
对于OpenGL ES 3.0:
<!-- Tell the system this app requires OpenGL ES 3.0.-->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
对于OpenGL ES 3.1:
<!-- Tell the system this app requires OpenGL ES 3.1.-->
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
OpenGL ES 3.x的API对OpenGL ES2.0的API是兼容的,这意味着我们可以使用更加灵活的方法来使用OpenGL ES.我们可以在manifest中声明OpenGL ES 2.0,并且默认情况下使用该版本,但是可以在运行时查询设备是否支持3.x,如果支持就可以使用.
在Android上显示图形的时候一个很基本的问题就是,这些设备的屏幕经常是不同的大小和形状的.默认情况下OpenGL会假设一个正方形的屏幕,然后将图形绘制在一个通常不是正方形的屏幕上.
默认情况下OpenGL的坐标系(左)和映射在典型屏幕上之后的样子(右).可以看到图形的比例有所变化,为了解决这个问题,我们可以申请OpenGL的投影模式(projection mode)和相机view(camera view)来转变坐标,这样图形就可以显示为正确的比例了.
为了申请projection 和camera view,我们需要创建一个projection矩阵和一个camera view矩阵并且将它们应用到OpenGL渲染管道.Projection矩阵负责重新计算图形的坐标,使他们可以正确的映射到Android的设备屏幕上.Camera view矩阵从渲染对象的一个指定的视角创建一个变换的图形.
在ES 1.0 API中,我们通过创建对应的矩阵来使用projection和camera view,然后将它们添加到OpenGL环境中.
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
}
public void onDrawFrame(GL10 gl) {
...
// 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 camera view
GLU.gluLookAt(gl,0,0,-5,0f,0f,0f,0f,1.0f,0.0f);
...
}
在ES2.0和3.0的API中,我们使用projection和camera view的时候,首先要向图形对象的vertex shaders(顶点着色器)中添加一个矩阵成员.通过增加这个矩阵成员,我们就可以生成和应用projection和camera view了.
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of objects that use this vertex shader.
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// The matrix must be included as part of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
上面的栗子在顶点着色器中定义了一个变换矩阵成员.根据APP的需求,我们可以定义独立的projection矩阵和camera view矩阵,这样我们就可以独立的修改它们了.
public void onSurfaceCreated(GL10 unused,EGLConfig config) {
...
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
...
}
public void onSurfaceCreated(GL10 unused,EGLConfig config) {
...
// Create a camera view matrix
Matrix.setLookAtM(mVMatrix,0,0,0,-3,0f,0f,0f,0f,1.0f,0.0f);
}
public void onSurfaceChanged(GL10 unused,int width,int height) {
GLES20.glViewport(0,0,width,height);
float ratio = (float) width / height;
// create a projection matrix from device screen geometry
Matrix.frustumM(mProjMatrix,0,-ratio,ratio,-1,1,3,7);
}
public void onDrawFrame(GL10 unused) {
...
// Combine the projection and camera view matrices
Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mVMatrix,0);
// Apply the combined projection and camera view transformations
GLES20.glUniformMatrix4fv(muMVPMatrixHandle,1,false,mMVPMatrix,0);
// Draw objects
...
}
OpenGL ES 1.0和1.1从Android1.0就已经开始支持,从Android2.2开始,可以支持OpenGL ES2.0.从Android4.3开始支持OpenGL ES 3.x.它们都提供了稳定的用于创建3D游戏的图形接口.对于2.0和3.0版本的OpenGL,它们的图形编程过程很相似.3.0版本是2.0的超集.但是1.0/1.1和2.0/3.0有很大的差别.所以开发之前应该考虑以下的一些因素:
性能
通常,2.0和3.0比1.0/1.1提供了更好的图片处理性能.但是性能同样也跟设备有关系.
设备兼容性
开发者应该考虑到设备的类型,Android的版本和OpenGL ES的版本.
编码便利性
1.0/1.1提供了一个固定的功能管道和便利功能,这些东西在2.0/3.0中没有.所以新的OpenGL的开发者可能会发现在1.0/1.1上开发更加便利和简单.
图形控制
OpenGL ES 2.0/3.0通过使用可编程管道提供了一个更高等级的控制,通过这些,开发者们可以比在1.0/1.1中更容易的控制图形.
Texture 支持: OpenGL ES 3.0拥有对Texture压缩最好的支持.