与context的关系_你还不知道 OpenGL ES 和 EGL 的关系?

2452e6069827e605d2d1894791c5a1bf.gif

什么是 EGL

EGL 是 OpenGL ES 和本地窗口系统(Native Window System)之间的通信接口,它的主要作用:
  • 与设备的原生窗口系统通信;

  • 查询绘图表面的可用类型和配置;

  • 创建绘图表面;

  • 在OpenGL ES 和其他图形渲染API之间同步渲染;

  • 管理纹理贴图等渲染资源。

OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异(Apple 提供了自己的 EGL API 的 iOS 实现,自称 EAGL)。

本地窗口相关的 API 提供了访问本地窗口系统的接口,而 EGL 可以创建渲染表面 EGLSurface ,同时提供了图形渲染上下文 EGLContext,用来进行状态管理,接下来 OpenGL ES 就可以在这个渲染表面上绘制。

90122985f58cb11171275462e12b8aea.png

egl、opengles 和设备之间的关系

图片中:

  • Display(EGLDisplay) 是对实际显示设备的抽象;

  • Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer(颜色缓冲区), Stencil Buffer(模板缓冲区) ,Depth Buffer(深度缓冲区);

  • Context (EGLContext) 存储 OpenGL ES 绘图的一些状态信息;

在 Android 平台上开发 OpenGL ES 应用时,类 GLSurfaceView 已经为我们提供了对 Display , Surface , Context 的管理,即 GLSurfaceView 内部实现了对 EGL 的封装,可以很方便地利用接口 GLSurfaceView.Renderer 的实现,使用 OpenGL ES API 进行渲染绘制,很大程度上提升了 OpenGLES 开发的便利性。

当然我们也可以自己实现对 EGL 的封装,本文就是在 Native 层对 EGL 进行封装,不借助于 GLSurfaceView ,实现图片后台渲染,利用 GPU 完成对图像的高效处理。

EGL 的应用

9a95ac6a639f88b4d49e11637f6241bd.gif

EGL 后台渲染实现效果图

使用 EGL 渲染的一般步骤:

  • 获取 EGLDisplay 对象,建立与本地窗口系统的连接调用 eglGetDisplay 方法得到 EGLDisplay。
  • 初始化 EGL 方法打开连接之后,调用 eglInitialize 方法初始化。
  • 获取 EGLConfig 对象,确定渲染表面的配置信息调用 eglChooseConfig 方法得到 EGLConfig。
  • 创建渲染表面 EGLSurface通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法创建渲染表面,得到 EGLSurface,其中 eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建屏幕外渲染区域。
  • 创建渲染上下文 EGLContext 通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext。
  • 绑定上下文通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后 OpenGLES 环境就创建好了,接下来便可以进行渲染。
  • 交换缓冲OpenGLES 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。
  • 释放 EGL 环境绘制结束后,不再需要使用 EGL 时,需要取消 eglMakeCurrent 的绑定,销毁  EGLDisplay、EGLSurface、EGLContext 三个对象。

代码实现:

// 创建 GLES 环境
int BgRender::CreateGlesEnv()
{
    // EGL config attributes
    const EGLint confAttr[] =
    {
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
            EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_ALPHA_SIZE, 8,// if you need the alpha channel
            EGL_DEPTH_SIZE, 8,// if you need the depth buffer
            EGL_STENCIL_SIZE,8,
            EGL_NONE
    };

    // EGL context attributes
    const EGLint ctxAttr[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };

    // surface attributes
    // the surface size is set to the input frame size
    const EGLint surfaceAttr[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT,1,
            EGL_NONE
    };
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;

    int resultCode = 0;
    do
    {
        //1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if(m_eglDisplay == EGL_NO_DISPLAY)
        {
            //Unable to open connection to local windowing system
            LOGCATE("BgRender::CreateGlesEnv Unable to open connection to local windowing system");
            resultCode = -1;
            break;
        }

        //2. 初始化 EGL 方法
        if(!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers))
        {
            // Unable to initialize EGL. Handle and recover
            LOGCATE("BgRender::CreateGlesEnv Unable to initialize EGL");
            resultCode = -1;
            break;
        }

        LOGCATE("BgRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);

        //3. 获取 EGLConfig 对象,确定渲染表面的配置信息
        if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
        {
            LOGCATE("BgRender::CreateGlesEnv some config is wrong");
            resultCode = -1;
            break;
        }

        //4. 创建渲染表面 EGLSurface, 使用 eglCreatePbufferSurface 创建屏幕外渲染区域
        m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
        if(m_eglSurface == EGL_NO_SURFACE)
        {
            switch(eglGetError())
            {
                case EGL_BAD_ALLOC:
                    // Not enough resources available. Handle and recover
                    LOGCATE("BgRender::CreateGlesEnv Not enough resources available");
                    break;
                case EGL_BAD_CONFIG:
                    // Verify that provided EGLConfig is valid
                    LOGCATE("BgRender::CreateGlesEnv provided EGLConfig is invalid");
                    break;
                case EGL_BAD_PARAMETER:
                    // Verify that the EGL_WIDTH and EGL_HEIGHT are
                    // non-negative values
                    LOGCATE("BgRender::CreateGlesEnv provided EGL_WIDTH and EGL_HEIGHT is invalid");
                    break;
                case EGL_BAD_MATCH:
                    // Check window and EGLConfig attributes to determine
                    // compatibility and pbuffer-texture parameters
                    LOGCATE("BgRender::CreateGlesEnv Check window and EGLConfig attributes");
                    break;
            }
        }

        //5. 创建渲染上下文 EGLContext
        m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
        if(m_eglCtx == EGL_NO_CONTEXT)
        {
            EGLint error = eglGetError();
            if(error == EGL_BAD_CONFIG)
            {
                // Handle error and recover
                LOGCATE("BgRender::CreateGlesEnv EGL_BAD_CONFIG");
                resultCode = -1;
                break;
            }
        }

        //6. 绑定上下文
        if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
        {
            LOGCATE("BgRender::CreateGlesEnv MakeCurrent failed");
            resultCode = -1;
            break;
        }
        LOGCATE("BgRender::CreateGlesEnv initialize success!");
    }
    while (false);

    if (resultCode != 0)
    {
        LOGCATE("BgRender::CreateGlesEnv fail");
    }

    return resultCode;
}

//渲染
void BgRender::Draw()
{
    LOGCATE("BgRender::Draw");
    if (m_ProgramObj == GL_NONE) return;
    glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);

    // Do FBO off screen rendering
    glUseProgram(m_ProgramObj);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);

    glBindVertexArray(m_VaoIds[0]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glUniform1i(m_SamplerLoc, 0);

    if (m_TexSizeLoc != GL_NONE) {
        GLfloat size[2];
        size[0] = m_RenderImage.width;
        size[1] = m_RenderImage.height;
        glUniform2fv(m_TexSizeLoc, 1, &size[0]);
    }

    //7. 渲染
    GO_CHECK_GL_ERROR();
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    GO_CHECK_GL_ERROR();
    glBindVertexArray(GL_NONE);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);

    //一旦解绑 FBO 后面就不能调用 readPixels
    //glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);

}

//释放 GLES 环境
void BgRender::DestroyGlesEnv()
{
    //8. 释放 EGL 环境
    if (m_eglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        eglDestroyContext(m_eglDisplay, m_eglCtx);
        eglDestroySurface(m_eglDisplay, m_eglSurface);
        eglReleaseThread();
        eglTerminate(m_eglDisplay);
    }

    m_eglDisplay = EGL_NO_DISPLAY;
    m_eglSurface = EGL_NO_SURFACE;
    m_eglCtx = EGL_NO_CONTEXT;

}

Java 层的代码,主要是一个 ImageView 用于展示渲染前后的图像。

// 创建渲染对象
NativeBgRender mBgRender = new NativeBgRender();
// 初始化创建 GLES 环境
mBgRender.native_BgRenderInit();
// 加载图片数据到纹理
loadRGBAImage(R.drawable.java, mBgRender);
// 离屏渲染
mBgRender.native_BgRenderDraw();
// 从缓冲区读出渲染后的图像数据,加载到 ImageView
mImageView.setImageBitmap(createBitmapFromGLSurface(0, 0, 421, 586));
// 释放 GLES 环境
mBgRender.native_BgRenderUnInit();


private void loadRGBAImage(int resId, NativeBgRender render) {
    InputStream is = this.getResources().openRawResource(resId);
    Bitmap bitmap;
    try {
        bitmap = BitmapFactory.decodeStream(is);
        if (bitmap != null) {
            int bytes = bitmap.getByteCount();
            ByteBuffer buf = ByteBuffer.allocate(bytes);
            bitmap.copyPixelsToBuffer(buf);
            byte[] byteArray = buf.array();
            render.native_BgRenderSetImageData(byteArray, bitmap.getWidth(), bitmap.getHeight());
        }
    }
    finally
    {
        try
        {
            is.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}

private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h) {
    int bitmapBuffer[] = new int[w * h];
    int bitmapSource[] = new int[w * h];
    IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
    intBuffer.position(0);
    try {
        GLES20.glReadPixels(x, y, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
                intBuffer);
        int offset1, offset2;
        for (int i = 0; i             offset1 = i * w;            offset2 = (h - i - 1) * w;            for (int j = 0; j                 int texturePixel = bitmapBuffer[offset1 + j];                int blue = (texturePixel >> 16) & 0xff;                int red = (texturePixel <16) & 0x00ff0000;                int pixel = (texturePixel & 0xff00ff00) | red | blue;                bitmapSource[offset2 + j] = pixel;            }        }    } catch (GLException e) {        return null;    }    return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);}

85ff782e3da3b2b549a4ed3a2b72607b.png

技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

96b1cdae901ce7446f287a3d52f49a0a.png

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

一文读懂 YUV 的采样与格式

OpenGL 之 GPUImage 源码分析

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

4887a65e6ac43f1090f8f0eb19f057b6.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/506618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ivew 的ajax,iView-Upload组件分析

源码分析xhr相关知识点Ajax要点分析拖拽事件以及粘贴事件具体实现总结xhr相关知识点XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象&#xff0c;用来表示上传的进度。通过onprogress属性进行监听,是在 XMLHttpRequest 完成之前周期性调用的函数。xhr.upload.onp…

安装thymeleaf 插件_史上最详细的WordPress安装教程(四):安装mysql 5.7

安装mysql添加源rpm -Uvh http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm#或wget http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpmrpm -ivh mysql57-community-release-el7-9.noarch.rpm安装yum -y install mysql-community-ser…

域名与网页服务器的什么对应,简述网站从域名到网页的访问流程

简述网站从域名到网页的访问流程时间&#xff1a;2019-12-301384 次浏览大家对上网并不陌生&#xff0c;不管是IT人士还是普通大众&#xff0c;不管用电脑还是手机&#xff0c;上网已变成大家生活中的一部分。什么是上网&#xff1f;简单点讲就是打开网页&#xff0c;说的详细一…

通过思科构造局域网_cisco设备构建典型局域网

典型局域网----三层交换机VLAN间路由和DHCP配置综合实验作者&#xff1a;Jason_Chenxz日期&#xff1a;2009.8.21内容&#xff1a;在一个典型局域网中实现三层交换机VTP、VLAN创建、VLAN间路由、上联设置以及DHCP服务&#xff0c;为各个VLAN分配IP地址、保留部分地址用于手动分…

分布式系统主从服务器,基于主从服务器的分布式存储系统的设计与实现

摘要&#xff1a;随着计算机网络,特别是Internet的迅猛发展,传统的信息系统概念发生了巨大的变化,基于网络的分布式信息系统在各个领域得到了广泛的应用,在整个社会生活中正发挥着日益突出的作用。Internet已经越来越多地成为构建信息系统的一个关键组成部分。如何在更为广域和…

中山大学曾兆阳_2010—2011学年度中山大学信科院优秀学生奖学金评选结果名单...

院系&#xff1a;信息科学与技术学院一等奖&#xff1a;获奖人数&#xff1a;沈招益姚良超张华张艺腾张起彤马楠熠刘宇熙陈水明赵钦耀张永福程嘉朗赖沛骏陈锦浩林舟驰林庆忠张嘉方晓敏符昊明薛政陈铮涛邓子恒刘冶马俊铖黄哲刘宸亨黄奕丰何浩汪翔郑东学章小强黄晓月二等奖&#…

如何修改7 服务器配置,centos7修改服务器配置

centos7修改服务器配置 内容精选换一换简要介绍Oases是一个转录组组装器&#xff0c;旨在没有任何基因组组装的情况下从短读测序技术生成转录本。开发语言&#xff1a;C一句话描述&#xff1a;基因组装软件开源协议&#xff1a;GPL 3.0建议的版本建议使用版本为最新版本&#x…

常用英雄胜率怎么刷_单排刷英雄胜率或者炸鱼方法。

适用对象&#xff1a;想刷英雄胜率又找不到车队一起刷的&#xff0c;亦或者单纯想炸鱼娱乐一下。适用英雄&#xff1a;有神装二打五终结比赛能力的射手&#xff0c;打野以及部分战士英雄&#xff0c;并且个人水准能使用以上英雄打上王者。准备工具&#xff1a;两个微信or qq号&…

贴纸效果_(新)AE插件:时尚印刷贴纸效果动画帽子眼镜胡须嘴唇图标社交标题库(3001)...

如何获取 | 点击底部“阅读原文”获取▎ 素材说明Live Stickers库为您提供无限的机会&#xff01;除了动画贴纸外&#xff0c;在项目中&#xff0c;您还将找到许多其他类别。外观类别包括眼睛情感&#xff0c;面具(如Snapchat应用程序中的面具)&#xff0c;凉爽的帽子&#xff…

逆向so_安卓逆向 | 分析调试与so调用实战

声明&#xff1a;本教程用于学习交流&#xff0c;如有侵权联系本人删除&#xff01;点击上方“逆向小白”&#xff0c;选择“加为星标”第一时间关注逆向技术干货&#xff01;使用fiddler抓取某app登录接口的时候&#xff0c;没有看到任何相关的数据包&#xff0c;猜测app做了防…

ide打开项目运行和调试按钮都是灰色的_如何开发一个IDE

IDE对于语言来说非常重要&#xff0c;让新手能更快入门&#xff0c;让老手能有更高的开发效率。所以我摸索着开发了Fanx语言的IDE。这里分享一些IDE内部工作原理和经验。IDE和编译器IDE为了实现功能&#xff0c;需要对源码进行解析。经过词法分析、语法分析、语义分析。相当于编…

电容的q值计算公式_在设计电路中电容容量大小、耐压等级选取详解 (转)

原文链接&#xff1a;在设计电路中电容容量大小、耐压等级选取详解 作者:张飞电容的计算 我们对电容的计算&#xff0c;目的是要知道&#xff0c;我们在电路中需要一个多大的电容。为什么要需要这么个电容&#xff1f;它的电压要多高&#xff1f;它的容量要多大&#xff1f;这是…

一个显示器分两个屏幕_桌面改造计划2.0:一个显示器不够那就两个,桌面好物分享...

日常生活节奏快&#xff0c;工作压力大应该目前年轻人的共识。为了适应快节奏的生活&#xff0c;使得大家往往不得不接受996或者997甚至更长的工作时间。而随着更长的工作时间也导致大家在电脑显示器前的时间更长&#xff0c;从而带来更多的不良影响&#xff0c;这两年过劳死的…

python寻找屏幕上的特定字符_库Turtle:用Python指挥小海龟在屏幕上绘图,流行的儿童编程...

海龟绘图&#xff08;Turtle Graphics&#xff09;是向儿童介绍编程的流行方式&#xff0c;源于Wally Feurzig和Seymour Papert1966年开发的LOGO语言。Python内置了Turtle库&#xff0c;基本包含所有原始功能。想象绘图区左上角有一只小海龟&#xff0c;代码就是指挥它动作的命…

flutterapp部分手机无法打开_Flutter run无法在iPhone上打开应用,直接在iphone也无法打开...

在电脑端运行flutter run后&#xff0c;报错信息如下&#xff1a;$ flutter runLaunching lib/main.dart on yuan的 iPhone in debug mode...Automatically signing iOS for device deployment using specified development team in Xcode project: 9D2Q3H854TRunning Xcode bu…

centos7 mysql 安装_CentOS7安装MySQL8.0图文教程(相对最齐全)

登录主机&#xff0c;然后进行下载第一步&#xff1a;在mysql官网下载源文件第二步&#xff1a;Select Operating System: 选择 Red Hat &#xff0c;CentOS 是基于红帽的&#xff0c;Select OS Version: 选择 linux 7第三步&#xff1a;选择 RPM Bundle 点击 Download第四步&a…

mysql定时导入_MySQL导入、导出、数据库定时备份

本篇使用的MySQL版本是5.7.30&#xff0c;注意不同版本之间可能会有差异。一、导出操作1、查找mysqldump命令位置which mysqldump2、mysqldump导出示例用户名和密码分别是root和123456&#xff1b;导出远程库&#xff0c;使用-hIP和-port端口&#xff0c;如下所示。后面的命令默…

ubuntu mysql 初始化_Ubuntu初始化MySQL碰到的坑

想着将MySQL初始化&#xff0c;改变一下存放数据文件的位置&#xff1a;rootubuntu:/lvmdata# mkdir datarootubuntu:/lvmdata# chown -R mysql:mysql /lvmdata/data修改MySQL配置文件&#xff1a;datadir /lvmdata/data然后初始化&#xff1a;rootubuntu:/lvmdata# mysqld --…

mysql 攻击密码_MySQL用户Root密码为弱口令的攻击

1、连接到对方MYSQL 服务器mysql -u root -h 192.168.0.1mysql.exe 这个程序在你安装了MYSQL的的BIN目录中。2、让我们来看看服务器中有些什么数据库mysqlshow databases; MYSQL默认安装时会有MYSQL、TEST这两个数据库&#xff0c;如果你看到有其它的数据库那么就是用户自建的数…

mysql封装执行_解决Mysql封装类执行报错——Mysql::insert() should not be called

今天在写程序的时候使用了别的程序员写的一个Mysql封装类&#xff0c;但是执行后php会报错&#xff1a;Strict standards: Non-static method Mysql::insert() should not be called statically in关于这个错误的的处理方法是修改类文件&#xff0c;将对应报错的“function ins…