您的位置:首页 > 移动开发 > Android开发

Android的NDK开发(2)-基于NDK的OpenGL开发

2013-01-12 19:00 393 查看
     之前在学习Android的时候有写过如果在Android中使用OpenGL,当时完全都是用java语言来实现的,现在我们用NDK来实现一次。

     实现的思路就是将渲染器中的onDrawFrame,onSurfaceChanged,onSurfaceCreated分别在C中实现,然后将C编译成.so文件之后在Java中直接调用相应的函数就可以了。

     步骤就不详细叙述了,代码贴一下。

    主Activity:

package com.empty.ndkgl;

import com.example.ndkgl.R;

import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class NdkGlActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView surface = new GLSurfaceView(this);
surface.setRenderer(new NdkGlRender());
setContentView(surface);
}
static {
//load library
System.loadLibrary("NdkGLRenderer");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_ndkgl, menu);
return true;
}

}


Render类代码:

package com.empty.ndkgl;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class NdkGlRender implements Renderer{

//declare native function
native private void onNdkSurfaceCreated ();
native private void onNdkSurfaceChanged (int width, int height);
native private void onNdkDrawFrame();
@Override
public void onDrawFrame(GL10 arg0) {
// TODO Auto-generated method stub
onNdkDrawFrame ();
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
onNdkSurfaceChanged (width, height);
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
onNdkSurfaceCreated ();
}

}


在工程目录下创建jni文件夹,用下面的命令生成.h文件。

javah -classpath bin/classes -d jni com.empty.ndkgl.NdkGlRender


根据头文件来创建.c文件。

注:虽然生产的 .h文件在编译的时候并没有什么作用,但还是建议做这一步,因为.c文件中的函数名一定要和.h文件中的函数名一致,最后的程序才能正常运行,不然会出现如

java.lang.UnsatisfiedLinkError的bug。

#include <jni.h>
#include <GLES/gl.h>
unsigned int vbo[2];
float positions[12] = {1,-1,0, 1,1,0, -1,-1,0, -1,1,0};
short indices  [4]  = {0,1,2,3};
JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)
{
//生成两个缓存区对象
glGenBuffers (2, vbo);
//绑定第一个缓存对象
glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);
//创建和初始化第一个缓存区对象的数据
glBufferData (GL_ARRAY_BUFFER, 4*12, positions, GL_STATIC_DRAW);
//绑定第二个缓存对象
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
//创建和初始化第二个缓存区对象的数据
glBufferData (GL_ELEMENT_ARRAY_BUFFER, 2*4, indices, GL_STATIC_DRAW);
}
JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)
{
//图形最终显示到屏幕的区域的位置、长和宽
glViewport (0,0,width,height);
//指定矩阵
glMatrixMode   (GL_PROJECTION);
//将当前的矩阵设置为glMatrixMode指定的矩阵
glLoadIdentity ();
glOrthof(-2, 2, -2, 2, -2, 2);
}

JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)
{
//启用顶点设置功能,之后必须要关闭功能
glEnableClientState (GL_VERTEX_ARRAY);
//清屏
glClearColor (0,0,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glBindBuffer    (GL_ARRAY_BUFFER, vbo[0]);
//定义顶点坐标
glVertexPointer (3, GL_FLOAT, 0, 0);
glBindBuffer    (GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
//按照参数给定的值绘制图形
glDrawElements  (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
//关闭顶点设置功能
glDisableClientState(GL_VERTEX_ARRAY);
}


编写Android.mk

#FileName:Android.mk
#Description:makefile of NdkGl
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := NdkGLRenderer
LOCAL_SRC_FILES := com_empty_ndkgl_NdkGlRender.c

LOCAL_LDLIBS := -lGLESv1_CM

include $(BUILD_SHARED_LIBRARY)


编译库

$NDK_ROOT/ndk-build


在Eclipse中运行程序



通过修改.c的实现,我们就可以绘制不同的图形。

比如网格圆球:

#include <jni.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GLES/gl.h>
typedef unsigned char byte;
typedef struct {
GLfloat x,y,z;
} XYZ;
float rotateQuad;
#define PI 3.14159265
#define DTOR PI/180
static byte indices[8]={0,1,1,2,2,3,3,0};  //索引数组

void CreateUnitSphere(int dtheta,int dphi)
{
int n;
int theta,phi;
XYZ p[4];

for (theta=-90;theta<=90-dtheta;theta+=dtheta) {
for (phi=0;phi<=360-dphi;phi+=dphi) {
n = 0;
p
.x = cos(theta*DTOR) * cos(phi*DTOR);
p
.y = cos(theta*DTOR) * sin(phi*DTOR);
p
.z = sin(theta*DTOR);
n++;
p
.x = cos((theta+dtheta)*DTOR) * cos(phi*DTOR);
p
.y = cos((theta+dtheta)*DTOR) * sin(phi*DTOR);
p
.z = sin((theta+dtheta)*DTOR);
n++;
p
.x = cos((theta+dtheta)*DTOR) * cos((phi+dphi)*DTOR);
p
.y = cos((theta+dtheta)*DTOR) * sin((phi+dphi)*DTOR);
p
.z = sin((theta+dtheta)*DTOR);
n++;
if (theta >=-90 && theta <= 90) {
p
.x = cos(theta*DTOR) * cos((phi+dphi)*DTOR);
p
.y = cos(theta*DTOR) * sin((phi+dphi)*DTOR);
p
.z = sin(theta*DTOR);
n++;
}

/* Do something with the n vertex facet p */

glVertexPointer(3, GL_FLOAT, 0, p);
glDrawElements(GL_LINES, 8, GL_UNSIGNED_BYTE, indices);

}
}
}

JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)
{
// 启用阴影平滑
glShadeModel(GL_SMOOTH);

// 黑色背景
glClearColor(0, 0, 0, 0);

// 设置深度缓存
glClearDepthf(1.0f);
// 启用深度测试
glEnable(GL_DEPTH_TEST);
// 所作深度测试的类型
glDepthFunc(GL_LEQUAL);

// 告诉系统对透视进行修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);}
JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)
{
//图形最终显示到屏幕的区域的位置、长和宽
glViewport (0,0,width,height);
//指定矩阵
glMatrixMode   (GL_PROJECTION);
//将当前的矩阵设置为glMatrixMode指定的矩阵
glLoadIdentity ();
glOrthof(-2, 2, -2, 2, -2, 2);
}

JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)
{
//启用顶点设置功能,之后必须要关闭功能
glEnableClientState (GL_VERTEX_ARRAY);
//清屏
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glFrontFace(GL_CW);

glRotatef(rotateQuad, 1.0f, 1.0f, 0.0f);//旋转效果
CreateUnitSphere(10,10);
//关闭顶点设置功能
glDisableClientState(GL_VERTEX_ARRAY);
rotateQuad -= 1.5f;
}




立方体

#include <jni.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GLES/gl.h>
#define col 1.0f
#define pos 1.0f
#define PI 3.14159265
static GLfloat vertex[] = {
-pos,-pos,-pos,	/*0*/
-pos,-pos,pos,	/*1*/
pos,-pos,pos,	/*2*/
pos,-pos,-pos,	/*3*/
-pos,pos,-pos,	/*4*/
-pos,pos,pos,	/*5*/
pos,pos,pos,	/*6*/
pos,pos,-pos,	/*7*/
};

static GLfloat colors[] = {
col,0,0,col,
0,col,0,col,
0,0,col,col,
col,col,0,col,
col,0,col,col,
0,col,col,col,
0,0,0,col,
col,col,col,col,
};

static GLubyte mindex[] = {
0,2,1,	0,3,2,
5,1,6,	6,1,2,
6,2,7,	7,2,3,
0,4,3,	4,7,3,
4,0,1,	4,1,5,
4,5,6,	4,6,7,
};

static GLfloat angle = 0.0f;

JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)
{
// 启用阴影平滑
glShadeModel(GL_SMOOTH);

// 黑色背景
glClearColor(0, 0, 0, 0);

// 设置深度缓存
glClearDepthf(1.0f);
// 启用深度测试
glEnable(GL_DEPTH_TEST);
// 所作深度测试的类型
glDepthFunc(GL_LEQUAL);
// 告诉系统对透视进行修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
}
static void _gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
{
GLfloat top = zNear * ((GLfloat) tan(fovy * PI / 360.0));
GLfloat bottom = -top;
GLfloat left = bottom * aspect;
GLfloat right = top * aspect;
glFrustumf(left, right, bottom, top, zNear, zFar);
}
JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)
{
if (height==0)                              // 防止被零除
{
height=1;                         // 将Height设为1
}

glViewport(0, 0, width, height);                  // 重置当前的视口
glMatrixMode(GL_PROJECTION);                      // 选择投影矩阵
glLoadIdentity();                         // 重置投影矩阵

GLfloat ratio = (GLfloat)width/(GLfloat)height;
// 设置视口的大小
_gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
//    glOrthof(-2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f);

glMatrixMode(GL_MODELVIEW);                       // 选择模型观察矩阵
glLoadIdentity();                         // 重置模型观察矩阵
}

JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -8.0f);
glRotatef(angle, 0, 1.0F, 0);
glRotatef(angle, 0, 0, 1.0F);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(3,GL_FLOAT,0,vertex);
glColorPointer(4,GL_FLOAT,0,colors);

glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_BYTE,mindex);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
angle += 1.2f;
}




打完收工。

2013.1.15日更新:

发现一个更强的demo,就在NDK的samples文件夹中,san-angeles就是了!

San Angeles Observation,XX大赛的冠军,原程序只有4K,被google收录到NDK的demo里面了,下面我们就跑一下它。

直接在Eclipse中创建Android工程,选择Android Project From Exiting Code。



直接跑的话会报错,提示无法初始化,我们必须先把c编译成.so.

命令行进入到项目文件夹,执行ndk-build



再修改一下Activity,让它全屏幕现实,只修改onCreate函数就可以了。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new DemoGLSurfaceView(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(mGLView);
}


运行结果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: