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 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[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.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);
    }

运行结果:




  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值