[Android JNI]Android的NDK开发-基于NDK的OpenGL开发

叁歲伎倆 2022-09-18 11:00 584阅读 0赞

之前在学习Android的时候有写过如果在Android中使用OpenGL,当时完全都是用java语言来实现的,现在我们用NDK来实现一次。

  1. 实现的思路就是将渲染器中的onDrawFrameonSurfaceChangedonSurfaceCreated分别在C中实现,然后将C编译成.so文件之后在Java中直接调用相应的函数就可以了。
  2. 步骤就不详细叙述了,代码贴一下。
  3. Activity

[java] view plain copy

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

Render类代码:

[java] view plain copy

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

在工程目录下创建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。

[cpp] view plain copy

  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. JNIEXPORT void JNICALL Java_com_empty_ndkgl_NdkGlRender_onNdkDrawFrame (JNIEnv* env, jobject obj)
  30. {
  31. //启用顶点设置功能,之后必须要关闭功能
  32. glEnableClientState (GL_VERTEX_ARRAY);
  33. //清屏
  34. glClearColor (0,0,1,1);
  35. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  36. glMatrixMode (GL_MODELVIEW);
  37. glLoadIdentity ();
  38. glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);
  39. //定义顶点坐标
  40. glVertexPointer (3, GL_FLOAT, 0, 0);
  41. glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
  42. //按照参数给定的值绘制图形
  43. glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
  44. //关闭顶点设置功能
  45. glDisableClientState(GL_VERTEX_ARRAY);
  46. }

编写Android.mk

[plain] view plain copy

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

编译库

[plain] view plain copy

  1. $NDK_ROOT/ndk-build

在Eclipse中运行程序

1357979824_3910.png

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

比如网格圆球:

[cpp] view plain copy

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

1357980846_5949.png

立方体

[cpp] view plain copy

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

1357988659_7346.png

打完收工。

2013.1.15日更新:

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

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

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

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

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

1358231747_7075.png

再修改一下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. }

运行结果:

1358231873_2119.png

1358232011_1588.png

发表评论

表情:
评论列表 (有 0 条评论,584人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android NDK开发基础

    最近在做视频处理这块儿,今天把原来整理的android ndk环境设置以及相关的android make文件的说明整理成如下文章。 现在做的内容涉及到了视频服务器端、手机an

    相关 NDK 开发

    为何要用到NDK? 概括来说主要分为以下几种情况: > 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。 > > 2. 在NDK