前面实现了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