本教程适用于学习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,然后去切换。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。