本文译自:http://developer.android.com/guide/topics/graphics/opengl.html
Android系统包含了OpenGL(Open Graphics Library),从而给2D和3D图形提供了高性能的支持,尤其是OpenGL ES API。OpenGL是一个跨平台图形API,它给3D图形处理指定了一个标准的软件接口。OpenGL ES是一种用于嵌入式设备的OpenGL规范。从Android1.0开始,就提供了对OpenGL ES1.0和1.1API规范的支持。从Android2.2(API Level 8)开始,框架开始支持OpenGL ES2.0 API规范。
注意:由Android框架所指定的API有点类似J2ME的JSR239 OpenGL ES API,但是它们还是有区别的,如果你熟悉J2ME的JSR239规范,就要小心其中的变化。
基础
Android通过它框架API和NDK(Native Development Kit)来支持OpenGL。本文主要关注Android框架接口,关于NDK的更多信息,请看Android NDK(http://developer.android.com/sdk/ndk/index.html)
在Android框架中有两个OpenGL ES API类库用来创建和维护图形:GLsurfaceView和GLSurfaceView.Renderer。如果是要在应用程序中使用OpenGL,那么了解在一个Activity如何实现这些类应该是第一目标。
GLSurfaceView
这个类是一个View,在这个类中能够绘制和维护使用OpenGL API调用的对象,并且它跟SurfaceView类的功能类似。通过创建GLSurfaceView类的一个实例,并给它添加一个Renderer对象来使用这个类。但是,如果想要捕获触屏事件,那么就应该继承GLSurfaceView类并实现它的触屏监听器。示例程序TouchRotateActivity(http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity.html)。
GLSurfaceView.Renderer
这个接口定义了在一个OpenGL的GLSurfaceView类中绘制图形所必须的方法。必须提供一个单独的类来实现这个接口,并且使用GLSurfaceView.setRenderer()方法把它绑定到GLSurefaceView类的实例上。
以下是必须实现的GLSurfaceView.Renderer接口方法:
1.onSurfaceCreated()
在创建GLSurfaceView对象时,系统会调用一次这个方法。使用这个方法来执行那些只需要发生一次的动作,如设置OpenGL环境参数或初始化OpenGL图形对象。
2.onDrawFrame()
每次重绘GLSurefaceView对象时,系统都会调用该方法。使用这个方法作为绘制图形(包括图形重绘)对象的初始执行点。
3.onSurefaceChanged()
当GLSurfaceView对象的几何图形发生改变时(包括尺寸或设备屏幕方向的改变等),系统会调用这个方法。例如,当设备从纵向改到横向时,系统就会调用这个方法。使用这个方法来响应GLSurfaceView容器中的改变。
本文译自:http://developer.android.com/guide/topics/graphics/opengl.html
OpenGL 包
一旦使用GLSurfaceView和GLSurfaceView.Renderer类给OpenGL建立了一个View容器,那么就可以开始使用以下的类来调用OpenGL的API:
1.OpenGL ES 1.0/1.1 API 包
android.opengl------这个包给OpenGL ES 1.0/1.1提供了一个静态的接口,并且比javax.microedition.khronos包接口具有更好的性能。
GLES10(http://developer.android.com/reference/android/opengl/GLES10.html)
GLES10Ext(http://developer.android.com/reference/android/opengl/GLES10Ext.html)
GLES11(http://developer.android.com/reference/android/opengl/GLES11.html)
GLES10Ext(http://developer.android.com/reference/android/opengl/GLES10Ext.html)
javax.microedition.khronos.opengles------这包提供了OpenGL ES 1.0/1.1的标准实现
GL10(http://developer.android.com/reference/javax/microedition/khronos/opengles/GL10.html)
GL10Ext(http://developer.android.com/reference/javax/microedition/khronos/opengles/GL10Ext.html)
GL11(http://developer.android.com/reference/javax/microedition/khronos/opengles/GL11.html)
GL11Ext(http://developer.android.com/reference/javax/microedition/khronos/opengles/GL11Ext.html)
GL11ExtensionPack(http://developer.android.com/reference/javax/microedition/khronos/opengles/GL11ExtensionPack.html)
2.OpenGL ES 2.0 API 类
android.opengl.ELES20------这个包提供OpenGL ES 2.0的接口,并且从Android2.2(API Level 8)开始有效。
如果想要立即创建与OpenGL有关的应用程序,请看OpenGL ES 1.0或OpenGL ES 2.0开发指南。
OpenGL ES 1.0:http://developer.android.com/resources/tutorials/opengl/opengl-es10.html
OpenGL ES 2.0:http://developer.android.com/resources/tutorials/opengl/opengl-es20.html
声明OpenGL的需求
如果应用程序使用的OpenGL功能不是在所有的设备上都是有效的,那么就必须在该应用的AndroidManifest.xml文件中包含这些功能需求。
通常OpenGL清单的声明如下:
1.OpenGL ES的版本需求---如果应用程序只支持OpenGL ES 2.0,那么就必须通过把下列设置添加到清单中来声明该需求:
<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-featureandroid:glEsVersion="0x00020000"android:required="true"/>
添加这个声明后,会导致Google Play限制该应用程序安装在那些不支持OpenGL ES 2.0的设备上。
2.纹理压缩的需求---如果应用程序使用了纹理压缩格式。就必须在该应用的清单文件中使用<supports-gl-texture>元素来声明其所支持的格式。关于有效的纹理压缩格式的更多信息,请看本文后续的“纹理压缩支持”
在应用程序清单中声明纹理压缩需求会把该应用程序从那些不支持其声明的压缩格式的设备上隐藏掉(只要有一种格式不被支持)。
绘图对象的坐标映射
在Android设备上显示图形中的基本问题之一就是它们的屏幕可能是各种尺寸和形状的。OpenGL会假设一个方形区域,统一的坐标区域,并且在默认情况下,在一些方形的屏幕能够像在方形区域一样,完美的绘制这些坐标。
图1. 默认的OpenGL坐标系统(左)被映射到一个典型的Android设备屏幕(右)。
上面的插图演示了如何把左边的OpenGL的统一坐标系统映射到右边横屏设备的实际坐标上。使用GpenGL的投影模式,可以解决这个问题,并且照相视图(Camera View)会变换坐标,以便图形对象能够按照正确的比例来显示图形。
为了应用投影和照相视图,就要创建一个投影矩阵和一个照相视图矩阵,并且把它们应用到OpenGL的呈现管道。投影矩阵会重新计算图形的坐标,以便让图形能够正确的映射到Android设备的屏幕上。照相视图矩阵会从一个特殊的视角来创建一个要呈现的对象的变换。
在OpenGL ES 1.0中的投影和照相视图
在ES 1.0 API中,要分别创建投影和照相视图,并把它们添加到OpenGL的环境中。
1.投影矩阵---为了重新计算图形对象的坐标以便让它们能够按照正确的比例来绘制,就要使用设备屏幕的几何形状来创建一个投影矩阵。下面的示例代码演示了如何编辑GLSurfaceView.Renderer的onSurfaceChanged()方法,来实现基于屏幕的外观比率的投影矩阵的创建,并且把这个投影矩阵应用给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.照相变换矩阵---一旦使用一个投影矩阵调整了坐标系统,就必须要使用一个照相视图。下面的代码显示如何使用GLSurfaceView.Renderer的onDrawFrame()方法来实现一个模型视图,并且使用GLU.gluLookAt()工具来创建一个模拟照相机视角的视图变换:
publicvoid 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);
...
}
关于如何使用OpenGL ES 1.0投影和照相视图的完整的例子,请看OpenGL ES 1.0指南(http://developer.android.com/training/graphics/opengl/index.html)
本文译自:http://developer.android.com/guide/topics/graphics/opengl.html
在OpenGL ES 2.0中的投影和照相视图
在ES 2.0的API中,首先要通过把一个矩阵成员添加给图形对象顶部的着色器(Vertex Shaders)来使用投影和照相视图。然后,这个被添加的矩阵成员能够生成并把投影和照相视图应用给图形对象。
1.给顶层着色器(Vertex Shaders)添加矩阵---给视图投影矩阵创建一个有效的,并且要把它作为着色位置的倍增器。在下面的代码中,包含的uMVPMatrix成员允许把投影和照相视图的矩阵应用到使用该着色器的图形对象的坐标上:
privatefinalString 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
" gl_Position = uMVPMatrix * vPosition; \n"+
"}\n";
注意:在上例的顶层着色器(Vertex Shader)中定义了一个单一变换矩阵,你可以把投影矩阵和照相视图矩阵一起组合到这个变换矩阵中。根据应用程序的需要,也可以在顶层着色器(Vertex Shader)中分别定义投影矩阵和照相视图矩阵,这样就可以分别对它们进行改变。
2.访问着色器矩阵---在顶层着色器(Veterx Shader)中创建了一个应用于投影和照相视图的回调之后,就能够访问这个应用于投影矩阵和照相视图矩阵的变量。下面的代码显示了如何编辑GLSurfaceView.Renderer类onSurfaceCreated()方法,来实现对定义在Vertex Shader中的矩阵变量的访问。
publicvoid onSurfaceCreated(GL10 unused,EGLConfig config){
...
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
...
}
3.创建投影和照相视图矩阵---生成应用与图形对象的投影和视图矩阵。下列代码显示了如何编辑GLSurfaceView.Renderer类的onSurfaceCreated()和onSurfaceChanged()方法,来实现基于设备的屏幕外观比率的照相视图矩阵和投影矩阵的创建:
publicvoid 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);
}
publicvoid 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.应用投影和照相视图矩阵---要适用投影和照相视图的变换,就要把它们一起设置给Verter Shader。下面的代码显示了如果编辑GLSurfaceView.Render类的onDrawFrame()方法,来实现上面代码中创建的投影矩阵和照相视图的合成,然后,把它应用到由OpenGL来呈现的图形对象上。
publicvoid 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 2.0的完整的示例,请看OpenGL ES 2.0指南(http://developer.android.com/training/graphics/opengl/index.html)
本文译自:http://developer.android.com/guide/topics/graphics/opengl.html
形状的外观和折点
在OpenGL中,形状的外观是由三个或三维空间中更多的点来定义的一个表面。一个三个或更多的三维点(在OpenGL中叫做顶点)的集合有前面和后面之分。如何知道哪个面是前,哪个面是后呢?就要用折点或定义形状中的点的方向来回答这个问题。
图1.图示显示了变换成逆时针绘图顺序的坐标列表。
在这个例子中,三角形的点是按照它们被逆时针方向绘制的顺序来定义的。这些绘图用坐标的顺序给图形定义了折点的方向。默认情况下,在OpenGL中被逆时针绘制的那个面就是前面。图1.所示的三角形定义,现在看到的是该图形的正面(就像OpenGL所解释的那样),而其他的边都是背面。
为什么了解图形的那个面是正面是重要的呢?这与OpenGL中被叫做面选取(Face Culling)的功能有关。面选取(Face Culling)是OpenGL环境的一个可选功能,它允许呈现管道在保存、内存和处理周期中忽略(不计算或绘制)对图形背面的处理:
// enable face culling feature
gl.glEnable(GL10.GL_CULL_FACE);
// specify which faces to not draw
gl.glCullFace(GL10.GL_BACK);
如果试图使用面选取(Face Culling)功能,但却不知道那些边是形状的正面和反面,那么OpenGL图形看起来会有点薄,或者图形根本就不会显示。因此,要始终用逆时针的顺序定义OpenGL图形的坐标。
注意:也可以使用顺时针的顺序作为正面来设置OpenGL的环境,但是这需要更多的代码,并且会给OpenGL开发者照成混乱,因此不要这么做。