1

本教程适用于学习opengles,采用c++语言。基于Android平台。

创建工程

可以使用android-studio,eclipse去创建一个工程,这里我使用命令行(Android的环境配置略)。

# create android project
mkdir GlesTutorial
cd GlesTutorial
android create project --target android-19 --name GlesTutorial --path ./ --package com.richard.glestutorial --activity AppActivity

# jni port
mkdir jni
# core c++ code
mkdir core

在项目的根目录下创建一个jni目录,用来放jni相关的文件,core为我们以后主要使用的编程目录,将只存放c++代码。

这里run没问题就继续。

创建SurfaceView

我们不再使用sdk中的api去创建view,而是利用opengl-es来创建我们的view。android里可以用到GLSurfaceView,这是给Opengl专用的。

在AppActivity中采用我们自己的view,并将一些生命周期状态发送给我们自己的View:

package com.richard.glestutorial;

import android.app.Activity;
import android.os.Bundle;

public class AppActivity extends Activity
{
    protected AppSurfaceView mView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mView = new AppSurfaceView(getApplication());
        setContentView(mView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mView.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

创建AppSurfaceView.java 继承GLSurfaceView:

package com.richard.glestutorial;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class AppSurfaceView extends GLSurfaceView {

    protected GLRenderer mRender;

    public AppSurfaceView(Context context) {
        super(context);
        setEGLContextClientVersion(2); // set OpenGLES version
        
        mRender = new GLRenderer();    // SurfaceView 需要设置一个渲染对象,待会自己实现一个SurfaceView.Renderer
        setRenderer(mRender);
    }

    @Override
    public void onPause() {
        super.onPause();
        mRender.handleOnPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        mRender.handleOnResume();
    }
}

GLRendder.java:

package com.richard.glestutorial;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;

public class GLRenderer implements Renderer {

    static {
        System.loadLibrary("gltutorial");  // 一起来就加载c++库文件,待会来指定这个库
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLRenderer.nativeInit();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLRenderer.nativeOnReSize(width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLRenderer.nativeUpdate();
    }

    public void handleOnPause() {
        GLRenderer.nativeOnPause();
    }

    public void handleOnResume() {
        Log.v("GLES Tutorial", "nativeOnResume");
        GLRenderer.nativeOnResume();
    }
    
    // ------------------------------------------------------------
    // Native Method

    private static native void nativeInit();

    private static native void nativeOnReSize(int width, int height);

    private static native void nativeUpdate();

    private static native void nativeOnPause();

    private static native void nativeOnResume();
}

这里设置了GLRenderer中声明了5个native方法,分别为:初始化;重绘制时设置view大小;每帧更新;暂停(退出前台);继续(回到前台)。将这些必备的状态通知给C++。以便我们进行绘制。

编写库

好了,接下来先编写Android.mk,用以生成libgltutorial.so这个库。在jni目录下新建Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := gltutorial

LOCAL_LDLIBS    := -llog -lGLESv2
LOCAL_CFLAGS    := -Werror
LOCAL_CPPFLAGS  := -std=c++11

include $(BUILD_SHARED_LIBRARY)

LOCAL_MODULE 指定了这个库的名字并添加了log库,opengl-es2这两个库给c++使用。LOCAL_CFLAGS是c/c++编译选项,Werror是警告也作为错误信息在编译时给出。BUILD_SHARED_LIBRARY是指定编译为动态库。

native实现

库是有了,还没有添加内容,这里要将GLRenderer中的native的实现以及我们自己定义的C++代码全部作为库。

先实现native方法:在bin下找到java源码build后的位置classes,运行javah生成native头文件: com_richard_glestutorial_GLRenderer.h

cd bin/classes
javah -classpath ~/dev/android/android-sdk-macosx/platforms/android-19/android.jar:. com.richard.glestutorial.GLRenderer
mv com_richard_glestutorial_GLRenderer.h ../../jni/

编写实现代码:com_richard_glestutorial_GLRenderer.cpp:这里为了以后使用方便,新建一个Director在根目录的core/中。

#include "com_richard_glestutorial_GLRenderer.h"
#include "../core/Director.h"

JNIEXPORT void JNICALL Java_com_richard_glestutorial_GLRenderer_nativeInit
  (JNIEnv *env, jclass thiz)
{
    Director::getInstance();
}

JNIEXPORT void JNICALL Java_com_richard_glestutorial_GLRenderer_nativeOnReSize
  (JNIEnv *env, jclass thiz, jint width, jint height)
{

    Director::getInstance()->setFrameSize(width, height);
}

JNIEXPORT void JNICALL Java_com_richard_glestutorial_GLRenderer_nativeUpdate
  (JNIEnv *env, jclass thiz)
{
    Director::getInstance()->mainLoop();
}

JNIEXPORT void JNICALL Java_com_richard_glestutorial_GLRenderer_nativeOnPause
  (JNIEnv *env, jclass thiz)
{
    Director::getInstance()->onEnterBackground();
}

JNIEXPORT void JNICALL Java_com_richard_glestutorial_GLRenderer_nativeOnResume
  (JNIEnv *env, jclass thiz)
{
    Director::getInstance()->onEnterForeground();
}

Director的定义如下:

Director.h:

#ifndef _DRECTOR_H__
#define _DRECTOR_H__

class Director
{
public:
    static Director* getInstance();

    void init();

    void setFrameSize(float width, float height);

    void mainLoop();

    void onEnterForeground();

    void onEnterBackground();

private:
    Director();
    ~Director();

    float _fFrameWidth;
    float _fFrameHeight;
};

其实现可以添加一些log。可以用于查看。

最后要在Android.mk中指定所有的c++代码:

LOCAL_SRC_FILES := \
com_richard_glestutorial_GLRenderer.cpp \
../core/Director.cpp
                
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)

Run

最后运行,我们就可以看到一个空白的view的界面。以后我们就可以在上面写界面了。

ndk-build # 编译库
ant debug # ant打包
adb install -r bin/GlesTutorial-debug.apk  # adb install
adb logcat | grep GLES  # 查看log

所有项目代码看里面的lesson1的tag。读者可以clone代码后,使用git checkout lesson1。github也提供了在线查看:点Branch:master那个绿色按钮可以看到tags,然后去切换。


RichardXG
337 声望19 粉丝