【OpenGL/ES】 第02讲 Android JNI 调用OpenGL ES 2.0

来源:互联网 发布:小红书 有钱人 知乎 编辑:程序博客网 时间:2024/04/30 12:01

    如何在Android上调用C/C++程序,进而直接把你写的3D程序移植到Android上,是一个对很多人来说比较麻烦的事情,因为这要求会C++的同时还要了解Java和Android的基础知识,在某种程度上cocos2dx和unity3d正是因为在这种方面做得很优秀才脱颖而出的,没有多少人真心愿意用别人做的东西,unity3d迫使很多人放弃研究繁琐的平台细节,大幅降低游戏开发的门槛。

    如果想走的远,就需要自己动手,在Android上调用c++,需要的软件环境如下:

    一、Android NDK(native develop kits)

           这是一个交叉编译工具,什么又是交叉编译呢,就是在一个平台编译出另一个平台可以运行的代码。从intel x86处理器平台->Android arm处理器平台。


    二、cygwin - windows下的linux模拟器

          安装成功后启动cygwin terminal


    三、cdt - c/c++ develop tools  完成c/c++语法高亮显示


    安装过程注意,安装目录和路径不要有中文和空格,避免不必要的问题出现。

    以上软件作者已打包到百度云盘jni-env.zip,并附加详细安装方法,下载地址:jni-env.zip

    完成环境软件安装后,按照步骤一步一步实现即可:

    第一步:创建一个Android工程,Android和Java部分的知识,读者自动补齐。

    第二步:在MainActivity.java中添加方法-public native String helloFromJNI()

    第三步:创建jni文件夹,并创建hello.c文件

                 helloFromJNI的c语言命名约定:返回值(jstring) Java_包名(com_example_ndkdemo)_类名(MainActivity)_方法名(helloFromJNI) 参数(JNIEnv* env,jobject obj)

             通过这种约定,jni就可以把c函数转成java函数,参数JNIEnv*是一个结构体指针里面存储的是函数指针和jobject是void*,注意命名必须一致不要写错。

public native String helloFromC();#include <stdio.h>#include <jni.h>#include "com_example_ndkdemo_MainActivity.h"jstring Java_com_example_ndkdemo_MainActivity_helloFromC(JNIEnv* env,jobject obj){return (*env)->NewStringUTF(env,"Hello From C");}


    第四步:通过cygwin编译hello.c

               创建Android.mk,注意下面的LOCAL_MODULE是生成的模块名,LOCAL_SRC_FILES是源文件名

LOCAL_PATH := $(call my-dir)   include $(CLEAR_VARS)   LOCAL_MODULE    := Hello   LOCAL_SRC_FILES := Hello.c   include $(BUILD_SHARED_LIBRARY)

              打开cygwin terminal,切换到源文件目录/cygdrive/f/Study/GameDevelop/Android/workspace/NdkDemo/jni

        

        执行ndk-build命令,即可完成编译

         

    第五步:在MainActivity加载libHello.so

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

    第六步:通过helloFromC完成对C函数的调用

public void click(View view){Toast.makeText(this,helloFromC(),1).show();//}

        在主界面activity_main.xml添加一个按钮,响应函数设置为click

<Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="hello"    android:onClick="click"/>

       第六步:运行效果如下,点击hello,弹出Hello From C

                            

     到了这一步,基本实现Android JNI对C/C++的调用。接下来就可以在C++里写一下OpenGL ES 2.0绘制一个三角形然后在Android中显示。

     在ndk\android-ndk-r7b\samples\hello-gl2\src\com\android\gl2jni目录下,找到ndk自带的gles2的示例工程源码,里面包含三个.java文件:GL2JNIActivity,GL2JNIView,GL2JNILib,在ndk\android-ndk-r7b\samples\hello-gl2\jni目录找到Android.mk,gl_code.cpp

    不出太大意外的情况下,按照下面的步骤一步一步实现即可,

    第一步,在你的工程创建GL2JNIView.java,创建并配置EGLContext,创建Renderer

GL2JNIView mview;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //mview = new MyTDView(this);        mview = new GL2JNIView(this);        //mview.requestFocus();        //mview.setFocusableInTouchMode(true);        setContentView(mview);        //setContentView(R.layout.activity_main);        //init();        //EditText etUrl = (EditText)findViewById(R.id.et_url);    }
    private static String TAG = "GL2JNIView";public GL2JNIView(Context context){super(context);init(false,0,0);}

private void init(boolean translucent,int depth,int stencil){//创建EGLContextif(translucent)this.getHolder().setFormat(PixelFormat.TRANSLUCENT);//设置Context的Factory,只需派生一下GLSurfaceView.EGLContextFactorysetEGLContextFactory(new ContextFactory());//选择EGLContext的配置setEGLConfigChooser(new ConfigChooser(5,6,5,0,depth,stencil));//设置渲染器setRenderer(new Renderer());}

    第二步:实现内部类ContextFactory

private static class ContextFactory implements GLSurfaceView.EGLContextFactory{private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;public EGLContext createContext(EGL10 egl,EGLDisplay display,EGLConfig eglConfig){int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION,2,EGL10.EGL_NONE};EGLContext context = egl.eglCreateContext(display,eglConfig,EGL10.EGL_NO_CONTEXT,attrib_list);return context;}public void destroyContext(EGL10 egl,EGLDisplay display,EGLContext context){egl.eglDestroyContext(display, context);}}

    第三步:实现EGLContext的配置选择内部类ConfigChooser

private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser{        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {            mRedSize = r;            mGreenSize = g;            mBlueSize = b;            mAlphaSize = a;            mDepthSize = depth;            mStencilSize = stencil;        }                private static int EGL_OPENGL_ES2_BIT = 4;        private static int[] s_configAttribs2 =         {            EGL10.EGL_RED_SIZE, 4,            EGL10.EGL_GREEN_SIZE, 4,            EGL10.EGL_BLUE_SIZE, 4,            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,            EGL10.EGL_NONE        };                public EGLConfig chooseConfig(EGL10 egl,EGLDisplay display)        {        int[] num_config = new int[1];        egl.eglChooseConfig(display,s_configAttribs2,null,0,num_config);        int numConfigs = num_config[0];            if (numConfigs <= 0) {                throw new IllegalArgumentException("No configs match configSpec");            }                         /* Allocate then read the array of minimally matching EGL configs             */            EGLConfig[] configs = new EGLConfig[numConfigs];            egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);            return chooseConfig(egl,display,configs);        }                public EGLConfig chooseConfig(EGL10 egl,EGLDisplay display,        EGLConfig[] configs)        {        for(EGLConfig config:configs){        int d = findConfigAttrib(egl, display, config,                        EGL10.EGL_DEPTH_SIZE, 0);                int s = findConfigAttrib(egl, display, config,                        EGL10.EGL_STENCIL_SIZE, 0);                // We need at least mDepthSize and mStencilSize bits                if (d < mDepthSize || s < mStencilSize)                    continue;                // We want an *exact* match for red/green/blue/alpha                int r = findConfigAttrib(egl, display, config,                        EGL10.EGL_RED_SIZE, 0);                int g = findConfigAttrib(egl, display, config,                            EGL10.EGL_GREEN_SIZE, 0);                int b = findConfigAttrib(egl, display, config,                            EGL10.EGL_BLUE_SIZE, 0);                int a = findConfigAttrib(egl, display, config,                        EGL10.EGL_ALPHA_SIZE, 0);                if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)                    return config;        }        return null;        }                private int findConfigAttrib(EGL10 egl, EGLDisplay display,                EGLConfig config, int attribute, int defaultValue) {            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {                return mValue[0];            }            return defaultValue;        }                // Subclasses can adjust these values:        protected int mRedSize;        protected int mGreenSize;        protected int mBlueSize;        protected int mAlphaSize;        protected int mDepthSize;        protected int mStencilSize;        private int[] mValue = new int[1];}

    第四步:实现Renderer

    private static class Renderer implements GLSurfaceView.Renderer {        public void onDrawFrame(GL10 gl) {            GL2JNILib.step();        }        public void onSurfaceChanged(GL10 gl, int width, int height) {            GL2JNILib.init(width, height);        }        public void onSurfaceCreated(GL10 gl, EGLConfig config) {            // Do nothing.        }    }

    第五步:实现Renderer所用到的GL2JNILib,声明step,init方法

public class GL2JNILib {static{System.loadLibrary("gl2jni");}    /**     * @param width the current view width     * @param height the current view height     */     public static native void init(int width, int height);     public static native void step();}

    第六步:编写Android.mk

LOCAL_PATH := $(call my-dir)   include $(CLEAR_VARS)   LOCAL_MODULE    := gl2jni   LOCAL_SRC_FILES := gl_code.cpp   LOCAL_LDLIBS    := -llog -lGLESv2   include $(BUILD_SHARED_LIBRARY)

    第七步:使用cygwin编译jni目录下的gl_code.cpp,Android.mk,注意修改一下下面函数的包名为你的工程包名

extern "C" {    JNIEXPORT void JNICALL Java_com_example_ndkdemo_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height);    JNIEXPORT void JNICALL Java_com_example_ndkdemo_GL2JNILib_step(JNIEnv * env, jobject obj);};JNIEXPORT void JNICALL Java_com_example_ndkdemo_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height){    setupGraphics(width, height);}JNIEXPORT void JNICALL Java_com_example_ndkdemo_GL2JNILib_step(JNIEnv * env, jobject obj){    renderFrame();}

    最终结果,在一个配置很渣的Coolpad上:

                  




如果本文给你带来实际的学习价值,请尊重作者的劳动成果

    



0 0
原创粉丝点击