基於 Android NDK 進行 OpenGL ES開發

基於 Android NDK 進行 OpenGL ES開發
作者: 劉鵬
日期: 2010-02-08
本文詳細介紹瞭如何利用 Anroid NDK 開發 native OpenGL ES程序。

NDK 簡介

Android SDK 提供了一套 OpenGL ES 接口,該接口是基於 Java 的,速度非常慢,往往很難滿足需要。

NDK 提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將 so和 java 應用一起打包成 apk。使用 NDK,我們可以將要求高性能的應用邏輯使用C開發,從而提高應用程序的執行效率。如 OpenGL ES 的程序。2

JNI

對於 NDK,在 Java 代碼中調用 C/C++ 代碼是通過 JNI 實現的。

Java Native Interface(JNI)是 Java 語言的本地編程接口,在java程序中,我們可以通過 JNI 實現一些用 java 語言不便實現的功能。通常有以下幾種情況我們需要使用 JNI 來實現:

  1. java 類庫沒有提供你的應用程序所需要的功能,通常這些功能是平臺相關的;
  2. 你希望使用一些已經有的類庫或者應用程序,而他們並非用java語言編寫的;
  3. 程序的某些部分對速度要求比較苛刻,你選擇用匯編或者 c 語言來實現並在 java 語言中調用他們。

注意,不到萬不得已不要使用 JNI 技術,一方面它需要你把握更多的知識才可以駕馭,一方面使用了 JNI 你的程序就會喪失可移植性。

我們可以把 JNI 想象爲本地代碼和 java 代碼的粘合劑,關係如下圖所示3

OpenGL ES

Java 端核心代碼如下所示:
public class DemoAct extends Activity {

    private GLSurfaceView mGLView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGLView = new DemoGLSurfaceView(this);
        setContentView(mGLView);
    }

    ... ...

    static {
        System.loadLibrary("sanangeles");
    }

}


class DemoGLSurfaceView extends GLSurfaceView {

    DemoRenderer mRenderer;

    public DemoGLSurfaceView(Context context) {
        super(context);

        mRenderer = new DemoRenderer();
        setRenderer(mRenderer);
    }

class DemoRenderer implements GLSurfaceView.Renderer {

    private static native void nativeInit();
    private static native void nativeResize(int w, int h);
    private static native void nativeRender();
    private static native void nativeDone();

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        nativeInit();
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {

        nativeResize(w, h);
    }

    public void onDrawFrame(GL10 gl) {
        nativeRender();
    }


}

注意:EGL Context Lost

There are situations where the EGL rendering context will be lost. This typically happens when device wakes up after going to sleep. When the EGL context is lost, all OpenGL resources (such as textures) that are associated with that context will be automatically deleted. In order to keep rendering correctly, a renderer must recreate any lost resources that it still needs. The onSurfaceCreated(GL10, EGLConfig) method is a convenient place to do this.

JNI 代碼如下所示:
#include <jni.h>
...

/* Call to initialize the graphics state */
void
Java_com_example_AndMii_DemoRenderer_nativeInit( JNIEnv*  env )
{
    appInit();
}


/* Call to finalize the graphics state */
void
Java_com_example_AndMii_DemoRenderer_nativeDone( JNIEnv*  env )
{
    free objects.
}

/* Call to render the next GL frame */
void
Java_com_example_AndMii_DemoRenderer_nativeRender( JNIEnv*  env )
{
    curTime = ...
    appRender(curTime, sWindowWidth, sWindowHeight);

}

C 語言端
void appInit()
{

    glEnable(GL_NORMALIZE);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glShadeModel(GL_FLAT);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHT2);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

}



// Called from the app framework.
/* The tick is current time in milliseconds, width and height
 * are the image dimensions to be rendered.
 */
void appRender(long tick, int width, int height)
{

    // Prepare OpenGL ES for rendering of the frame.
    prepareFrame(width, height);

    // Configure environment.
    configureLightAndMaterial();

    // Draw all the models normally.
    drawModels(1);

}

static void prepareFrame(int width, int height)
{
    glViewport(0, 0, width, height);

    glClearColorx((GLfixed)(0.1f * 65536),
                  (GLfixed)(0.2f * 65536),
                  (GLfixed)(0.3f * 65536), 0x10000);
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, (float)width / height, 0.5f, 150);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();
}

static void configureLightAndMaterial()
{
    static GLfixed light0Position[] = { -0x40000, 0x10000, 0x10000, 0 };
    static GLfixed light0Diffuse[] = { 0x10000, 0x6666, 0, 0x10000 };
    static GLfixed light1Position[] = { 0x10000, -0x20000, -0x10000, 0 };
    static GLfixed light1Diffuse[] = { 0x11eb, 0x23d7, 0x5999, 0x10000 };
    static GLfixed light2Position[] = { -0x10000, 0, -0x40000, 0 };
    static GLfixed light2Diffuse[] = { 0x11eb, 0x2b85, 0x23d7, 0x10000 };
    static GLfixed materialSpecular[] = { 0x10000, 0x10000, 0x10000, 0x10000 };

    glLightxv(GL_LIGHT0, GL_POSITION, light0Position);
    glLightxv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
    glLightxv(GL_LIGHT1, GL_POSITION, light1Position);
    glLightxv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse);
    glLightxv(GL_LIGHT2, GL_POSITION, light2Position);
    glLightxv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse);
    glMaterialxv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular);

    glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, 60 << 16);
    glEnable(GL_COLOR_MATERIAL);
}


static void drawModels(float zScale)
{

    glVertexPointer(......);
    glColorPointer(......);

    if (normalArray != NULL)
    {
        glNormalPointer(GL_FIXED, 0, normalArray);
        glEnableClientState(GL_NORMAL_ARRAY);
    }
    else
        glDisableClientState(GL_NORMAL_ARRAY);

    glDrawArrays(GL_TRIANGLES, 0, object->count);



}


調試

要想在 jni native 代碼中看打印信息,printf 是不行的,需使用 __android_log_print,如下所示。

__android_log_print(ANDROID_LOG_INFO, "ProjectName", "I am : %d/n", n);

該函數與 printf 用法相似,使用格式字符。打印的結果通過 logcat 查看。

注意,使用時需要將頭文件 android/log.h 包含進來。

爲方便使用,往往定義一些宏

#include <android/log.h>

#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ProjectName", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "ProjectName", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO   , "ProjectName", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN   , "ProjectName", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "ProjectName", __VA_ARGS__)

Reference

  1. Android 1.6 NDK
  2. Android NDK具體作用講解
  3. JNI入門教程之HelloWorld篇
  4. JNI Examples for Android
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章