前面實現了Android有關OpenGL ES 2.0的一些例子,現在,把它改成用JNI實現。
以立方體為例。代碼主要變化發生在Renderer里,以前直接用JAVA的,現在都移到C++里了。
代碼和JAVA的實質上是一樣的。
下面來看看主要的代碼。
先看看工程結構:

上代碼。
OpenGLJniActivity.java:
1 package com.jayce.eopengljni; 2 3 import android.app.Activity; 4 import android.app.ActivityManager; 5 import android.content.Context; 6 import android.content.pm.ConfigurationInfo; 7 import android.opengl.GLSurfaceView; 8 import android.os.Bundle; 9 10 public class OpenGLJniActivity extends Activity 11 { 12 private GLSurfaceView mGLSurfaceView; 13 14 public void onCreate(Bundle savedInstanceState) 15 { 16 super.onCreate(savedInstanceState); 17 mGLSurfaceView = new GLSurfaceView(this); 18 final ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); 19 final ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo(); 20 if(configInfo.reqGlEsVersion >= 0x20000) 21 { 22 mGLSurfaceView.setEGLContextClientVersion(2); 23 OpenGLJniRenderer renderer = new OpenGLJniRenderer(); 24 mGLSurfaceView.setRenderer(renderer); 25 } 26 27 setContentView(mGLSurfaceView); 28 } 29 30 @Override 31 protected void onResume() 32 { 33 // The activity must call the GL surface view's onResume() on activity onResume(). 34 super.onResume(); 35 mGLSurfaceView.onResume(); 36 } 37 38 @Override 39 protected void onPause() 40 { 41 // The activity must call the GL surface view's onPause() on activity onPause(). 42 super.onPause(); 43 mGLSurfaceView.onPause(); 44 } 45 }
上面這個和普通JAVA版的一模一樣的。
然后是OpenGLJniRenderer.java:
1 package com.jayce.eopengljni; 2 3 import javax.microedition.khronos.egl.EGLConfig; 4 import javax.microedition.khronos.opengles.GL10; 5 import android.opengl.GLSurfaceView; 6 7 public class OpenGLJniRenderer implements GLSurfaceView.Renderer 8 { 9 10 @Override 11 public void onDrawFrame(GL10 gl) { 12 // TODO Auto-generated method stub 13 OpenGLJniLib.step(); 14 } 15 16 @Override 17 public void onSurfaceChanged(GL10 gl, int width, int height) { 18 OpenGLJniLib.init(width, height); 19 } 20 21 @Override 22 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 23 OpenGLJniLib.create(); 24 } 25 }
這個發生了不小的變化,主要的三個方法都換由C++實現了。
然后是native方法的包裝類了。
OpenGLJniLib.java:
1 package com.jayce.eopengljni; 2 3 public class OpenGLJniLib { 4 5 static { 6 System.loadLibrary("gljni"); 7 } 8 9 public static native void init(int width, int height); 10 public static native void create(); 11 public static native void step(); 12 }
然后主要工作都在C++里做了。
opengljni.cpp:
1 #include <jni.h> 2 #include <android/log.h> 3 4 #include <GLES2/gl2.h> 5 #include <GLES2/gl2ext.h> 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <math.h> 10 #include <string.h> 11 #include "common/Matrix.h" 12 13 #define LOG_TAG "libgljni" 14 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 15 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 16 17 #define POSITION_DATA_SIZE 3 18 #define COLOR_DATA_SIZE 4 19 20 GLfloat gMVPMatrix[16] = {0.0f}; 21 GLfloat gViewMatrix[16] = {0.0f}; 22 GLfloat gModelMatrix[16] = {0.0f}; 23 GLfloat gProjectionMatrix[16] = {0.0f}; 24 25 GLuint gMVPMatrixHandle = 0; 26 GLuint gPositionHandle = 0; 27 GLuint gColorHandle = 0; 28 GLuint gProgram = 0; 29 30 const GLfloat cubePosition[] = 31 { 32 -1.0f, 1.0f, 1.0f, 33 -1.0f, -1.0f, 1.0f, 34 1.0f, 1.0f, 1.0f, 35 -1.0f, -1.0f, 1.0f, 36 1.0f, -1.0f, 1.0f, 37 1.0f, 1.0f, 1.0f, 38 39 1.0f, 1.0f, 1.0f, 40 1.0f, -1.0f, 1.0f, 41 1.0f, 1.0f, -1.0f, 42 1.0f, -1.0f, 1.0f, 43 1.0f, -1.0f, -1.0f, 44 1.0f, 1.0f, -1.0f, 45 46 1.0f, 1.0f, -1.0f, 47 1.0f, -1.0f, -1.0f, 48 -1.0f, 1.0f, -1.0f, 49 1.0f, -1.0f, -1.0f, 50 -1.0f, -1.0f, -1.0f, 51 -1.0f, 1.0f, -1.0f, 52 53 -1.0f, 1.0f, -1.0f, 54 -1.0f, -1.0f, -1.0f, 55 -1.0f, 1.0f, 1.0f, 56 -1.0f, -1.0f, -1.0f, 57 -1.0f, -1.0f, 1.0f, 58 -1.0f, 1.0f, 1.0f, 59 60 -1.0f, 1.0f, -1.0f, 61 -1.0f, 1.0f, 1.0f, 62 1.0f, 1.0f, -1.0f, 63 -1.0f, 1.0f, 1.0f, 64 1.0f, 1.0f, 1.0f, 65 1.0f, 1.0f, -1.0f, 66 67 1.0f, -1.0f, -1.0f, 68 1.0f, -1.0f, 1.0f, 69 -1.0f, -1.0f, -1.0f, 70 1.0f, -1.0f, 1.0f, 71 -1.0f, -1.0f, 1.0f, 72 -1.0f, -1.0f, -1.0f 73 }; 74 75 const GLfloat cubeColor[] = 76 { 77 1.0f, 0.0f, 0.0f, 1.0f, 78 1.0f, 0.0f, 0.0f, 1.0f, 79 1.0f, 0.0f, 0.0f, 1.0f, 80 1.0f, 0.0f, 0.0f, 1.0f, 81 1.0f, 0.0f, 0.0f, 1.0f, 82 1.0f, 0.0f, 0.0f, 1.0f, 83 84 0.0f, 1.0f, 0.0f, 1.0f, 85 0.0f, 1.0f, 0.0f, 1.0f, 86 0.0f, 1.0f, 0.0f, 1.0f, 87 0.0f, 1.0f, 0.0f, 1.0f, 88 0.0f, 1.0f, 0.0f, 1.0f, 89 0.0f, 1.0f, 0.0f, 1.0f, 90 91 0.0f, 0.0f, 1.0f, 1.0f, 92 0.0f, 0.0f, 1.0f, 1.0f, 93 0.0f, 0.0f, 1.0f, 1.0f, 94 0.0f, 0.0f, 1.0f, 1.0f, 95 0.0f, 0.0f, 1.0f, 1.0f, 96 0.0f, 0.0f, 1.0f, 1.0f, 97 98 1.0f, 1.0f, 0.0f, 1.0f, 99 1.0f, 1.0f, 0.0f, 1.0f, 100 1.0f, 1.0f, 0.0f, 1.0f, 101 1.0f, 1.0f, 0.0f, 1.0f, 102 1.0f, 1.0f, 0.0f, 1.0f, 103 1.0f, 1.0f, 0.0f, 1.0f, 104 105 0.0f, 1.0f, 1.0f, 1.0f, 106 0.0f, 1.0f, 1.0f, 1.0f, 107 0.0f, 1.0f, 1.0f, 1.0f, 108 0.0f, 1.0f, 1.0f, 1.0f, 109 0.0f, 1.0f, 1.0f, 1.0f, 110 0.0f, 1.0f, 1.0f, 1.0f, 111 112 1.0f, 0.0f, 1.0f, 1.0f, 113 1.0f, 0.0f, 1.0f, 1.0f, 114 1.0f, 0.0f, 1.0f, 1.0f, 115 1.0f, 0.0f, 1.0f, 1.0f, 116 1.0f, 0.0f, 1.0f, 1.0f, 117 1.0f, 0.0f, 1.0f, 1.0f 118 }; 119 120 static const char gVertexShader[] = 121 { 122 "uniform mat4 u_MVPMatrix; \n" 123 "attribute vec4 a_Position; \n" 124 "attribute vec4 a_Color; \n" 125 126 "varying vec4 v_Color; \n" 127 128 "void main() \n" 129 "{ \n" 130 " v_Color = a_Color; \n" 131 " gl_Position = u_MVPMatrix \n" 132 " * a_Position; \n" 133 "} \n" 134 }; 135 136 static const char gFragmentShader[] = 137 { 138 "precision mediump float; \n" 139 "varying vec4 v_Color; \n" 140 "void main() \n" 141 "{ \n" 142 " gl_FragColor = v_Color; \n" 143 "} \n" 144 }; 145 146 GLuint loadShader(GLenum type, const char* source) 147 { 148 GLuint shader = glCreateShader(type); 149 if(shader) 150 { 151 glShaderSource(shader, 1, &source, NULL); 152 glCompileShader(shader); 153 GLint compileStatus = 0; 154 glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); 155 if(!compileStatus) 156 { 157 GLint info_length = 0; 158 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_length); 159 if(info_length) 160 { 161 char* buf = (char*)malloc(info_length * sizeof(char)); 162 if(buf) 163 { 164 glGetShaderInfoLog(shader, info_length, NULL, buf); 165 LOGE("Create shader %d failed\n%s\n", type, buf); 166 } 167 } 168 glDeleteShader(shader); 169 shader = 0; 170 } 171 } 172 return shader; 173 } 174 175 GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) 176 { 177 GLuint vshader = loadShader(GL_VERTEX_SHADER, pVertexSource); 178 if(!vshader) 179 { 180 return 0; 181 } 182 GLuint fshader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); 183 if(!fshader) 184 { 185 return 0; 186 } 187 GLuint program = glCreateProgram(); 188 if(program) 189 { 190 glAttachShader(program, vshader); 191 glAttachShader(program, fshader); 192 glBindAttribLocation(program, 0, "a_Position"); 193 glBindAttribLocation(program, 1, "a_Color"); 194 195 glLinkProgram(program); 196 197 GLint status = 0; 198 glGetProgramiv(program, GL_LINK_STATUS, &status); 199 200 if(!status) 201 { 202 GLint info_length = 0; 203 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_length); 204 if(info_length) 205 { 206 char* buf = (char*)malloc(info_length * sizeof(char)); 207 glGetProgramInfoLog(program, info_length, NULL, buf); 208 LOGE("create program failed\n%s\n", buf); 209 } 210 glDeleteProgram(program); 211 program = 0; 212 } 213 } 214 return program; 215 } 216 217 static GLfloat angleInDegrees = 0.1; 218 219 void drawCube(const GLfloat* positions, const GLfloat* colors) 220 { 221 glVertexAttribPointer(gPositionHandle, POSITION_DATA_SIZE, GL_FLOAT, GL_FALSE, 0, positions); 222 glEnableVertexAttribArray(gPositionHandle); 223 glVertexAttribPointer(gColorHandle, COLOR_DATA_SIZE, GL_FLOAT, GL_FALSE, 0, colors); 224 glEnableVertexAttribArray(gColorHandle); 225 Matrix::multiplyMM(gMVPMatrix, 0, gViewMatrix, 0, gModelMatrix, 0); 226 Matrix::multiplyMM(gMVPMatrix, 0, gProjectionMatrix, 0, gMVPMatrix, 0); 227 228 glUniformMatrix4fv(gMVPMatrixHandle, 1, GL_FALSE, gMVPMatrix); 229 glDrawArrays(GL_TRIANGLES, 0, 36); 230 } 231 232 void renderFrame() 233 { 234 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 235 glUseProgram(gProgram); 236 gMVPMatrixHandle = glGetUniformLocation(gProgram, "u_MVPMatrix"); 237 gPositionHandle = glGetAttribLocation(gProgram, "a_Position"); 238 gColorHandle = glGetAttribLocation(gProgram, "a_Color"); 239 240 Matrix::setIdentityM(gModelMatrix, 0); 241 Matrix::translateM(gModelMatrix, 0, 0.0f, 0.0f, -5.0f); 242 if(359.0 <= angleInDegrees) 243 { 244 angleInDegrees = 0.1; 245 } 246 else 247 { 248 angleInDegrees = angleInDegrees + 1.0; 249 } 250 251 Matrix::rotateM(gModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); 252 253 drawCube(cubePosition, cubeColor); 254 } 255 256 extern "C" 257 { 258 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_create(JNIEnv * env, jobject object); 259 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_init(JNIEnv * env, jobject object, jint width, jint height); 260 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_step(JNIEnv * env, jobject object); 261 } 262 263 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_create(JNIEnv * env, jobject object) 264 { 265 LOGI("create"); 266 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 267 glEnable(GL_CULL_FACE); 268 glEnable(GL_DEPTH_TEST); 269 const GLfloat eyeX = 0.0f; 270 const GLfloat eyeY = 0.0f; 271 const GLfloat eyeZ = -0.5f; 272 273 const GLfloat lookX = 0.0f; 274 const GLfloat lookY = 0.0f; 275 const GLfloat lookZ = -5.0f; 276 277 const GLfloat upX = 0.0f; 278 const GLfloat upY = 1.0f; 279 const GLfloat upZ = 0.0f; 280 281 Matrix::setLookAtM(gViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 282 gProgram = createProgram(gVertexShader, gFragmentShader); 283 } 284 285 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_init(JNIEnv * env, jobject object, jint width, jint height) 286 { 287 LOGI("init"); 288 glViewport(0, 0, width, height); 289 const GLfloat ratio = (GLfloat) width / height; 290 const GLfloat left = -ratio; 291 const GLfloat right = ratio; 292 const GLfloat bottom = -1.0f; 293 const GLfloat top = 1.0f; 294 const GLfloat near = 1.0f; 295 const GLfloat far = 10.0f; 296 297 Matrix::frustumM(gProjectionMatrix, 0, left, right, bottom, top, near, far); 298 } 299 300 JNIEXPORT void JNICALL Java_com_jayce_eopengljni_OpenGLJniLib_step(JNIEnv * env, jobject object) 301 { 302 LOGI("step"); 303 renderFrame(); 304 }
這里面實現了三個native方法,還有一些輔助的函數。
這里用到的Matrix類是我根據android源碼里的Matrix類進行改寫。
內容和Matrix.java大同小異,只是語法上的差異,功能和Matrix.java完全一樣。
還有一點,Matrix類我里面有些有關數組越界的問題我沒有做嚴格的檢測,如果要運用在實際項目里應該加上的。
好了,下面就是Make文件了,很簡單,參考NDK里的例子應該很容易寫出來。
Android.mk:
1 # Copyright (C) 2009 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # 15 LOCAL_PATH:= $(call my-dir) 16 17 include $(CLEAR_VARS) 18 19 LOCAL_MODULE := gljni 20 LOCAL_CFLAGS := -Werror 21 LOCAL_SRC_FILES := opengljni.cpp common/Matrix.cpp 22 LOCAL_LDLIBS := -llog -lGLESv2 23 24 include $(BUILD_SHARED_LIBRARY)
如果NDK環境已安好,ndk-build命令就能正確編譯出庫了。
當然,也可以用eclipse的NDK,cdt輔助,看個人喜好。
最后,效果圖,是一個旋轉的立方體,跟純JAVA版的是完全一樣的。

