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 ES方案
在android应用程序中使用OpenGL ES共有四种方案:
1. 使用GLSurfaceView作为绘图的窗口,利用GLSurfaceView.Renderer接口实现OpenGL渲染上下文,并通过调用android.OpenGL.GLES20中的API函数实现对图像的渲染。
2. 使用GLSurfaceView作为绘图的窗口,使用GLSurfaceView.Renderer实现OpenGL渲染上下文,和1不一样的是通过JNI接口调用#include <GLES2/gl2.h>中的API函数来实现图形渲染。
3. 使用NativeActivity实现OpenGL渲染上下文,并通过JNI接口调用#include <GLES2/gl2.h>中的API函数来实现图形渲染。使用这种方案就意味着整个APP全部用C++语言编写,不能实现android基本控件的绘制,如Button/TextView。
4. 最后一种方法,就是使用SurfaceView作为绘图的窗口,并使用native层的pthread作为OpenGL渲染上下文,并通过pthread调用#include <GLES2/gl2.h>实现图形渲染。
综合以上方案内容,考虑实现效果以及入门难易度,采用方案2实现android 调用OpenGL.。
本手册以下内容将结合示例代码简述JNI接口实现OpenGL 曲线绘制的流程。示例代码采用Android Studio 2.2.2开发工具实现,详细内容参考示例代码文件(openGL_jni.rar)及参考资料。
绘制流程
工程文件目录:
./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控件。
openGLView.java
自定义OpenGL绘图组件openGLView类,继承GLSurfaceView,定义构造函数时需要指定使用OpenGL版本,并且设置绘图的渲染器:
其中new Renderer为自定义类,继承GLSurfaceView.Renderer接口。
OpenGL渲染上下文
选择方案1和方案2进行OpenGL绘图,渲染上下文都由GLSurfaceView类来维护。当用户调用GLSurfaceView.setRenderer函数绑定Renderer到GLSurfaceView时,GLSurfaceView会开启一个GLThread线程来初始化OpenGL渲染上下文,并循环运行来刷新渲染图像。
GLThread线程的工作为初始化渲染上下文,并调用GLSurfaceView.Renderer接口中的回调函数。线程执行的流程图如下图所示:
对以下接口函数调用GL2JNILib类函数进行实例化。
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,以及原生代码函数。
其中,System.LoadLibrary中库的名字应和CmakeList.txt中定义库的名字相同。将本地函数声明中加入publicstatic native关键字。
init(int width,int height)函数在onSurfaceChanged中调用,完成初始化工作。
step()函数在onDrawFrame中调用,实现绘制。
Ø 在native-lib.cpp代码文件添加如下内容
其中Java_com_example_dell_OpenGL_1jni_GL2JNILib_init()命名规则为java,java类完整包名称,函数名称。
可在声明为native的函数上使用android studio自动补全代码的功能生成对应的C++函数,并在其中加入需要的逻辑。
native-lib.cpp
Ø 定义着色器源代码(GLSL语言):顶点着色器、片段着色器(OpenGL项目必须包含的着色器)
顶点着色器定义显示坐标位置变量;片段着色器定义像素属性。
顶点着色器和片段着色器在编译好后将运行在GPU中,所以如果着色器中包含错误将导致编译失败或者屏幕黑屏,但是log日志不打印任何报错信息。
对于着色器脚本若想深入理解,请阅读《OpenGL ES 2.0 programmer guide》
Ø 加载着色器对象
在该函数中调用glCreateShader()函数,创建着色器对象,返回着色器对象ID值shader。
glShaderSource(shader, 1,&pSource, NULL);//关联着色器对象与着色器源代码
glCompileShader(shader);//编译着色器源代码
glGetShaderiv(shader,GL_COMPILE_STATUS, &compiled);//返回编译结果
glGetShaderInfoLog(shader,infoLen, NULL, buf);//返回着色器信息日志,常在编译失败调用。
Ø 加载着色器程序对象
调用函数加载着色器对象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类绘制曲线标度。
曲线绘制(10000点数据)CPU占用率11%~15%之间。
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