当前位置: 代码迷 >> Android >> Android的NDK开发(二)-基于NDK的OpenGL开发
  详细解决方案

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

热度:31   发布时间:2016-05-01 12:06:13.0
Android的NDK开发(2)-基于NDK的OpenGL开发

     之前在学习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 NdkGlLOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := NdkGLRendererLOCAL_SRC_FILES := com_empty_ndkgl_NdkGlRender.c LOCAL_LDLIBS := -lGLESv1_CMinclude $(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/180static 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[n].x = cos(theta*DTOR) * cos(phi*DTOR);         p[n].y = cos(theta*DTOR) * sin(phi*DTOR);         p[n].z = sin(theta*DTOR);         n++;         p[n].x = cos((theta+dtheta)*DTOR) * cos(phi*DTOR);         p[n].y = cos((theta+dtheta)*DTOR) * sin(phi*DTOR);         p[n].z = sin((theta+dtheta)*DTOR);         n++;         p[n].x = cos((theta+dtheta)*DTOR) * cos((phi+dphi)*DTOR);         p[n].y = cos((theta+dtheta)*DTOR) * sin((phi+dphi)*DTOR);         p[n].z = sin((theta+dtheta)*DTOR);         n++;         if (theta >=-90 && theta <= 90) {            p[n].x = cos(theta*DTOR) * cos((phi+dphi)*DTOR);            p[n].y = cos(theta*DTOR) * sin((phi+dphi)*DTOR);            p[n].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.14159265static 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);    }

运行结果:




  相关解决方案