前面实现了Android有关OpenGL ES 2.0的一些例子,现在,把它改成用JNI实现。
以立方体为例。代码主要变化发生在Renderer里,以前直接用JAVA的,现在都移到C++里了。
代码和JAVA的实质上是一样的。
下面来看看主要的代码。
先看看工程结构:
上代码。
OpenGLJniActivity.java:
package com.jayce.eopengljni; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.content.pm.ConfigurationInfo; import android.opengl.GLSurfaceView; import android.os.Bundle; public class OpenGLJniActivity extends Activity { private GLSurfaceView mGLSurfaceView; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLSurfaceView = new GLSurfaceView(this); final ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); final ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo(); if(configInfo.reqGlEsVersion >= 0x20000) { mGLSurfaceView.setEGLContextClientVersion(2); OpenGLJniRenderer renderer = new OpenGLJniRenderer(); mGLSurfaceView.setRenderer(renderer); } setContentView(mGLSurfaceView); } @Override protected void onResume() { // The activity must call the GL surface view's onResume() on activity onResume(). super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // The activity must call the GL surface view's onPause() on activity onPause(). super.onPause(); mGLSurfaceView.onPause(); } }
上面这个和普通JAVA版的一模一样的。然后是OpenGLJniRenderer.java:
package com.jayce.eopengljni; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class OpenGLJniRenderer implements GLSurfaceView.Renderer { @Override public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub OpenGLJniLib.step(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { OpenGLJniLib.init(width, height); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { OpenGLJniLib.create(); } }
这个发生了不小的变化,主要的三个方法都换由C++实现了。
然后是native方法的包装类了。
OpenGLJniLib.java:
package com.jayce.eopengljni; public class OpenGLJniLib { static { System.loadLibrary("gljni"); } public static native void init(int width, int height); public static native void create(); public static native void step(); }
然后主要工作都在C++里做了。
opengljni.cpp:
#include <jni.h> #include <android/log.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include "common/Matrix.h" #define LOG_TAG "libgljni" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define POSITION_DATA_SIZE 3 #define COLOR_DATA_SIZE 4 GLfloat gMVPMatrix[16] = {0.0f}; GLfloat gViewMatrix[16] = {0.0f}; GLfloat gModelMatrix[16] = {0.0f}; GLfloat gProjectionMatrix[16] = {0.0f}; GLuint gMVPMatrixHandle = 0; GLuint gPositionHandle = 0; GLuint gColorHandle = 0; GLuint gProgram = 0; const GLfloat cubePosition[] = { -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f }; const GLfloat cubeColor[] = { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }; static const char gVertexShader[] = { "uniform mat4 u_MVPMatrix; \n" "attribute vec4 a_Position; \n" "attribute vec4 a_Color; \n" "varying vec4 v_Color; \n" "void main() \n" "{ \n" " v_Color = a_Color; \n" " gl_Position = u_MVPMatrix \n" " * a_Position; \n" "} \n" }; static const char gFragmentShader[] = { "precision mediump float; \n" "varying vec4 v_Color; \n" "void main() \n" "{ \n" " gl_FragColor = v_Color; \n" "} \n" }; GLuint loadShader(GLenum type, const char* source) { GLuint shader = glCreateShader(type); if(shader) { glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); GLint compileStatus = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if(!compileStatus) { GLint info_length = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_length); if(info_length) { char* buf = (char*)malloc(info_length * sizeof(char)); if(buf) { glGetShaderInfoLog(shader, info_length, NULL, buf); LOGE("Create shader %d failed\n%s\n", type, buf); } } glDeleteShader(shader); shader = 0; } } return shader; } GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) { GLuint vshader = loadShader(GL_VERTEX_SHADER, pVertexSource); if(!vshader) { return 0; } GLuint fshader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); if(!fshader) { return 0; } GLuint program = glCreateProgram(); if(program) { glAttachShader(program, vshader); glAttachShader(program, fshader); glBindAttribLocation(program, 0, "a_Position"); glBindAttribLocation(program, 1, "a_Color"); glLinkProgram(program); GLint status = 0; glGetProgramiv(program, GL_LINK_STATUS, &status); if(!status) { GLint info_length = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_length); if(info_length) { char* buf = (char*)malloc(info_length * sizeof(char)); glGetProgramInfoLog(program, info_length, NULL, buf); LOGE("create program failed\n%s\n", buf); } glDeleteProgram(program); program = 0; } } return program; } static GLfloat angleInDegrees = 0.1; void drawCube(const GLfloat* positions, const GLfloat* colors) { glVertexAttribPointer(gPositionHandle, POSITION_DATA_SIZE, GL_FLOAT, GL_FALSE, 0, positions); glEnableVertexAttribArray(gPositionHandle); glVertexAttribPointer(gColorHandle, COLOR_DATA_SIZE, GL_FLOAT, GL_FALSE, 0, colors); glEnableVertexAttribArray(gColorHandle); Matrix::multiplyMM(gMVPMatrix, 0, gViewMatrix, 0, gModelMatrix, 0); Matrix::multiplyMM(gMVPMatrix, 0, gProjectionMatrix, 0, gMVPMatrix, 0); glUniformMatrix4fv(gMVPMatrixHandle, 1, GL_FALSE, gMVPMatrix); glDrawArrays(GL_TRIANGLES, 0, 36); } void renderFrame() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(gProgram); gMVPMatrixHandle = glGetUniformLocation(gProgram, "u_MVPMatrix"); gPositionHandle = glGetAttribLocation(gProgram, "a_Position"); gColorHandle = glGetAttribLocation(gProgram, "a_Color"); Matrix::setIdentityM(gModelMatrix, 0); Matrix::translateM(gModelMatrix, 0, 0.0f, 0.0f, -5.0f); if(359.0 <= angleInDegrees) { angleInDegrees = 0.1; } else { angleInDegrees = angleInDegrees + 1.0; } Matrix::rotateM(gModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); drawCube(cubePosition, cubeColor); } extern "C" { JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_create(JNIEnv * env, jobject object); JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_init(JNIEnv * env, jobject object, jint width, jint height); JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_step(JNIEnv * env, jobject object); } JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_create(JNIEnv * env, jobject object) { LOGI("create"); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); const GLfloat eyeX = 0.0f; const GLfloat eyeY = 0.0f; const GLfloat eyeZ = -0.5f; const GLfloat lookX = 0.0f; const GLfloat lookY = 0.0f; const GLfloat lookZ = -5.0f; const GLfloat upX = 0.0f; const GLfloat upY = 1.0f; const GLfloat upZ = 0.0f; Matrix::setLookAtM(gViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); gProgram = createProgram(gVertexShader, gFragmentShader); } JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_init(JNIEnv * env, jobject object, jint width, jint height) { LOGI("init"); glViewport(0, 0, width, height); const GLfloat ratio = (GLfloat) width / height; const GLfloat left = -ratio; const GLfloat right = ratio; const GLfloat bottom = -1.0f; const GLfloat top = 1.0f; const GLfloat near = 1.0f; const GLfloat far = 10.0f; Matrix::frustumM(gProjectionMatrix, 0, left, right, bottom, top, near, far); } JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_step(JNIEnv * env, jobject object) { LOGI("step"); renderFrame(); }
这里面实现了三个native方法,还有一些辅助的函数。
这里用到的Matrix类是我根据android源码里的Matrix类进行改写。
内容和Matrix.java大同小异,只是语法上的差异,功能和Matrix.java完全一样。
还有一点,Matrix类我里面有些有关数组越界的问题我没有做严格的检测,如果要运用在实际项目里应该加上的。
好了,下面就是Make文件了,很简单,参考NDK里的例子应该很容易写出来。
Android.mk:
# 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. # LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := gljni LOCAL_CFLAGS := -Werror LOCAL_SRC_FILES := opengljni.cpp common/Matrix.cpp LOCAL_LDLIBS := -llog -lGLESv2 include $(BUILD_SHARED_LIBRARY)
如果NDK环境已安好,ndk-build命令就能正确编译出库了。
当然,也可以用eclipse的NDK,cdt辅助,看个人喜好。
最后,效果图,是一个旋转的立方体,跟纯JAVA版的是完全一样的。
转贴:http://www.cnblogs.com/jayceli/archive/2012/06/26/2564144.html