利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
来源:互联网 发布:js获取translatey的值 编辑:程序博客网 时间:2024/05/15 11:07
1、 打开Eclipse,File-->New-->Project…-->Android-->AndroidApplication Project,Next-->Application Name:FillTriangle, PackageName:com.filltriangle.android,Minimum Required SDK:API 10Android2.3.3(Gingerbread),Next-->不勾选Create customlauncher icon,Next-->选中Blank Activity,Next-->Activity Name:FillTriangle,Finish-->Runas Android Application,查看是否一切运行正常;
2、 打开FillTriangleActivity.java,将其内容改为:
package com.filltriangle.android;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.WindowManager;import java.io.File;public class FillTriangleActivity extends Activity { GL2JNIView mView; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); mView = new GL2JNIView(getApplication());setContentView(mView); } @Override protected void onPause() { super.onPause(); mView.onPause(); } @Override protected void onResume() { super.onResume(); mView.onResume(); }}
3、 新建2个java文件,选中com.filltriangle.android,点击右键,New-->Class,Name:GL2JNILib和Name:GL2JNIView;
4、GL2JNILib.java文件内容为:
package com.filltriangle.android;//Wrapper for native librarypublic 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();}
5、 GL2JNIView.java文件内容为:
package com.filltriangle.android;import android.content.Context;import android.graphics.PixelFormat;import android.opengl.GLSurfaceView;import android.util.AttributeSet;import android.util.Log;import android.view.KeyEvent;import android.view.MotionEvent;import javax.microedition.khronos.egl.EGL10;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.egl.EGLContext;import javax.microedition.khronos.egl.EGLDisplay;import javax.microedition.khronos.opengles.GL10;/** * A simple GLSurfaceView sub-class that demonstrate how to perform * OpenGL ES 2.0 rendering into a GL Surface. Note the following important * details: * * - The class must use a custom context factory to enable 2.0 rendering. * See ContextFactory class definition below. * * - The class must use a custom EGLConfigChooser to be able to select * an EGLConfig that supports 2.0. This is done by providing a config * specification to eglChooseConfig() that has the attribute * EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag * set. See ConfigChooser class definition below. * * - The class must select the surface's format, then choose an EGLConfig * that matches it exactly (with regards to red/green/blue/alpha channels * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */class GL2JNIView extends GLSurfaceView { private static String TAG = "GL2JNIView"; private static final boolean DEBUG = false; public GL2JNIView(Context context) { super(context); init(false, 0, 0); } public GL2JNIView(Context context, boolean translucent, int depth, int stencil) { super(context); init(translucent, depth, stencil); } private void init(boolean translucent, int depth, int stencil) { /* By default, GLSurfaceView() creates a RGB_565 opaque surface. * If we want a translucent one, we should change the surface's * format here, using PixelFormat.TRANSLUCENT for GL Surfaces * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. */ if (translucent) { this.getHolder().setFormat(PixelFormat.TRANSLUCENT); } /* Setup the context factory for 2.0 rendering. * See ContextFactory class definition below */ setEGLContextFactory(new ContextFactory()); /* We need to choose an EGLConfig that matches the format of * our surface exactly. This is going to be done in our * custom config chooser. See ConfigChooser class definition * below. */ setEGLConfigChooser( translucent ? new ConfigChooser(8, 8, 8, 8, depth, stencil) : new ConfigChooser(5, 6, 5, 0, depth, stencil) ); /* Set the renderer responsible for frame rendering */ setRenderer(new Renderer()); } private static class ContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { Log.w(TAG, "creating OpenGL ES 2.0 context"); checkEglError("Before eglCreateContext", egl); int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); checkEglError("After eglCreateContext", egl); return context; } public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { egl.eglDestroyContext(display, context); } } private static void checkEglError(String prompt, EGL10 egl) { int error; while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); } } 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; } /* This EGL config specification is used to specify 2.0 rendering. * We use a minimum size of 4 bits for red/green/blue, but will * perform actual matching in chooseConfig() below. */ 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) { /* Get the number of minimally matching EGL configurations */ 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); if (DEBUG) { printConfigs(egl, display, configs); } /* Now return the "best" one */ 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; } private void printConfigs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { int numConfigs = configs.length; Log.w(TAG, String.format("%d configurations", numConfigs)); for (int i = 0; i < numConfigs; i++) { Log.w(TAG, String.format("Configuration %d:\n", i)); printConfig(egl, display, configs[i]); } } private void printConfig(EGL10 egl, EGLDisplay display, EGLConfig config) { int[] attributes = { EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_BLUE_SIZE, EGL10.EGL_GREEN_SIZE, EGL10.EGL_RED_SIZE, EGL10.EGL_DEPTH_SIZE, EGL10.EGL_STENCIL_SIZE, EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_CONFIG_ID, EGL10.EGL_LEVEL, EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH, EGL10.EGL_NATIVE_RENDERABLE, EGL10.EGL_NATIVE_VISUAL_ID, EGL10.EGL_NATIVE_VISUAL_TYPE, 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE, 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE, EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE, 0x3042 // EGL10.EGL_CONFORMANT }; String[] names = { "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE", "EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE", "EGL_CONFIG_CAVEAT", "EGL_CONFIG_ID", "EGL_LEVEL", "EGL_MAX_PBUFFER_HEIGHT", "EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH", "EGL_NATIVE_RENDERABLE", "EGL_NATIVE_VISUAL_ID", "EGL_NATIVE_VISUAL_TYPE", "EGL_PRESERVED_RESOURCES", "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS", "EGL_SURFACE_TYPE", "EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE", "EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA", "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL", "EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE", "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE", "EGL_CONFORMANT" }; int[] value = new int[1]; for (int i = 0; i < attributes.length; i++) { int attribute = attributes[i]; String name = names[i]; if ( egl.eglGetConfigAttrib(display, config, attribute, value)) { Log.w(TAG, String.format(" %s: %d\n", name, value[0])); } else { // Log.w(TAG, String.format(" %s: failed\n", name)); while (egl.eglGetError() != EGL10.EGL_SUCCESS); } } } // 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]; } 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. } }}
6、编译该工程,会在bin\classes\com\filltriangle\android文件夹下生成GL2JNILib.class等文件;
7、打开命令行窗口,将其定位到\bin\classes目录下,输入命令:javah –classpath D:\ProgramFiles\Android\android-sdk\platforms\android-10\android.jar;(不用忘掉此分号) com.filltriangle.android.GL2JNILib,会在classes文件夹下生成com_filltriangle_android_GL2JNILib.h(说明:*.jar也可以是其它版本);
8、生成的com_filltriangle_android_GL2JNILib.h文件内容为:/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_filltriangle_android_GL2JNILib */#ifndef _Included_com_filltriangle_android_GL2JNILib#define _Included_com_filltriangle_android_GL2JNILib#ifdef __cplusplusextern "C" {#endif/* * Class: com_filltriangle_android_GL2JNILib * Method: init * Signature: (II)V */JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_init (JNIEnv *, jclass, jint, jint);/* * Class: com_filltriangle_android_GL2JNILib * Method: step * Signature: ()V */JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_step (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif
9、选中FillTriangle工程,点击右键-->New-->Folder新建一个jni文件夹,选中jni, -->New-->File,新建2个文件,名称分别为Android.mk和opengles_code.cpp;
10、Android.mk文件内容为:
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := libgl2jniLOCAL_CFLAGS := -WerrorLOCAL_SRC_FILES := opengles_code.cppLOCAL_LDLIBS := -llog -lGLESv2include $(BUILD_SHARED_LIBRARY)
11、opengles_code.cpp文件内容为:
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */// OpenGL ES 2.0 code#include <jni.h>#include <android/log.h>#include <GLES2/gl2.h>#include <GLES2/gl2ext.h>#include <stdio.h>#include <stdlib.h>#include <math.h>#define LOG_TAG "libgl2jni"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)static void printGLString(const char *name, GLenum s) { const char *v = (const char *) glGetString(s); LOGI("GL %s = %s\n", name, v);}static void checkGlError(const char* op) { for (GLint error = glGetError(); error; error = glGetError()) { LOGI("after %s() glError (0x%x)\n", op, error); }}static const char gVertexShader[] = "attribute vec4 vPosition;\n" "void main() {\n" " gl_Position = vPosition;\n" "}\n";static const char gFragmentShader[] = "precision mediump float;\n" "void main() {\n" " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" "}\n";GLuint loadShader(GLenum shaderType, const char* pSource) { GLuint shader = glCreateShader(shaderType); if (shader) { glShaderSource(shader, 1, &pSource, NULL); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); LOGE("Could not compile shader %d:\n%s\n", shaderType, buf); free(buf); } glDeleteShader(shader); shader = 0; } } } return shader;}GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) { GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); if (!vertexShader) { return 0; } GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); if (!pixelShader) { return 0; } GLuint program = glCreateProgram(); if (program) { glAttachShader(program, vertexShader); checkGlError("glAttachShader"); glAttachShader(program, pixelShader); checkGlError("glAttachShader"); glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, NULL, buf); LOGE("Could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(program); program = 0; } } return program;}GLuint gProgram;GLuint gvPositionHandle;bool setupGraphics(int w, int h) { printGLString("Version", GL_VERSION); printGLString("Vendor", GL_VENDOR); printGLString("Renderer", GL_RENDERER); printGLString("Extensions", GL_EXTENSIONS); LOGI("setupGraphics(%d, %d)", w, h); gProgram = createProgram(gVertexShader, gFragmentShader); if (!gProgram) { LOGE("Could not create program."); return false; } gvPositionHandle = glGetAttribLocation(gProgram, "vPosition"); checkGlError("glGetAttribLocation"); LOGI("glGetAttribLocation(\"vPosition\") = %d\n", gvPositionHandle); glViewport(0, 0, w, h); checkGlError("glViewport"); return true;}const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f };void renderFrame() { static float grey; grey += 0.01f; if (grey > 1.0f) { grey = 0.0f; } glClearColor(grey, grey, grey, 1.0f); checkGlError("glClearColor"); glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); checkGlError("glClear"); glUseProgram(gProgram); checkGlError("glUseProgram"); glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices); checkGlError("glVertexAttribPointer"); glEnableVertexAttribArray(gvPositionHandle); checkGlError("glEnableVertexAttribArray"); glDrawArrays(GL_TRIANGLES, 0, 3); checkGlError("glDrawArrays");}extern "C" { JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_init(JNIEnv * env, jobject obj, jint width, jint height); JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_step(JNIEnv * env, jobject obj);};JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_init(JNIEnv * env, jobject obj, jint width, jint height){ setupGraphics(width, height);}JNIEXPORT void JNICALL Java_com_filltriangle_android_GL2JNILib_step(JNIEnv * env, jobject obj){ renderFrame();}
12、利用NDK生成.so文件:选中工程,点击右键-->Properties-->Builders-->New,新建立一个Builder,在弹出的对话框上点中Program,点击OK;在弹出对话框EditConfiguration中,配置选项卡Main:Location中填入NDK安装目录,D:\ProgramFiles\Android\android-sdk\android-ndk-r9\ndk-build.cmd;WorkingDirectory中填入工程的根目录,E:\Test\Android\FillTriangle,点击Apply;配置选项卡Refresh,勾选Refreshresources upon completion, The entire workspace, Recursively includesub-folders,点击Apply;配置Build Options选项卡,勾选Allocate Console(necessary for input), After a “Clean”, Duringmanual builds, During auto builds, Specify working set of relevant resources,点击SpecifyResources..,勾选FillTriangle工程的jni目录,点击Finish,点击Apply,点击OK,会在\libs\armeabi目录下生成相应的libgl2jni.so库;
13、运行该工程,会显示绿色三角。
参考文献:
1、 以上代码来自adt-bundle-windows-x86_64-20130729中的例程;
2、 http://blog.csdn.net/fengbingchun/article/details/11580983- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
- 利用JNI技术在Android中调用C、调试C++代码(工具,详解)
- 利用JNI技术在Android中调用、调试C++代码
- 利用JNI技术在Android中调用、调试C++代码
- 利用JNI技术在Android中调用C++代码
- 【OpenGL/ES】 第02讲 Android JNI 调用OpenGL ES 2.0
- 《Android JNI》05 在JNI中调用Java的函数
- 采用JNI方法利用opengl es 1.x在android上绘图
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
- 如何阅读科研论文
- 移动共享工具快拿走红 或将引发手机内容传播革命
- iOS CFBundleDisplayName="XXXX";
- java反射详解
- 什么是wcf?wcf和webservice的区别
- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
- One Billion Customers
- 多线程(day13)
- poj 3628 01 背包
- 白求恩在中国是怎样解决性问题的
- Win7安装Ubuntu-Ubuntu下安装eclipse总结
- HDU 4726 Kia's Calculation
- 微信取消推送将葬送公众账号前途
- 斯大林逝世后的权利更迭