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

发表于2017-09-14
评论0 6.7k浏览

在做安卓开发时,经常使用java语言实现在Android中使用OpenGL,现在我们就尝试用NDK来实现一次。实现的思路就是将渲染器中的onDrawFrame,onSurfaceChanged,onSurfaceCreated分别在C中实现,然后将C编译成.so文件之后在Java中直接调用相应的函数就可以了。


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

主Activity:

  1. package com.empty.ndkgl;  
  2.   
  3. import com.example.ndkgl.R;  
  4.   
  5. import android.opengl.GLSurfaceView;  
  6. import android.os.Bundle;  
  7. import android.app.Activity;  
  8. import android.view.Menu;  
  9.   
  10. public class NdkGlActivity extends Activity {  
  11.   
  12.     @Override  
  13.     protected void onCreate(Bundle savedInstanceState) {  
  14.         super.onCreate(savedInstanceState);  
  15.         GLSurfaceView surface = new GLSurfaceView(this);    
  16.         surface.setRenderer(new NdkGlRender());    
  17.         setContentView(surface);   
  18.     }  
  19.     static {    
  20.         //load library     
  21.         System.loadLibrary("NdkGLRenderer");    
  22.    }    
  23.     @Override  
  24.     public boolean onCreateOptionsMenu(Menu menu) {  
  25.         // Inflate the menu; this adds items to the action bar if it is present.  
  26.         getMenuInflater().inflate(R.menu.activity_ndkgl, menu);  
  27.         return true;  
  28.     }  
  29.   
  30. }  

Render类代码:

[java] view plain copy
  1. package com.empty.ndkgl;  
  2.   
  3. import javax.microedition.khronos.egl.EGLConfig;  
  4. import javax.microedition.khronos.opengles.GL10;  
  5.   
  6. import android.opengl.GLSurfaceView.Renderer;  
  7.   
  8. public class NdkGlRender implements Renderer{  
  9.   
  10.     //declare native function  
  11.      native private void onNdkSurfaceCreated ();    
  12.      native private void onNdkSurfaceChanged (int width, int height);    
  13.      native private void onNdkDrawFrame();  
  14.     @Override  
  15.     public void onDrawFrame(GL10 arg0) {  
  16.         // TODO Auto-generated method stub  
  17.         onNdkDrawFrame ();   
  18.     }  
  19.   
  20.     @Override  
  21.     public void onSurfaceChanged(GL10 gl, int width, int height) {  
  22.         // TODO Auto-generated method stub  
  23.         onNdkSurfaceChanged (width, height);  
  24.     }  
  25.   
  26.     @Override  
  27.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  28.         // TODO Auto-generated method stub  
  29.         onNdkSurfaceCreated ();  
  30.     }  
  31.       
  32.   
  33. }  

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

[plain] view plain copy
  1. javah -classpath bin/classes -d jni com.empty.ndkgl.NdkGlRender  

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

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

java.lang.UnsatisfiedLinkError的bug。

  1. #include   
  2. #include   
  3. unsigned int vbo[2];  
  4. float positions[12] = {1,-1,0, 1,1,0, -1,-1,0, -1,1,0};  
  5. short indices  [4]  = {0,1,2,3};  
  6. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)  
  7. {  
  8.     //生成两个缓存区对象  
  9.     glGenBuffers (2, vbo);  
  10.     //绑定第一个缓存对象  
  11.     glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);  
  12.     //创建和初始化第一个缓存区对象的数据  
  13.     glBufferData (GL_ARRAY_BUFFER, 4*12, positions, GL_STATIC_DRAW);  
  14.     //绑定第二个缓存对象  
  15.     glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo[1]);  
  16.     //创建和初始化第二个缓存区对象的数据  
  17.     glBufferData (GL_ELEMENT_ARRAY_BUFFER, 2*4, indices, GL_STATIC_DRAW);  
  18. }  
  19. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)  
  20. {  
  21.     //图形最终显示到屏幕的区域的位置、长和宽  
  22.     glViewport (0,0,width,height);  
  23.     //指定矩阵  
  24.     glMatrixMode   (GL_PROJECTION);  
  25.     //将当前的矩阵设置为glMatrixMode指定的矩阵  
  26.     glLoadIdentity ();  
  27.     glOrthof(-2, 2, -2, 2, -2, 2);  
  28. }  
  29.   
  30. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)  
  31. {  
  32.     //启用顶点设置功能,之后必须要关闭功能  
  33.     glEnableClientState (GL_VERTEX_ARRAY);  
  34.     //清屏  
  35.     glClearColor (0,0,1,1);  
  36.     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  37.     glMatrixMode (GL_MODELVIEW);  
  38.     glLoadIdentity ();  
  39.     glBindBuffer    (GL_ARRAY_BUFFER, vbo[0]);  
  40.     //定义顶点坐标  
  41.     glVertexPointer (3, GL_FLOAT, 0, 0);  
  42.     glBindBuffer    (GL_ELEMENT_ARRAY_BUFFER, vbo[1]);  
  43.     //按照参数给定的值绘制图形  
  44.     glDrawElements  (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);  
  45.     //关闭顶点设置功能  
  46.     glDisableClientState(GL_VERTEX_ARRAY);  
  47. }  

编写Android.mk

[plain] view plain copy
  1. #FileName:Android.mk  
  2. #Description:makefile of NdkGl  
  3. LOCAL_PATH := $(call my-dir)  
  4.   
  5. include $(CLEAR_VARS)  
  6.   
  7. LOCAL_MODULE    := NdkGLRenderer  
  8. LOCAL_SRC_FILES := com_empty_ndkgl_NdkGlRender.c  
  9.    
  10. LOCAL_LDLIBS := -lGLESv1_CM  
  11.   
  12. include $(BUILD_SHARED_LIBRARY)  

编译库

[plain] view plain copy
  1. $NDK_ROOT/ndk-build  

在Eclipse中运行程序



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

比如网格圆球:

  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. typedef unsigned char byte;  
  8. typedef struct {  
  9.    GLfloat x,y,z;  
  10. } XYZ;  
  11. float rotateQuad;  
  12. #define PI 3.14159265  
  13. #define DTOR PI/180  
  14. static byte indices[8]={0,1,1,2,2,3,3,0};  //索引数组  
  15.   
  16. void CreateUnitSphere(int dtheta,int dphi)  
  17. {  
  18.    int n;  
  19.    int theta,phi;  
  20.    XYZ p[4];  
  21.   
  22.    for (theta=-90;theta<=90-dtheta;theta =dtheta) {  
  23.       for (phi=0;phi<=360-dphi;phi =dphi) {  
  24.          n = 0;  
  25.          p[n].x = cos(theta*DTOR) * cos(phi*DTOR);  
  26.          p[n].y = cos(theta*DTOR) * sin(phi*DTOR);  
  27.          p[n].z = sin(theta*DTOR);  
  28.          n ;  
  29.          p[n].x = cos((theta dtheta)*DTOR) * cos(phi*DTOR);  
  30.          p[n].y = cos((theta dtheta)*DTOR) * sin(phi*DTOR);  
  31.          p[n].z = sin((theta dtheta)*DTOR);  
  32.          n ;  
  33.          p[n].x = cos((theta dtheta)*DTOR) * cos((phi dphi)*DTOR);  
  34.          p[n].y = cos((theta dtheta)*DTOR) * sin((phi dphi)*DTOR);  
  35.          p[n].z = sin((theta dtheta)*DTOR);  
  36.          n ;  
  37.          if (theta >=-90 && theta <= 90) {  
  38.             p[n].x = cos(theta*DTOR) * cos((phi dphi)*DTOR);  
  39.             p[n].y = cos(theta*DTOR) * sin((phi dphi)*DTOR);  
  40.             p[n].z = sin(theta*DTOR);  
  41.             n ;  
  42.          }  
  43.   
  44.          /* Do something with the n vertex facet p */  
  45.   
  46.         glVertexPointer(3, GL_FLOAT, 0, p);  
  47.         glDrawElements(GL_LINES, 8, GL_UNSIGNED_BYTE, indices);  
  48.   
  49.   
  50.       }  
  51.    }  
  52. }  
  53.   
  54.   
  55. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)  
  56. {  
  57.         // 启用阴影平滑  
  58.     glShadeModel(GL_SMOOTH);  
  59.   
  60.     // 黑色背景  
  61.     glClearColor(0, 0, 0, 0);  
  62.   
  63.     // 设置深度缓存  
  64.     glClearDepthf(1.0f);  
  65.     // 启用深度测试  
  66.     glEnable(GL_DEPTH_TEST);  
  67.     // 所作深度测试的类型  
  68.     glDepthFunc(GL_LEQUAL);  
  69.   
  70.     // 告诉系统对透视进行修正  
  71.     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);}  
  72. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)  
  73. {  
  74.     //图形最终显示到屏幕的区域的位置、长和宽  
  75.     glViewport (0,0,width,height);  
  76.     //指定矩阵  
  77.     glMatrixMode   (GL_PROJECTION);  
  78.     //将当前的矩阵设置为glMatrixMode指定的矩阵  
  79.     glLoadIdentity ();  
  80.     glOrthof(-2, 2, -2, 2, -2, 2);  
  81. }  
  82.   
  83. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)  
  84. {  
  85.      //启用顶点设置功能,之后必须要关闭功能  
  86.     glEnableClientState (GL_VERTEX_ARRAY);  
  87.      //清屏  
  88.     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  89.   
  90.     glMatrixMode (GL_MODELVIEW);  
  91.     glLoadIdentity ();   
  92.     glFrontFace(GL_CW);  
  93.   
  94.     glRotatef(rotateQuad, 1.0f, 1.0f, 0.0f);//旋转效果  
  95.     CreateUnitSphere(10,10);  
  96.      //关闭顶点设置功能  
  97.      glDisableClientState(GL_VERTEX_ARRAY);  
  98.     rotateQuad -= 1.5f;  
  99. }  



立方体

  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #define col 1.0f  
  8. #define pos 1.0f  
  9. #define PI 3.14159265  
  10. static GLfloat vertex[] = {  
  11.                -pos,-pos,-pos,  /*0*/  
  12.                 -pos,-pos,pos,  /*1*/  
  13.                 pos,-pos,pos,   /*2*/  
  14.                 pos,-pos,-pos,  /*3*/  
  15.                 -pos,pos,-pos,  /*4*/  
  16.                 -pos,pos,pos,   /*5*/  
  17.                 pos,pos,pos,    /*6*/  
  18.                 pos,pos,-pos,   /*7*/  
  19.        };  
  20.          
  21. static GLfloat colors[] = {  
  22.                col,0,0,col,  
  23.                 0,col,0,col,  
  24.                 0,0,col,col,  
  25.                 col,col,0,col,  
  26.                 col,0,col,col,  
  27.                 0,col,col,col,  
  28.                 0,0,0,col,  
  29.                 col,col,col,col,  
  30.        };  
  31.          
  32. static GLubyte mindex[] = {  
  33.                 0,2,1,  0,3,2,  
  34.                 5,1,6,  6,1,2,  
  35.                 6,2,7,  7,2,3,  
  36.                 0,4,3,  4,7,3,  
  37.                 4,0,1,  4,1,5,  
  38.                 4,5,6,  4,6,7,  
  39.        };  
  40.   
  41. static GLfloat angle = 0.0f;  
  42.   
  43.   
  44. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceCreated (JNIEnv* env, jobject obj)  
  45. {  
  46.     // 启用阴影平滑  
  47.     glShadeModel(GL_SMOOTH);  
  48.   
  49.     // 黑色背景  
  50.     glClearColor(0, 0, 0, 0);  
  51.   
  52.     // 设置深度缓存  
  53.     glClearDepthf(1.0f);  
  54.     // 启用深度测试  
  55.     glEnable(GL_DEPTH_TEST);  
  56.     // 所作深度测试的类型  
  57.     glDepthFunc(GL_LEQUAL);  
  58.     // 告诉系统对透视进行修正  
  59.     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);  
  60. }  
  61. static void _gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)    
  62. {    
  63.     GLfloat top = zNear * ((GLfloat) tan(fovy * PI / 360.0));    
  64.     GLfloat bottom = -top;    
  65.     GLfloat left = bottom * aspect;    
  66.     GLfloat right = top * aspect;    
  67.     glFrustumf(left, right, bottom, top, zNear, zFar);    
  68. }  
  69. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkSurfaceChanged(JNIEnv* env, jobject obj, jint width, jint height)  
  70. {  
  71.     if (height==0)                              // 防止被零除    
  72.       {    
  73.               height=1;                         // 将Height设为1    
  74.       }    
  75.     
  76.       glViewport(0, 0, width, height);                  // 重置当前的视口    
  77.       glMatrixMode(GL_PROJECTION);                      // 选择投影矩阵    
  78.       glLoadIdentity();                         // 重置投影矩阵    
  79.     
  80.       GLfloat ratio = (GLfloat)width/(GLfloat)height;    
  81.       // 设置视口的大小    
  82.       _gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);    
  83.   //    glOrthof(-2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f);    
  84.     
  85.       glMatrixMode(GL_MODELVIEW);                       // 选择模型观察矩阵    
  86.       glLoadIdentity();                         // 重置模型观察矩阵  
  87. }  
  88.   
  89. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)  
  90. {  
  91.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);         
  92.     glMatrixMode(GL_MODELVIEW);  
  93.     glLoadIdentity();  
  94.     glTranslatef(0, 0, -8.0f);  
  95.     glRotatef(angle, 0, 1.0F, 0);  
  96.     glRotatef(angle, 0, 0, 1.0F);  
  97.            
  98.     glEnableClientState(GL_VERTEX_ARRAY);  
  99.     glEnableClientState(GL_COLOR_ARRAY);  
  100.             
  101.     glVertexPointer(3,GL_FLOAT,0,vertex);  
  102.     glColorPointer(4,GL_FLOAT,0,colors);  
  103.             
  104.     glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_BYTE,mindex);         
  105.     glDisableClientState(GL_VERTEX_ARRAY);  
  106.     glDisableClientState(GL_COLOR_ARRAY);  
  107.     angle  = 1.2f;  
  108. }  


打完收工。


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函数就可以了。

[java] view plain copy
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     mGLView = new DemoGLSurfaceView(this);  
  5.     requestWindowFeature(Window.FEATURE_NO_TITLE);    
  6.     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,     
  7.                     WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  8.     setContentView(mGLView);  
  9. }  

运行结果:




如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引