Android的NDK开发(2):基于NDK的OpenGL开发
发表于2017-09-14
在做安卓开发时,经常使用java语言实现在Android中使用OpenGL,现在我们就尝试用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
- #include
- 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
- #include
- #include
- #include
- #include
- #include
- 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
- #include
- #include
- #include
- #include
- #include
- #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);
- }
运行结果: