在 Android 中使用 OpenGL
Android 通過 OpenGL 包含了對高性能 2D 和 3D 圖形的支持,特別是 OpenGL ES API。OpenGL 是一個跨平臺的圖形 API,它為 3D 圖形處理硬件規定了一個標準的軟件接口。OpenGL ES 是一種用于嵌入式設備的 OpenGL 規范。Android 支持多種版本的 OpenGL ES API:
- OpenGL ES 1.0 和 1.1 - Android 1.0 及更高版本支持這個 API 規范。
- OpenGL ES 2.0 - Android 2.0(API level 8)及更高版本支持這個 API 規范。
- OpenGL ES 3.0 - Android 4.3(API level 18)及更高版本支持這個 API 規范。
- OpenGL ES 3.1 - Android 5.0(API level 21)及更高版本支持這個 API 規范。
注意: 設備上對 OpenGL ES 3.0 API 的支持需要設備生產商提供這個圖形管線的實現。運行 Android 4.3 或更高版本的設備 可能不 支持 OpenGL ES 3.0 API。在運行時檢查支持何種版本的 OpenGL ES 的信息,請參考 Checking OpenGL ES Version。
注意: Android framework 提供的特別的 API 與 J2ME JSR239 OpenGL ES API 類似,但不完全一致。如果你對 J2ME JSR239 比較熟悉,請注意其中的不同。
Android 同時通過它的 framework API 和 NDK 支持 OpenGL。這里主要來看 Android framework 的接口。Android framework 中提供了多個接口讓我們可以通過 OpenGL ES 創建并管理圖形:GLSurfaceView,TextureView,SurfaceView 等等。如果是要在實際的應用中使用 OpenGL,則 GLSurfaceView 最好用。
為了能夠更清晰地厘清,EGL 為 OpenGL 渲染做環境準備的過程,以及 EGL contexts 的管理,這里使用 SurfaceView。示例 OpenGL 應用程序代碼(完整代碼可以在 GitHub 獲取)如下:
package com.cloudgame.opengldemo;import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL10;import android.app.Activity; import android.content.Context; import android.content.Intent; import android.opengl.GLDebugHelper; import android.opengl.GLU; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView;public class BasicGLActivity extends Activity {public static final String COLOR_OPTION_EXTRA = "COLORFUL";private boolean doColorful = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Intent starter = getIntent();doColorful = starter.getBooleanExtra(COLOR_OPTION_EXTRA, false);mAndroidSurface = new BasicGLSurfaceView(this);setContentView(mAndroidSurface);}private class BasicGLSurfaceView extends SurfaceView implementsSurfaceHolder.Callback {SurfaceHolder mAndroidHolder;BasicGLSurfaceView(Context context) {super(context);mAndroidHolder = getHolder();mAndroidHolder.addCallback(this);mAndroidHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);}public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}public void surfaceCreated(SurfaceHolder holder) {mGLThread = new BasicGLThread(this);mGLThread.start();}public void surfaceDestroyed(SurfaceHolder holder) {if (mGLThread != null) {mGLThread.requestStop();}}}BasicGLThread mGLThread;private class BasicGLThread extends Thread {private static final String DEBUG_TAG = "BasicGLThread";SurfaceView sv;BasicGLThread(SurfaceView view) {sv = view;}private boolean mDone = false;public void run() {try {initEGL();initGL();TriangleSmallGLUT triangle = new TriangleSmallGLUT(3);mGL.glMatrixMode(GL10.GL_MODELVIEW);mGL.glLoadIdentity();GLU.gluLookAt(mGL, 0, 0, 10f, 0, 0, 0, 0, 1, 0f);mGL.glColor4f(1f, 0f, 0f, 1f);while (!mDone) {mGL.glClear(GL10.GL_COLOR_BUFFER_BIT| GL10.GL_DEPTH_BUFFER_BIT);mGL.glRotatef(1f, 0, 0, 1f);if (doColorful) {triangle.drawColorful(mGL);} else {triangle.draw(mGL);}mEGL.eglSwapBuffers(mGLDisplay, mGLSurface);}} catch (Exception e) {Log.e(DEBUG_TAG, "GL Failure", e);} finally {cleanupGL();}}public void requestStop() {mDone = true;try {join();} catch (InterruptedException e) {Log.e(DEBUG_TAG, "failed to stop gl thread", e);}cleanupGL();}private void cleanupGL() {mEGL.eglMakeCurrent(mGLDisplay, EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);mEGL.eglDestroySurface(mGLDisplay, mGLSurface);mEGL.eglDestroyContext(mGLDisplay, mGLContext);mEGL.eglTerminate(mGLDisplay);Log.i(DEBUG_TAG, "GL Cleaned up");}public void initGL( ) {int width = sv.getWidth();int height = sv.getHeight();mGL.glViewport(0, 0, width, height);mGL.glMatrixMode(GL10.GL_PROJECTION);mGL.glLoadIdentity();float aspect = (float) width/height;GLU.gluPerspective(mGL, 45.0f, aspect, 1.0f, 30.0f);mGL.glClearColor(0.5f,0.5f,0.5f,1);// the only way to draw primitives with OpenGL ESmGL.glEnableClientState(GL10.GL_VERTEX_ARRAY);Log.i(DEBUG_TAG, "GL initialized");}public void initEGL() throws Exception {mEGL = (EGL10) GLDebugHelper.wrap(EGLContext.getEGL(),GLDebugHelper.CONFIG_CHECK_GL_ERROR| GLDebugHelper.CONFIG_CHECK_THREAD, null);if (mEGL == null) {throw new Exception("Couldn't get EGL");}mGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);if (mGLDisplay == null) {throw new Exception("Couldn't get display for GL");}int[] curGLVersion = new int[2];mEGL.eglInitialize(mGLDisplay, curGLVersion);Log.i(DEBUG_TAG, "GL version = " + curGLVersion[0] + "."+ curGLVersion[1]);EGLConfig[] configs = new EGLConfig[1];int[] num_config = new int[1];mEGL.eglChooseConfig(mGLDisplay, mConfigSpec, configs, 1,num_config);mGLConfig = configs[0];mGLSurface = mEGL.eglCreateWindowSurface(mGLDisplay, mGLConfig, sv.getHolder(), null);if (mGLSurface == null) {throw new Exception("Couldn't create new surface");}mGLContext = mEGL.eglCreateContext(mGLDisplay, mGLConfig,EGL10.EGL_NO_CONTEXT, null);if (mGLContext == null) {throw new Exception("Couldn't create new context");}if (!mEGL.eglMakeCurrent(mGLDisplay, mGLSurface, mGLSurface, mGLContext)) {throw new Exception("Failed to eglMakeCurrent");}mGL = (GL10) GLDebugHelper.wrap(mGLContext.getGL(),GLDebugHelper.CONFIG_CHECK_GL_ERROR| GLDebugHelper.CONFIG_CHECK_THREAD| GLDebugHelper.CONFIG_LOG_ARGUMENT_NAMES, null);if (mGL == null) {throw new Exception("Failed to get GL");}}// main OpenGL variablesGL10 mGL;EGL10 mEGL;EGLDisplay mGLDisplay;EGLConfig mGLConfig;EGLSurface mGLSurface;EGLContext mGLContext;int[] mConfigSpec = { EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onPause() {super.onPause();}SurfaceView mAndroidSurface; }SurfaceView 的 SurfaceHolder 為 OpenGL 的渲染提供畫布,即 Surface,它決定圖像被實際渲染到什么地方。在上面的代碼中,Activity 的整個 layout 中就只有一個 SurfaceView,它被用于獲取 SurfaceHolder。
上面的代碼中,SurfaceView 的 Surface 創建好之后,起了一個單獨的線程,這個線程用于處理在該 Surface 上渲染的所有事情。
在能夠使用 OpenGL 渲染之前,首先需要為渲染做環境上的準備,即創建 EGL context,并啟用它。這通過如下的方法完成:
public void initEGL() throws Exception {mEGL = (EGL10) GLDebugHelper.wrap(EGLContext.getEGL(),GLDebugHelper.CONFIG_CHECK_GL_ERROR| GLDebugHelper.CONFIG_CHECK_THREAD, null);if (mEGL == null) {throw new Exception("Couldn't get EGL");}mGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);if (mGLDisplay == null) {throw new Exception("Couldn't get display for GL");}int[] curGLVersion = new int[2];mEGL.eglInitialize(mGLDisplay, curGLVersion);Log.i(DEBUG_TAG, "GL version = " + curGLVersion[0] + "."+ curGLVersion[1]);EGLConfig[] configs = new EGLConfig[1];int[] num_config = new int[1];mEGL.eglChooseConfig(mGLDisplay, mConfigSpec, configs, 1,num_config);mGLConfig = configs[0];mGLSurface = mEGL.eglCreateWindowSurface(mGLDisplay, mGLConfig, sv.getHolder(), null);if (mGLSurface == null) {throw new Exception("Couldn't create new surface");}mGLContext = mEGL.eglCreateContext(mGLDisplay, mGLConfig,EGL10.EGL_NO_CONTEXT, null);if (mGLContext == null) {throw new Exception("Couldn't create new context");}if (!mEGL.eglMakeCurrent(mGLDisplay, mGLSurface, mGLSurface, mGLContext)) {throw new Exception("Failed to eglMakeCurrent");}mGL = (GL10) GLDebugHelper.wrap(mGLContext.getGL(),GLDebugHelper.CONFIG_CHECK_GL_ERROR| GLDebugHelper.CONFIG_CHECK_THREAD| GLDebugHelper.CONFIG_LOG_ARGUMENT_NAMES, null);if (mGL == null) {throw new Exception("Failed to get GL");}}// main OpenGL variablesGL10 mGL;EGL10 mEGL;EGLDisplay mGLDisplay;EGLConfig mGLConfig;EGLSurface mGLSurface;EGLContext mGLContext;int[] mConfigSpec = { EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };這個過程如下:
隨后就可以使用 OpenGL 做渲染了。
每次一個場景渲染完成,都需要通過交換緩沖區來顯示渲染結果:
mEGL.eglSwapBuffers(mGLDisplay, mGLSurface);整個渲染過程結束之后,還需要銷毀前面創建的 EGL context:
private void cleanupGL() {mEGL.eglMakeCurrent(mGLDisplay, EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);mEGL.eglDestroySurface(mGLDisplay, mGLSurface);mEGL.eglDestroyContext(mGLDisplay, mGLContext);mEGL.eglTerminate(mGLDisplay);}而 framework 的 GLSurfaceView 類提供了一些輔助類來管理 EGL contexts,線程間通信,以及與 Activity 生命周期的交互,僅此而已。大多數時候,GLSurfaceView 可以簡化我們對 OpenGL ES 的使用。
Done.
Android OpenGL 圖形系統分析系列文章
在 Android 中使用 OpenGL
Android 圖形驅動初始化
EGL Context 創建
總結
以上是生活随笔為你收集整理的在 Android 中使用 OpenGL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 模拟器下载、编译及调试
- 下一篇: Android 图形驱动初始化