通常在Android上使用OpenGL ES,都是希望把渲染后的结果显示在屏幕上,例如图片处理、模型显示等。这种情况下,只需要使用Android API中提供的GLSurfaceView类和Renderer类,在这两个类提供的初始化、回调函数中设置/编写相应的代码即可。不过,如果不希望把渲染结果显示在屏幕上,也就是所说的离屏渲染(offscreen render),这两个类就帮不上忙了。在此介绍一下如何在Android系统上做OpenGL ES 的离屏渲染。

1.基础知识

要想使用OpenGL ES,一般包括如下几个步骤:

  (1)EGL初始化

  (2)OpenGL ES初始化

  (3)OpenGL ES设置选项&绘制

  (4)OpenGL ES资源释放(可选)

  (5)EGL资源释放

Android提供的GLSurfaceView和Renderer自动完成了(1)(5)两个部分,这部分只需要开发者做一些简单配置即可。另外(4)这一步是可选的,因为随着EGL中上下文的销毁,openGL ES用到的资源也跟着释放了。因此只有(2)(3)是开发者必须做的。这大大简化了开发过程,但是灵活性也有所降低,利用这两个类是无法完成offscreen render的。要想完成offscreen render其实也很简单,相信大家也都猜到了,只要我们把(1)~(5)都自己完成就可以了。后续部分的代码大部分都是C/C++,少部分是Java。

2.EGL初始化

EGL的功能是将OpenGL ES API和设备当前的窗口系统粘合在一起,起到了沟通桥梁的作用。不同设备的窗口系统千变万化,但是OpenGL ES提供的API却是统一的,所以EGL需要协调当前设备的窗口系统和OpenGL ES。下面EGL初始化的代码我是用C++写的,然后通过jni调用。Android在Java层面上也提供了对应的Java接口函数。

static EGLConfig eglConf;
static EGLSurface eglSurface;
static EGLContext eglCtx;
static EGLDisplay eglDisp;JNIEXPORT void JNICALL Java_com_handspeaker_offscreentest_MyGles_init
(JNIEnv*env,jobject obj)
{// EGL config attributesconst EGLint confAttr[] ={EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,// very important!EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surfaceEGL_RED_SIZE,   8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE,  8,EGL_ALPHA_SIZE, 8,// if you need the alpha channelEGL_DEPTH_SIZE, 8,// if you need the depth bufferEGL_STENCIL_SIZE,8,EGL_NONE};// EGL context attributesconst EGLint ctxAttr[] = {EGL_CONTEXT_CLIENT_VERSION, 2,// very important!
            EGL_NONE};// surface attributes// the surface size is set to the input frame sizeconst EGLint surfaceAttr[] = {EGL_WIDTH,512,EGL_HEIGHT,512,EGL_NONE};EGLint eglMajVers, eglMinVers;EGLint numConfigs;eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY);if(eglDisp == EGL_NO_DISPLAY){//Unable to open connection to local windowing systemLOGI("Unable to open connection to local windowing system");}if(!eglInitialize(eglDisp, &eglMajVers, &eglMinVers)){// Unable to initialize EGL. Handle and recoverLOGI("Unable to initialize EGL");}LOGI("EGL init with version %d.%d", eglMajVers, eglMinVers);// choose the first config, i.e. best configif(!eglChooseConfig(eglDisp, confAttr, &eglConf, 1, &numConfigs)){LOGI("some config is wrong");}else{LOGI("all configs is OK");}// create a pixelbuffer surfaceeglSurface = eglCreatePbufferSurface(eglDisp, eglConf, surfaceAttr);if(eglSurface == EGL_NO_SURFACE){switch(eglGetError()){case EGL_BAD_ALLOC:// Not enough resources available. Handle and recoverLOGI("Not enough resources available");break;case EGL_BAD_CONFIG:// Verify that provided EGLConfig is validLOGI("provided EGLConfig is invalid");break;case EGL_BAD_PARAMETER:// Verify that the EGL_WIDTH and EGL_HEIGHT are// non-negative valuesLOGI("provided EGL_WIDTH and EGL_HEIGHT is invalid");break;case EGL_BAD_MATCH:// Check window and EGLConfig attributes to determine// compatibility and pbuffer-texture parametersLOGI("Check window and EGLConfig attributes");break;}}eglCtx = eglCreateContext(eglDisp, eglConf, EGL_NO_CONTEXT, ctxAttr);if(eglCtx == EGL_NO_CONTEXT){EGLint error = eglGetError();if(error == EGL_BAD_CONFIG){// Handle error and recoverLOGI("EGL_BAD_CONFIG");}}if(!eglMakeCurrent(eglDisp, eglSurface, eglSurface, eglCtx)){LOGI("MakeCurrent failed");}LOGI("initialize success!");
}

代码比较长,不过大部分都是检测当前函数调用是否出错的,核心的函数只有6个,只要它们的调用没有问题即可:

eglGetDisplay(EGL_DEFAULT_DISPLAY)

eglInitialize(eglDisp, &eglMajVers, &eglMinVers)

eglChooseConfig(eglDisp, confAttr, &eglConf, 1, &numConfigs)

eglCreatePbufferSurface(eglDisp, eglConf, surfaceAttr)

eglCreateContext(eglDisp, eglConf, EGL_NO_CONTEXT, ctxAttr)

eglMakeCurrent(eglDisp, eglSurface, eglSurface, eglCtx)

这些函数中参数的具体定义可以在这个网站找到:  https://www.khronos.org/registry/egl/sdk/docs/man/

需要说明的是,eglChooseConfig(eglDisp, confAttr, &eglConf, 1, &numConfigs)中confAttr参数一定要有EGL_SURFACE_TYPE,EGL_PBUFFER_BIT这个配置,它决定了是渲染surface的类型,是屏幕还是内存。另外,还有一些选项和OpenGL ES版本有关,具体选用1.x还是2.x,这个视个人情况而定,我使用的是2.x。

3.OpenGL ES部分

为了方便说明,我把OpenGL ES部分都写在一起了,代码如下:

JNIEXPORT void JNICALL Java_com_handspeaker_offscreentest_MyGles_draw
(JNIEnv*env,jobject obj)
{const char*vertex_shader=vertex_shader_fix;const char*fragment_shader=fragment_shader_simple;glPixelStorei(GL_UNPACK_ALIGNMENT,1);glClearColor(0.0,0.0,0.0,0.0);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);glCullFace(GL_BACK);glViewport(0,0,512,512);GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader,1,&vertex_shader,NULL);glCompileShader(vertexShader);GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader,1,&fragment_shader,NULL);glCompileShader(fragmentShader);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);glUseProgram(program);GLuint aPositionLocation =glGetAttribLocation(program, "a_Position");glVertexAttribPointer(aPositionLocation,2,GL_FLOAT,GL_FALSE,0,tableVerticesWithTriangles);glEnableVertexAttribArray(aPositionLocation);//draw somethingglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glDrawArrays(GL_TRIANGLES,0,6);eglSwapBuffers(eglDisp,eglSurface);
}

需要说明的是,绘制完成后,需要调用eglSwapBuffers(eglDisp,eglSurface)函数,因为在初始化EGL时默认设置的是双缓冲模式,即一份缓冲用于绘制图像,一份缓冲用于显示图像,每次显示时需要交换两份缓冲,把绘制好的图像显示出来。

上面openGL绘制需要的两个shader在此不写了,可供下载的demo里会有。

4.EGL资源释放

最后还差一个函数,用于EGL资源释放,代码如下:

JNIEXPORT void JNICALL Java_com_handspeaker_offscreentest_MyGles_release
(JNIEnv*env,jobject obj)
{eglMakeCurrent(eglDisp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);eglDestroyContext(eglDisp, eglCtx);eglDestroySurface(eglDisp, eglSurface);eglTerminate(eglDisp);eglDisp = EGL_NO_DISPLAY;eglSurface = EGL_NO_SURFACE;eglCtx = EGL_NO_CONTEXT;
}

5.总结

大功告成,其实吃透了openGL ES的原理后,整个过程还是很简单的。为了测试是否真的做到了offscreen render,我们把framebuffer中的图片保存成图片,来检测结果。代码如下:

        RGBABuffer = IntBuffer.allocate(512 * 512);MyGles.release();MyGles.init();MyGles.draw();RGBABuffer.position(0);GLES20.glReadPixels(0, 0, 512, 512,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE,RGBABuffer);int[] modelData=RGBABuffer.array();int[] ArData=new int[modelData.length];int offset1, offset2;for (int i = 0; i < 512; i++) {offset1 = i * 512;offset2 = (512 - i - 1) * 512;for (int j = 0; j < 512; j++) {int texturePixel = modelData[offset1 + j];int blue = (texturePixel >> 16) & 0xff;int red = (texturePixel << 16) & 0x00ff0000;int pixel = (texturePixel & 0xff00ff00) | red | blue;ArData[offset2 + j] = pixel;}}Bitmap modelBitmap = Bitmap.createBitmap(ArData,512,512,Bitmap.Config.ARGB_8888);saveBitmap(modelBitmap);

要注意的是,因为openGL ES framebuffer和图像通道的存储顺序不同,需要做一次ABGR到ARGB的转换。

一般来说,offscreen render的用处主要是做GPU加速,如果你的算法是计算密集型的,可以通过一些方法将其转化成位图形式,作为纹理(texture)载入到GPU显存中,再利用GPU的并行计算能力,对其进行处理,最后利用glReadPixels将计算结果读取到内存中。就说这么多吧,更多的用法还需要大家的发掘。

这里是demo下载链接

转载于:https://www.cnblogs.com/hrlnw/p/4642272.html

Android OpenGL ES 离屏渲染(offscreen render)相关推荐

  1. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  2. Android OpenGL ES 学习(十二) - MediaCodec + OpenGL 解析H264视频+滤镜

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  3. Android OpenGL ES视频渲染(一)GLSurfaceView

    相关文章:Android OpenGL ES视频渲染(二)EGL+OpenGL Android中视频渲染有几种方式,之前的文章使用的是nativewindow(包括softwareRender).今天 ...

  4. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  5. OpenGl文章 Android OpenGL ES 简明开发教程

    Android OpenGL ES 简明开发教程 分类:android学习笔记2011-12-14 15:04375人阅读评论(0)收藏举报 ApiDemos 的Graphics示例中含有OpenGL ...

  6. Android OpenGL ES 从入门到精通系统性学习教程

    1 为什么要写这个教程 目前这个 OpenGL ES 极简教程的更新暂时告一段落,在此之前,很荣幸获得了阮一峰老师的推荐. 因为在工作中频繁使用 OpenGL ES 做一些特效.滤镜之类的效果,加上平 ...

  7. Android OpenGL ES 画出三棱锥

    如今VR这么火,感觉有必要学学OpenGL.什么是OpenGL ES ,OpenGL ES (OpenGL for Embedded System ) 为适用于嵌入式系统的一个免费二维和三维图形库.O ...

  8. Android OpenGL ES 基础原理

    由于5G的发展,现在音视频越来越流行,我们的生活已经完全被抖音.视频号.B站等视频应用所包围.从这一点也能看到音视频的重要性. 而作为一名Android开发者,是时候来了解一下关于Android方面渲 ...

  9. Android OpenGL ES 学习(二) -- 图形渲染管线和GLSL

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

最新文章

  1. ubuntu18.04 -- 创建第一个Django项目
  2. boost::transform_iterator用法的测试程序
  3. ssm影城项目_影场与属性访问器界面
  4. 大数据的数据采集数据处理_让我们处理大数据
  5. bat 远程桌面登陆 命令_内网渗透之域渗透命令执行总结
  6. java分享第十六天( java读取properties文件的几种方法java配置文件持久化:static块的作用)...
  7. 20155313 2016-2017-2 《Java程序设计》第十周学习总结
  8. Go语言的线程模型-Goroutine机制
  9. 常用的电气制图软件有哪些?以及各自的优缺点
  10. maya! board_教你玩转MAYA的四十二精华造诣(第二期)
  11. python压缩包怎么打开-详解python解压压缩包的五种方法
  12. win7战网服务器修改,正在更新战网【设置办法】
  13. 设计模式之观察者模式(Observable与Observer)
  14. 蓝牙mesh中的TTL解读
  15. 线性代数 行列式(二)
  16. Sailfish OS
  17. 智能人体存在感知方案,毫米波雷达感应器成品,智能化感知联动应用
  18. 网站推广技巧-网站推广常用技巧教程
  19. 思科模拟器配置静态路由(下一跳使用端口)
  20. eclipse代码服务器运行不了,eclipse中run运行不了怎么回事?常见问题及解决方法...

热门文章

  1. IOS学习之IOS沙盒(sandbox)机制和文件操作之NSFileManager(三)
  2. Java程序结合Aapche Ant进行远程服务器文件备份
  3. linux下创建具有root权限的账户
  4. jquery eaayui 学习(三)tab
  5. mysql学习总结一:mysql的安装,介绍,基本命令操作
  6. MySQL入门系列:查询简介(四)之类型转换和分组查询
  7. 挑战程序设计竞赛2:算法和数据结构 (渡部有隆 著)
  8. 搭建Mysql-proxy实现主从同步读写分离
  9. 201671010128 2017-11-05《Java程序设计》之集合
  10. Android自定义泡泡效果 源码