概述:
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的容器的改变.
OpenGL ES 包:
一旦我们已经确定在一个View中使用OpenGL ES, 我们可以使用下列这些类来调用OpenGL 的API:
OpenGL ES 1.0/1.1 API包:
android.opengl:这个包为OpenGL ES1.0/1.1类提供了静态接口, 并且比下面的javax.microedition.khronos包更加稳定. 包括GLES10,GLES10Ext, GLES11, GLES11Ext.
Javax.microediton.khronos.opengles:这个包提供了OpenGL ES 1.0/1.1的标准的实现. 包括GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack.
OpenGL ES 2.0 API类:
android.opengl.GLES20:这个包提供了OpenGL ES 2.0接口并且从Android2.2开始可用.
OpenGL ES 3.0/3.1 API包:
android.opengl:这个包提供了OpenGL ES 3.0/3.1类的接口. 3.0版本从Android4.3开始可用, 3.1版本则从Android5.0才开始可用. 包括GLES30, GLES31, GLES31Ext.
想了解更多的使用OpenGL ES创建app的信息, 可以参考这里.
声明OpenGL需求:
如果我们的APP需要使用的OpenGL功能并非支持所有的设备, 那么就必须包含这些需求在AndroidManifest.xml文件中. 这是一些常用的OpenGL manifest声明:
1. OpenGL ES version requirements– 如果我们的APP需要指定OpenGL ES的版本, 就必须声明requirement. 需要增加如下的设置到manifest中:
对于OpenGL ES 2.0:
<!-- 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, 如果支持就可以使用.
2. Texture compressionrequirements – 如果我们的APP需要使用texture compression, 那么就必须在manifest中用<support-gl-texture>声明需要支持的格式. 对此后文会有更加详细的介绍.
映射绘制对象的坐标(主要为了解决宽高比的问题):
在Android上显示图形的时候一个很基本的问题就是, 这些设备的屏幕经常是不同的大小和形状的. 默认情况下OpenGL会假设一个正方形的屏幕, 然后将图形绘制在一个通常不是正方形的屏幕上.
默认情况下OpenGL的坐标系(左)和映射在典型屏幕上之后的样子(右). 可以看到图形的比例有所变化, 为了解决这个问题, 我们可以申请OpenGL的投影模式(projection mode)和相机view(camera view)来转变坐标, 这样图形就可以显示为正确的比例了.
为了申请projection 和camera view, 我们需要创建一个projection矩阵和一个camera view矩阵并且将它们应用到OpenGL渲染管道. Projection矩阵负责重新计算图形的坐标, 使他们可以正确的映射到Android的设备屏幕上. Camera view矩阵从渲染对象的一个指定的视角创建一个变换的图形.
OpenGLES 1.0中的projection和camera view:
在ES 1.0 API中, 我们通过创建对应的矩阵来使用projection和camera view, 然后将它们添加到OpenGL环境中.
1. Projection矩阵: 创建一个Projection以通过屏幕的几何形状来重新计算对象的坐标, 以便它们可以被正确的绘制出来. 下面的栗子演示了如何基于屏幕的宽高比修改onSurfaceChanged()方法来创建Projection矩阵, 并将其应用于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 }
2. Camera transformation 矩阵: 一旦用projection算出了坐标系, 下一步就必须用到cameraview了. 下面的栗子演示了如何修改GLSurfaceView.Renderer的onDrawFrame()方法来应用一个model view和使用GLU.gluLookAt()工具来创建一个view模拟一个视角.
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); ... }
OpenGLES 2.0及更高版本中的projection和camera view:
在ES2.0和3.0的API中, 我们使用projection和camera view的时候, 首先要向图形对象的vertex shaders(顶点着色器)中添加一个矩阵成员. 通过增加这个矩阵成员, 我们就可以生成和应用projection和camera view了.
1. 向顶点着色器中添加一个矩阵– 为projection矩阵创建一个变量, 并把它作为着色器位置的乘数. 在下面的栗子中, uMVPMatrix成员变量让我们可以应用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矩阵, 这样我们就可以独立的修改它们了.
2. 访问着色器矩阵– 在顶点着色器中创建一个钩子之后, 我们就可以访问这个变量来应用projection和camera view矩阵. 下面的代码演示了如何修改GLSurfaceView.Renderer中的onSurfaceCreated()方法来访问上面栗子中的顶点着色器的矩阵变量.
public void onSurfaceCreated(GL10 unused, EGLConfig config) { ... muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); ... }
3. 创建projection和camera view矩阵– 生成图形对象要使用的projection和view矩阵. 下面的栗子演示了如何基于屏幕宽高比并通过GLSurfaceView.Renderer中的onSurfaceChanged()和onSurfaceCreated()方法来创建camera view矩阵和一个projection矩阵.
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); }
4. 应用projection和camera view矩阵– 想要应用projection和camera view变换, 需要将矩阵相乘后保存在顶点着色器中. 下面的栗子中演示了如何通过onDrawFrame()方法组合projection矩阵和camera view然后将它们应用于将要被OpenGL渲染的图形对象.
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的版本选择:
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压缩最好的支持.
参考: https://developer.android.com/guide/topics/graphics/opengl.html