Android 图形学原理之OpenGL ES
Android包括使用Open Graphics Library(OpenGL®)支持高性能2D和3D图形,特别是OpenGL ES API。 OpenGL是一种跨平台图形API,为3D图形处理硬件指定标准软件接口。 OpenGL ES是针对嵌入式设备的OpenGL规范的一种风格。 Android支持多种版本的OpenGL ES API:
- OpenGL ES 1.0 and 1.1:Android 1.0 开始支持
- OpenGL ES 2.0:Android 2.2 api 8 开始支持
- OpenGL ES 3.0:Android 4.3 api 18 开始支持
- OpenGL ES 3.1:Android 5.0 api 21 开始支持
在设备上支持OpenGL ES 3.0 API需要实现设备制造商提供的此图形管道。运行Android 4.3或更高版本的设备可能不支持OpenGL ES 3.0 API。有关检查运行时支持的OpenGL ES版本的信息,请参阅检查OpenGL ES版本。下面会介绍如果检查OpenGL ES支持的版本信息。
Android通过其框架API和Native Development Kit(NDK)支持OpenGL。本主题重点介绍Android框架接口。有关NDK的更多信息,请参阅Android NDK。
1.概述
Android框架中有两个基础类,可让您使用OpenGL ES API创建和操作图形:GLSurfaceView和GLSurfaceView.Renderer。如果您的目标是在Android应用程序中使用OpenGL,那么了解如何在活动中实现这些类应该是您的首要目标。
GLSurfaceView:此类是一个View,您可以使用OpenGL API调用绘制和操作对象,其功能类似于SurfaceView。您可以通过创建GLSurfaceView实例并将Renderer添加到其中来使用此类。但是,如果要捕获触摸屏事件,则应扩展GLSurfaceView类以实现触摸侦听器,如OpenGL培训课程“响应触摸事件”中所示。
GLSurfaceView.Renderer:此接口定义在GLSurfaceView中绘制图形所需的方法。您必须将此接口的实现作为单独的类提供,并使用GLSurfaceView.setRenderer()将其附加到GLSurfaceView实例。
GLSurfaceView.Renderer 接口要求你必现实现如下的方法:
onSurfaceCreated(...):在创建GLSurfaceView时,系统会调用此方法一次。使用此方法执行仅需要发生一次的操作,例如设置OpenGL环境参数或初始化OpenGL图形对象。
onSurfaceChanged(...):当GLSurfaceView几何体发生变化时,系统会调用此方法,包括GLSurfaceView大小的更改或设备屏幕的方向。例如,当设备从纵向更改为横向时,系统会调用此方法。使用此方法响应GLSurfaceView容器中的更改。
onDrawFrame(...):系统在每次重绘GLSurfaceView时调用此方法。使用此方法作为绘制(和重新绘制)图形对象的主要执行点。
2.OpenGL ES包介绍
使用GLSurfaceView和GLSurfaceView.Renderer为OpenGL ES建立容器视图后,可以使用以下类开始调用OpenGL API:
目前OpenGL ES 3.X主要在android.opengl下面,android.opengl包下面提供了opengl的静态接口和实体。
关键的类主要有
- GLES30.java
- GLES31.java
- GLES31Ext.java
- GLES32.java
3.声明OpenGL的要求
如果您的应用程序使用的是所有设备都不具备的OpenGL功能,则必须在AndroidManifest.xml文件中包含这些要求。以下是最常见的OpenGL清单声明:
3.1 OpenGL ES 版本要求
如果您的应用程序需要特定版本的OpenGL ES,则必须通过向清单添加以下设置来声明该要求,如下所示。
<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
添加此声明会导致Google Play限制您的应用程序安装在不支持OpenGL ES 2.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.x API向后兼容2.0 API,这意味着您可以更灵活地在应用程序中实现OpenGL ES。通过在清单中声明OpenGL ES 2.0 API作为要求,您可以将该API版本用作默认值,在运行时检查3.x API的可用性,如果设备支持则使用OpenGL ES 3.x功能它。有关检查设备支持的OpenGL ES版本的更多信息,请参阅检查OpenGL ES版本。
3.2 texture压缩要求
如果您的应用程序使用纹理压缩格式,则必须使用<supports-gl-texture>在清单文件中声明应用程序支持的格式。有关可用纹理压缩格式的详细信息,请参阅纹理压缩支持。
在清单中声明纹理压缩要求会使用不支持至少一种声明压缩类型的设备的用户隐藏应用程序。有关Google Play过滤如何用于纹理压缩的详细信息,请参阅<supports-gl-texture>文档的Google Play和纹理压缩过滤部分。
4.选择OpenGL版本
OpenGL ES 1.0 API版本(和1.1扩展),版本2.0和版本3.0都提供高性能图形界面,用于创建3D游戏,可视化和用户界面。 OpenGL ES 2.0和3.0的图形编程大致相似,版本3.0代表具有附加功能的2.0 API的超集。 OpenGL ES 1.0 / 1.1 API与OpenGL ES 2.0和3.0的编程有很大不同,因此在开始使用这些API进行开发之前,开发人员应仔细考虑以下因素:
- 性能:通常,OpenGL ES 2.0和3.0提供比ES 1.0 / 1.1 API更快的图形性能。但是,由于硬件制造商对OpenGL ES图形管道的实现存在差异,性能差异可能因运行OpenGL应用程序的Android设备而异。
- 设备兼容性:开发人员应考虑客户可用的设备类型,Android版本和OpenGL ES版本。有关跨设备的OpenGL兼容性的更多信息,请参阅OpenGL版本和设备兼容性部分。
- 编码便捷:OpenGL ES 1.0 / 1.1 API提供了固定的功能管道和便利功能,这些功能在OpenGL ES 2.0或3.0 API中不可用。不熟悉OpenGL ES的开发人员可以更快,更方便地找到1.0 / 1.1版的编码。
图形控制:OpenGL ES 2.0和3.0 API通过使用着色器提供完全可编程的管道,从而提供更高程度的控制。通过更直接地控制图形处理管道,开发人员可以创建使用1.0 / 1.1 API非常难以生成的效果。- Texture支持:OpenGL ES 3.0 API对纹理压缩具有最佳支持,因为它保证了ETC2压缩格式的可用性,该格式支持透明度。 1.x和2.0 API实现通常包括对ETC1的支持,但是此纹理格式不支持透明度,因此您通常必须提供您所定位的设备支持的其他压缩格式的资源。有关更多信息,请参阅纹理压缩支持。
虽然性能,兼容性,便利性,控制和其他因素可能会影响您的决策,但您应该根据您认为为用户提供的最佳体验选择OpenGL API版本。
5.映射绘制对象的坐标
在Android设备上显示图形的基本问题之一是它们的屏幕尺寸和形状可能不同。 OpenGL采用方形,均匀的坐标系统,默认情况下,可以将这些坐标快速绘制到典型的非方形屏幕上,就好像它是完全正方形一样。
这部分非常重要,下面会列举例子来讲解。
6.使用OpenGL ES显示图形
Android框架提供了大量标准工具,用于创建有吸引力的功能性图形用户界面。但是,如果您想要更多地控制应用程序在屏幕上绘制的内容,或者想要进入三维图形,则需要使用不同的工具。 Android框架提供的OpenGL ES API提供了一组工具,用于显示仅受您想象力限制的高端动画图形,并且还可以受益于许多Android设备上提供的图形处理单元(GPU)的加速。
下面将向您介绍开发使用OpenGL的应用程序的基础知识,包括设置,绘图对象,移动绘制元素和响应触摸输入。
此类中的示例代码使用OpenGL ES 2.0 API,这是推荐的API版本,可与当前的Android设备一起使用。有关OpenGL ES版本的更多信息,请参阅OpenGL开发人员指南。
注意:不要将OpenGL ES 1.x API调用与OpenGL ES 2.0方法混合使用!这两个API不可互换,混合使用会导致严重的问题。
6.1 构建OpenGL ES环境
要在Android应用程序中使用OpenGL ES绘制图形,必须为它们创建一个视图容器。其中一种更直接的方法是实现GLSurfaceView和GLSurfaceView.Renderer。 GLSurfaceView是使用OpenGL和GLSurfaceView.Renderer绘制的图形的视图容器,用于控制在该视图中绘制的内容。有关这些类的更多信息,请参阅OpenGL ES开发人员指南。
GLSurfaceView只是将OpenGL ES图形合并到应用程序中的一种方法。对于全屏或近全屏图形视图,这是一个合理的选择。想要在布局的一小部分中加入OpenGL ES图形的开发人员应该看一下TextureView。对于真正的,自己动手的开发人员,也可以使用SurfaceView构建OpenGL ES视图,但这需要编写相当多的额外代码。
下面介绍如何在简单的应用程序活动中完成GLSurfaceView和GLSurfaceView.Renderer的最小实现。
6.2 定义形状
能够在OpenGL ES视图的上下文中定义要绘制的形状是创建高端图形杰作的第一步。在不了解OpenGL ES如何定义图形对象的基本知识的情况下,使用OpenGL ES绘图可能有点棘手。
下面介绍了相对于Android设备屏幕的OpenGL ES坐标系,定义形状,形状面以及定义三角形和正方形的基础知识。
6.3 绘制形状
在定义要使用OpenGL绘制的形状后,您可能想要绘制它们。使用OpenGL ES 2.0绘制形状需要的代码比您想象的多一些,因为API提供了对图形渲染管道的大量控制。
下面介绍如何使用OpenGL ES 2.0 API绘制您在上一课中定义的形状。
6.4 应用投影和相机视图
在OpenGL ES环境中,投影和camera视图允许您以更接近您用眼睛看物理对象的方式显示绘制对象。这种物理观察模拟是通过绘制对象坐标的数学变换完成的:
投影:
此变换根据显示它们的GLSurfaceView的宽度和高度调整绘制对象的坐标。如果没有这个计算,OpenGL ES绘制的对象会因视图窗口的不等比例而倾斜。通常只需在渲染器的onSurfaceChanged()方法中建立或更改OpenGL视图的比例时计算投影变换。有关OpenGL ES投影和坐标映射的更多信息,请参阅映射绘制对象的坐标。
Camera视图:
此变换基于虚拟摄像机位置调整绘制对象的坐标。重要的是要注意OpenGL ES不定义实际的相机对象,而是提供通过转换绘制对象的显示来模拟相机的实用方法。建立GLSurfaceView时,摄像机视图转换可能只计算一次,或者可能会根据用户操作或应用程序的功能动态更改。
下面介绍如何创建投影和camera视图,并将其应用于GLSurfaceView中绘制的形状。
6.5 动作添加
在屏幕上绘制对象是OpenGL的一个非常基本的功能,但您可以使用其他Android图形框架类(包括Canvas和Drawable对象)执行此操作。 OpenGL ES提供了三维或以其他独特方式移动和转换绘制对象的附加功能,以创建引人注目的用户体验。 下面将介绍如何通过旋转向动态添加动作,从而向前迈出另一步使用OpenGL ES。
6.6 响应touch event
使对象按照预设程序移动(如旋转三角形)对于获得一些注意力很有用,但如果您希望用户与您的OpenGL ES图形进行交互,该怎么办?使您的OpenGL ES应用程序触摸交互的关键是扩展您的GLSurfaceView实现以覆盖onTouchEvent()以侦听触摸事件。 下面将介绍如何监听触摸事件以允许用户旋转OpenGL ES对象。
下面根据上面的讲解完成一个简单的OpenGL demo:
OpenGL demo演示图.gif
下面附上核心的源码:
MyGLSurfaceView.java
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
/**
* A view container where OpenGL ES graphics can be drawn on screen.
* This view can also be used to capture touch events, such as a user
* interacting with drawn objects.
*/
public class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context) {
super(context);
// Create an OpenGL ES 2.0 context.
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
mRenderer = new MyGLRenderer();
setRenderer(mRenderer);
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
@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.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR)); // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
}
MyGLRenderer.java
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
/**
* Provides drawing instructions for a GLSurfaceView object. This class
* must override the OpenGL ES drawing lifecycle methods:
* <ul>
* <li>{@link android.opengl.GLSurfaceView.Renderer#onSurfaceCreated}</li>
* <li>{@link android.opengl.GLSurfaceView.Renderer#onDrawFrame}</li>
* <li>{@link android.opengl.GLSurfaceView.Renderer#onSurfaceChanged}</li>
* </ul>
*/
public class MyGLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private Triangle mTriangle;
private Square mSquare;
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private float mAngle;
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mTriangle = new Triangle();
mSquare = new Square();
}
@Override
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Draw square
mSquare.draw(mMVPMatrix);
// Create a rotation for the triangle
// Use the following code to generate constant rotation.
// Leave this code out when using TouchEvents.
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
// Draw triangle
mTriangle.draw(scratch);
}
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
/**
* Utility method for compiling a OpenGL shader.
*
* <p><strong>Note:</strong> When developing shaders, use the checkGlError()
* method to debug shader coding errors.</p>
*
* @param type - Vertex or fragment shader type.
* @param shaderCode - String containing the shader code.
* @return - Returns an id for the shader.
*/
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
*
* If the operation is not successful, the check throws an error.
*
* @param glOperation - Name of the OpenGL call to check.
*/
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
/**
* Returns the rotation angle of the triangle shape (mTriangle).
*
* @return - A float representing the rotation angle.
*/
public float getAngle() {
return mAngle;
}
/**
* Sets the rotation angle of the triangle shape (mTriangle).
*/
public void setAngle(float angle) {
mAngle = angle;
}
}
Square.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import android.opengl.GLES20;
/**
* A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
*/
public class Square {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// The matrix must be included as a modifier 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;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* @param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
Triangle.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
/**
* A two-dimensional triangle for use as a drawn object in OpenGL ES 2.0.
*/
public class Triangle {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier 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;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = {
// in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f };
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* @param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
MainActivity.java中的设置:
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class MainActivity 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 MyGLSurfaceView(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();
}
}
最后别忘了在AndroidManifest.xml中声明当前的opengl版本:
<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />