Android调用OpenGL绘制曲线入门手册

时间:2024-04-03 17:50:11

 

Android调用OpenGL绘制曲线入门手册

 

 

Android  OpenGL ES2.0 JNI  Cmake

简介:该手册内容仅作为Android调用OpenGL实现绘制曲线图的入门手册,深入学习请参考手册推荐书籍。本手册针对采用jni接口实现java调用C++代码完成OpenGL在Android中的曲线绘制做了详细流程介绍,最终实现效果为在Android平台绘制正弦曲线。

 

撰写人:武斌([email protected])周星宇

时间:2017年10月11日

CopyRight :东南大学电子科学与工程学院601实验室

版本:1.0

 

 

OpenGL简介:

OpenGL为跨编程语言、跨平台的编程接口标准,被当做客户端-服务端系统实现,客户端为应用程序,服务端为硬件厂商提供的OpenGL实现,OpenGL ES为其嵌入式版本。OpenGL ES目前共有三个版本发布,版本一提供不灵活的固定功能管道;版本二引入可编程管道;版本三在原有基础上新增部分API,目前主流使用为版本二OpenGL ES2.0,手册如下内容均用OpenGL代指OpenGL ES2.0。其渲染流程为:OpenGL命令和数据会缓存在RAM中,在一定条件下,会将这些命令和数据通过CPU时钟发送到VRAM,在GPU的控制下,使用VRAM中的数据和命令,完成图形的渲染,并将结果存入帧缓冲区中,帧缓冲区中的帧最终会被发送到显示器上,显示出结果。在现代的图形硬件系统中,还支持不通过CPU时钟直接将数据由RAM发送至顶点缓存区,再由顶点缓存区将数据传递到VRAM或直接将数据由像素帧缓冲区发送至显存(例如OpenGL中的VBO,PBO)。

Android调用OpenGL绘制曲线入门手册

Android调用OpenGL ES方案

在android应用程序中使用OpenGL ES共有四种方案:

1.     使用GLSurfaceView作为绘图的窗口,利用GLSurfaceView.Renderer接口实现OpenGL渲染上下文,并通过调用android.OpenGL.GLES20中的API函数实现对图像的渲染。

Android调用OpenGL绘制曲线入门手册

2.     使用GLSurfaceView作为绘图的窗口,使用GLSurfaceView.Renderer实现OpenGL渲染上下文,和1不一样的是通过JNI接口调用#include <GLES2/gl2.h>中的API函数来实现图形渲染。

Android调用OpenGL绘制曲线入门手册

3.     使用NativeActivity实现OpenGL渲染上下文,并通过JNI接口调用#include <GLES2/gl2.h>中的API函数来实现图形渲染。使用这种方案就意味着整个APP全部用C++语言编写,不能实现android基本控件的绘制,如Button/TextView。

Android调用OpenGL绘制曲线入门手册

4.     最后一种方法,就是使用SurfaceView作为绘图的窗口,并使用native层的pthread作为OpenGL渲染上下文,并通过pthread调用#include <GLES2/gl2.h>实现图形渲染。

Android调用OpenGL绘制曲线入门手册

综合以上方案内容,考虑实现效果以及入门难易度,采用方案2实现android 调用OpenGL.。

本手册以下内容将结合示例代码简述JNI接口实现OpenGL 曲线绘制的流程。示例代码采用Android Studio 2.2.2开发工具实现,详细内容参考示例代码文件(openGL_jni.rar)及参考资料。

绘制流程

工程文件目录:

   Android调用OpenGL绘制曲线入门手册

./cpp/native-lib.cpp为C++调用OpenGL 库接口函数文件。

./java/com.example.dell.opengl_jni/canvasview文件为 自定义canvas view类,实现刻度标注绘制功能

./java/com.example.dell.opengl_jni/GL2JNILib  为JNI接口类定义

./java/com.example.dell.opengl_jni/MainActivity 为程序主界面activity类

./java/com.example.dell.opengl_jni/openGLView 为自定义view控件,绘制曲线。

  Mainactivity.java

Activity加载的布局文件中添加分别表示Y轴刻度的Y_ScaleView控件、表示X轴刻度的X_ScaleView控件、表示曲线图的openGLView控件。

Android调用OpenGL绘制曲线入门手册

 

  openGLView.java

自定义OpenGL绘图组件openGLView类继承GLSurfaceView,定义构造函数时需要指定使用OpenGL版本,并且设置绘图的渲染器:

Android调用OpenGL绘制曲线入门手册

其中new Renderer为自定义类,继承GLSurfaceView.Renderer接口。

OpenGL渲染上下文

选择方案1和方案2进行OpenGL绘图,渲染上下文都由GLSurfaceView类来维护。当用户调用GLSurfaceView.setRenderer函数绑定Renderer到GLSurfaceView时,GLSurfaceView会开启一个GLThread线程来初始化OpenGL渲染上下文,并循环运行来刷新渲染图像。

Android调用OpenGL绘制曲线入门手册

GLThread线程的工作为初始化渲染上下文,并调用GLSurfaceView.Renderer接口中的回调函数。线程执行的流程图如下图所示:

Android调用OpenGL绘制曲线入门手册

 

对以下接口函数调用GL2JNILib类函数进行实例化。

Android调用OpenGL绘制曲线入门手册

Android调用OpenGL绘制曲线入门手册

Android调用OpenGL绘制曲线入门手册

OnSurfaceCreated(GL10  gl, EGLConfig config)函数在Surface创建时被GLsurfaceView调用,主要用于完成初始化工作。并且当用户在多个Activity中切换时,也会调用。

onSurfaceChanged(GL10gl, int width, int height)在 Surface创建后会自动调用,并且在检测平台横竖屏切换时调用,保证界面图像不发生形变做相应处理。

onDrawFrame(GL10gl)函数在程序执行后默认情况下会被系统Surface线程循环调用, 用于图像绘制。调用setRenderMode(int renderMode)函数可以设置渲染模式,可选模式有:

连续模式:传入参数RENDERMODE_CONTINUOUSLY,图像每隔16ms渲染一帧(60fps)。此模式适合于视频之类连续刷新的应用。

不连续模式:传入参数RENDERMODE_WHEN_DIRTY,当用户调用GLSurfaceView.requestRender()函数时才渲染一帧图像。此模式适合于图片浏览器之类不常更新画面的应用程序。

详见参考书籍《OpenGL ES 2 for Android A Quick - Start Guide (2013)》1.4节及官方API文档。

  GL2JNILib.java

JNI采用cmake方式实现在JAVA调用C++程序

JNI(java native interface)即java本地接口。Java的优点是跨平台,但是作为优点的同时,其在本地交互的时候就编程了缺点。Java的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java无法完成,于是Java提供了jni专门用于和本地代码交互,这样就增强了Java语言的本地交互能力。

在 Android Studio 2.2 之后提供2种选择编译 c/c++ 代码。分别为:ndk-build + Android.mk + Application.mk 以及 CMake +CMakeLists.txt 组合。这2个组合与Android代码和c/c++代码无关,只是不同的构建脚本和构建命令,其中后者为主流应用。

实现步骤

Ø  编写Cmakelist.txt脚本

脚本内容可仿照OpenGL资料配套例程。这里需要注意在target_link_libraries中加入EGL和GLESv2两项,否则会导致找不到OpenGL函数的报错。此语句作用为将OpenGL应用程序与android系统的OpenGL与EGL框架进行链接。

target_link_libraries(native-lib
                     
android
                      log
                      EGL
                     
GLESv2)

 

Ø  导入依赖库native-lib,以及原生代码函数。

Android调用OpenGL绘制曲线入门手册

其中,System.LoadLibrary中库的名字应和CmakeList.txt中定义库的名字相同。将本地函数声明中加入publicstatic native关键字。

init(int width,int height)函数在onSurfaceChanged中调用,完成初始化工作。

step()函数在onDrawFrame中调用,实现绘制。

 

Ø  在native-lib.cpp代码文件添加如下内容

Android调用OpenGL绘制曲线入门手册

其中Java_com_example_dell_OpenGL_1jni_GL2JNILib_init()命名规则为java,java类完整包名称,函数名称。

可在声明为native的函数上使用android studio自动补全代码的功能生成对应的C++函数,并在其中加入需要的逻辑。

  native-lib.cpp

Ø  定义着色器源代码(GLSL语言):顶点着色器、片段着色器(OpenGL项目必须包含的着色器)

顶点着色器定义显示坐标位置变量;片段着色器定义像素属性。

Android调用OpenGL绘制曲线入门手册

顶点着色器和片段着色器在编译好后将运行在GPU中,所以如果着色器中包含错误将导致编译失败或者屏幕黑屏,但是log日志不打印任何报错信息。

对于着色器脚本若想深入理解,请阅读《OpenGL ES 2.0 programmer guide》

Ø  加载着色器对象

Android调用OpenGL绘制曲线入门手册

在该函数中调用glCreateShader()函数,创建着色器对象,返回着色器对象ID值shader。

glShaderSource(shader, 1,&pSource, NULL);//关联着色器对象与着色器源代码

      glCompileShader(shader);//编译着色器源代码

glGetShaderiv(shader,GL_COMPILE_STATUS, &compiled);//返回编译结果

glGetShaderInfoLog(shader,infoLen, NULL, buf);//返回着色器信息日志,常在编译失败调用。

Ø  加载着色器程序对象

Android调用OpenGL绘制曲线入门手册

调用函数加载着色器对象loadShader(GL_VERTEX_SHADER,pVertexSource)

GLuint program = glCreateProgram();//创建着色器程序对象

glAttachShader(program, pixelShader);//关联着色器对象到着色器程序对象
glLinkProgram(program);//链接着色器程序对象成为可被GPU调度的二进制程序

Ø  init函数

setupGraphics(int w, int h)函数在init函数中调用,函数体内容如下。

1)         调用createProgram(gVertexShader,gFragmentShader),实现着色器程序对象的初始化。

2)         gvPositionHandle= glGetAttribLocation(gProgram, "vPosition");

gvValueHandle=glGetAttribLocation(gProgram,"vValue");

guFragColorHandle=glGetUniformLocation(gProgram,"uFragColor");

获得着色器中vPosition,vValue,uFragColor在程序执行时的位置。

3)         glViewport(0,0, w, h);//初始化屏幕信息

getXop();//生成X轴刻度数据

getYop();//生成Y轴刻度数据

Ø  step函数

定义renderFrame()函数,在step函数中调用,函数体内容如下。

生成曲线数据

updata();

设置背景颜色

glClearColor(grey, grey, grey,1.0f);//r,g,b,a其中a指透明度

清空绘制区域缓存,在OpenGL中有三种缓存数据,分别是颜色缓存,深度缓存和模板缓存,依据需要清除相应缓存数据。

glClear( GL_DEPTH_BUFFER_BIT |GL_COLOR_BUFFER_BIT);

通知GPU执行相应的着色器程序

glUseProgram(gProgram);

填充当前绑定顶点数组对象,启用与gvPositionHandle索引相关联的顶点数组,保证GPU可以拿到数据。

glVertexAttribPointer(gvPositionHandle, 1,GL_FLOAT, GL_FALSE, 0,xpos); glEnableVertexAttribArray(gvPositionHandle);

指定片段颜色

glUniform4f(guFragColorHandle, 0.75f,0.75f,0.75f, 1.0f);

绘制相应曲线

glDrawArrays(GL_LINE_STRIP, 0, frequency);

实现的绘制效果及性能如下:

由于OpenGL绘制汉字、数字等实现难度大,因此采用canvas类绘制曲线标度。

Android调用OpenGL绘制曲线入门手册

曲线绘制(10000点数据)CPU占用率11%~15%之间。

Android调用OpenGL绘制曲线入门手册

OpenGL学习参考链接:

http://www.twinklingstar.cn/2015/1532/introduce-to-OpenGL/

http://www.cnblogs.com/android-blogs/p/5454698.html

http://blog.csdn.net/qq_16564215/article/details/51764145

http://wondertwo.me/2017/04/14/OpenGL-%E7%BB%98%E5%88%B6%E7%9F%A9%E5%BD%A2/

http://www.qingpingshan.com/rjbc/az/119625.html

http://blog.csdn.net/kkae8643150/article/details/52805738、

http://blog.csdn.net/aa841538513/article/details/52291759(渲染过程)

http://www.android-doc.com/reference/android/opengl/GLSurfaceView.html

书籍推荐

《OpenGL ES 2 for Android A Quick - Start Guide(2013)》1~7章

中文版《OpenGL ES应用开发实践指南》

《OpenGL ES.2.0 Programming Guide(2008-7)》(按需阅读)

《OpenGL编程指南(第八版)》

《OpenGL超级宝典》

JNI学习参考链接:

http://www.jianshu.com/p/6332418b12b1

http://blog.csdn.net/carson_ho/article/details/73250163

http://www.cnblogs.com/tt2015-sz/p/6027662.html

示例代码:

https://download.csdn.net/download/u011361385/10356706