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

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

2013-01-12 19:00 357 查看
之前在学习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);     }

运行结果:



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