在Android中使用NDK调用OpenGl
2014-02-22 13:38
330 查看
在Android中使用NDK调用OpenGl
Calling OpenGL from C on Android, Using the NDK原文地址:http://www.learnopengles.com/calling-opengl-from-android-using-the-ndk/对于我的系列文章Developinga Simple Game of Air Hockey Using C++ and OpenGL ES 2 for Android, iOS, and the Web的第一节,我们需要用opengl创建一个简单的android工程,这里会用到本地代码渲染场景。预备知识
android sdk ndk 和一个合适的ide模拟器或一个支持opengl es 2.0的android设备本次课程里我们将使用eclipse测试本次教程里的代码,我使用adt 22.0.1 和 platform 17 ,ndk 8e 和 Eclipse Juno Service Pack 2。准备工作
首先创建一个新工程,带NDK支持的那种,当然你也可以从 GitHub project获取代码。在创建新项目之前,建立一个airhockey文件夹,然后建立一个git文件夹,Git可以帮助你管理源代码,比如在你出现错误代码时恢复,学习更多的内容,请点击Git documentation。File->New->Android Application Project,然后取名为airHockey,application name设置为Air Hockey,包名设置为com.learnopengles.airhockey,其他的选项默认,或者自己填写,保存项目到我们刚才创建的文件夹中。创建好后,右击project,选择Android Tools->Add Native Support,提问library名称时,输入game,则创建出的库名称为libgame.so,这将在项目文件夹中建立一个jni文件夹。初始化OpenGl
项目创建后,现在我们可以修改activity和configurate来加载OpenGl,首先我们先在Activity类中添加两个变量private GLSurfaceView glSurfaceView; private boolean rendererSet;现在设置onCreate()
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000 || isProbablyEmulator(); if (supportsEs2) { glSurfaceView = new GLSurfaceView(this); if (isProbablyEmulator()) { // Avoids crashes on startup with some emulator images. glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); } glSurfaceView.setEGLContextClientVersion(2); glSurfaceView.setRenderer(new RendererWrapper()); rendererSet = true; setContentView(glSurfaceView); } else { // Should never be seen in production, since the manifest filters // unsupported devices. Toast.makeText(this, "This device does not support OpenGL ES 2.0.", Toast.LENGTH_LONG).show(); return; } }首先检测设备是否支持OpenGl ES 2.0 ,支持的话创建一个GlSurfaceView。configurationInfo.reqGlEsVersion >= 0x20000在模拟器上不可用,所以我们添加
private boolean isProbablyEmulator() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && (Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown") || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK built for x86")); }OpengGl ES 2.0只能在启用了Host GPU的模拟器上使用, 获取功多信息请阅读,AndroidEmulator Now Supports Native OpenGL ES2.0!添加一下代码完成对Activity的修改:
@Override protected void onPause() { super.onPause(); if (rendererSet) { glSurfaceView.onPause(); } } @Override protected void onResume() { super.onResume(); if (rendererSet) { glSurfaceView.onResume(); } }我们需要处理Android的生命周期,所以在需要的时候暂停继续游戏。只有在执行了glSurfaceView.setRenderer()后处理才回起作用,否则调用这些方法回使程序跳出。获得更多的信息,点击Android Lesson One: Getting Started or OpenGLES 2 for Android: A Quick-Start Guide。
创建一个RendererWrapper类
public class RendererWrapper implements Renderer { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { glClearColor(0.0f, 0.0f, 1.0f, 0.0f); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // No-op } @Override public void onDrawFrame(GL10 gl) { glClear(GL_COLOR_BUFFER_BIT); } }这个简单的渲染程序用蓝色背景清除屏幕。稍后我们将把这些方法换成c++程序。调用这些方法不用添加gl前缀,只需添加android.opengl.GLES20.*在文件顶部,然后选择Source->Organize Imports。如果你在编译的时候遇到了错误,确保你加入了下面所有的引用
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView.Renderer;修改manifest排除不支持OpenGl的设备把下面的代码加入到manifest的某个地方
<uses-feature android:glEsVersion="0x00020000" android:required="true" />从OGl 2.0开始只支持Android2.3.3 api10以上的系统,所以替换use-sdk标签:
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />工作正常,将看到一下画面
加入本地代码
我们已经完成了java上的代码,但我们真正想做的用本地代码调用OpenGl,应该怎么做呢?下面我们将创建一个NDK项目,并把opengl代码移植到c文件里。为了将来能让ios和web平台共享我们制作的本地代码,所以我们需要在项目文件夹上层建立一个common文件夹,这意味这,在你的Air Hockey项目中有一个android文件夹,里面存放android项目,common文件夹中存放公用代码。用eclipse连接一个项目文件夹外的文件夹有些麻烦,分以下几步:右击项目选择属性,Resource->Linked Resources然后选择New输入COMMON_SRC_LOC作为名称,位置为‘${PROJECT_LOC}\..\common’然后点击Ok。右键点击项目选择Build Path->Link Source…, 选择 Variables…, 选择 COMMON_SRC_LOC,点击Ok,输入common作为文件夹的名称选择Finish。现在项目里出现了一个新文件夹common。接下来我们要在common文件夹中创建两个文件game.c和game.h,方法为右键点击项目选择New File。将一下内容添加进game.h:void on_surface_created(); void on_surface_changed(); void on_draw_frame();c语言中,.h为头文件,这个文件作为.c文件的声明。这里包含三个我们将在Java中调用的函数。在game.c中加入下面内容。
#include "game.h" #include "glwrapper.h" void on_surface_created() { glClearColor(1.0f, 0.0f, 0.0f, 0.0f); } void on_surface_changed() { // No-op } void on_draw_frame() { glClear(GL_COLOR_BUFFER_BIT); }这些代码可以用红色清除屏幕,且每帧都会进行清除操作。我们使用一个glwrapper.h去包含OpenGl平台特征库,这个文件在其他平台的不同位置都有出现。加入平台特征码和JNI码使用这些代码,我们只需要两样东西:定义glwrapper.h和一些JNI结合代码。JNI可以替代Java本地接口,这是在Android上让Java和C互相调用的方法。在工程中的jni文件夹创建一个glwrapper.h文件,加入以下内容:
#include "../../common/game.h" #include <jni.h> JNIEXPORT void JNICALL Java_com_learnopengles_airhockey_GameLibJNIWrapper_on_1surface_1created (JNIEnv * env, jclass cls) { on_surface_created(); } JNIEXPORT void JNICALL Java_com_learnopengles_airhockey_GameLibJNIWrapper_on_1surface_1changed (JNIEnv * env, jclass cls, jint width, jint height) { on_surface_changed(); } JNIEXPORT void JNICALL Java_com_learnopengles_airhockey_GameLibJNIWrapper_on_1draw_1frame (JNIEnv * env, jclass cls) { on_draw_frame(); }好的文件修改完了,且加入了game.h引用,这样可以调用我们的game方法。这里是工作原理:GameLibJNIWrapper 定义了可以从Java调用的本地c方法。为了能从Java进行调用,必须用特殊的方式进行命名,每个方法至少有两个参数,几个JNIEnv指针,和jclass 。为了方便,我们可以使用javah创建带前缀的jin.c。我们从jin.c调用game.h定义的方法。这样Java调用本地代码的连接就完成了。
编译本地代码
编译和执行本地代码,我们需要为ndk创建系统提供描述文件。为了达到这个目的,我们需要两个文件Android.mk 和 Application.mk。当我们增加本地支持到项目中时,项目中自动增加了一个叫game.cpp的文件,这个文件不需要,所以你可以删除他。为Android.mk做以下设置:这行语句告诉ndk项目创建使用的android版本为api 10,所以这里会在你使用了早些版本不支持的特性时发出警告,也告诉编译系统生成
ARMv7-A格式的库,这可以支持浮点数和许多android设备的新功能。
更新RendererWrapper
我们必须更新RendererWrapper以调用我们做的native代码,才能看到我们多绘制的东西,如下:运行应用
现在可以运行工程了。在创建后会有个libgame.so文件在 /libs/armeabi-v7a/中创建。运行后,程序看起来是这个样子的:Second pass屏幕呈现红蓝交替变换。下期预告
项目的完整代码在这里 GitHub project。更详细的介绍OpenGL ES 2,请参考 AndroidLesson One: Getting Started or OpenGL ES 2 for Android: A Quick-Start Guide。在接下来的章节里,我们将创建一个ios项目。到时你回发现用oc重新使用common文件夹里的代码是件多么容易的事。如果有问题请留言!(作者)相关文章推荐
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 解決Linux下Android开发真机调试设备不被识别问题
- [Android]在代码里运行另一个程序的方法
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- [软件咨询]WPS2012正式版已发布 金山Office移动版4.0发布
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- 解决Vista系统OpenGL驱动问题的方法整理
- android USB如何修改VID具体实现
- Android增量升级的方法和原理详细介绍
- Android Mouse实现过程详细笔记
- 深入Android Browser配置管理的详解
- Android Mms之:深入理解对话列表管理
- Android APP与媒体存储服务的交互
- android 多线程技术应用
- Android之采用execSQL与rawQuery方法完成数据的添删改查操作详解
- Android数据类型之间相互转换系统介绍