Android OpenGL ES 2.0画立方体JNI实现
http://www.cnblogs.com/jayceli/archive/2012/06/26/2564144.html
前面实现了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版的是完全一样的。