OpenGL 是跨平台的、专业的图形编程接口,而接口的实现是由厂商来完成的。

而当我们使用这组接口完成绘制之后,要把结果显示在屏幕上,就要用到 EGL 来完成这个转换工作。

EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它也主要由厂商来实现。EGL 提供了如下机制:

  • 与设备的原生窗口系统通信

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

  • 创建绘图表面

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

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

为了让 OpenGL ES 能够绘制在当前设备上,我们需要 EGL 作为 OpenGL ES 与设备的桥梁。

我们可以直接用 GLSurfaceView 来进行 OpenGL 的渲染,就是因为在 GLSurfaceView 的内部已经完成了对 EGL 的使用封装,当然我们也可以封装自己的 EGL 环境。

EGL 使用实践

EGL 的使用要遵循一些固定的步骤,按照这些步骤去配置、创建、渲染、释放。

  • 创建与本地窗口系统的连接

    • 调用 eglGetDisplay 方法得到 EGLDisplay

  • 初始化 EGL 方法

    • 调用 eglInitialize 方法初始化

  • 确定渲染表面的配置信息

    • 调用 eglChooseConfig 方法得到 EGLConfig

  • 创建渲染上下文

    • 通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext

  • 创建渲染表面

    • 通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 方法创建渲染表面,得到 EGLSurface

  • 绑定上下文

    • 通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,接下来就可以使用 OpenGL 进行绘制了。

  • 交换缓冲

    • 当用 OpenGL 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上

  • 释放 EGL 环境

    • 绘制结束,不再需要使用 EGL 时,取消 eglMakeCurrent 的绑定,销毁  EGLDisplay、EGLSurface、EGLContext。

如果对 EGLDisplay、EGLSurface 、EGLContext 这些抽象概念傻傻分不清楚,可以参考这幅图:

其中:

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

  • Surface(EGLSurface)是对用来存储图像的内存区域

  • FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer

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

使用 EGL 的具体步骤如下:

创建与本地窗口系统的连接

通过 eglGetDisplay 方法创建与本地窗口系统的连接,返回的是 EGLDisplay 类型对象,可以把它抽象理解成设备的显示屏幕。

1    private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
2    // 创建与本地窗口系统的连接
3    mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
4    // 如果创建之后还是 EGL_NO_DISPLAY ,表示创建失败
5    if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
6        // failed
7    }

这一步就可以看成是选择显示设备,一般都是选择默认的显示设备,也就是手机屏幕。

初始化 EGL

创建 EGLDisplay 对象之后,要将它进行初始化。

1    // EGL 的主版本号
2    private int[] mMajorVersion = new int[1];
3    // EGL 的次版本号
4    private int[] mMinorVersion = new int[1];
5    boolean result = EGL14.eglInitialize(mEGLDisplay, mMajorVersion, 0, mMajorVersion, 0);
6    if (!result) {
7        // failed
8    }

初始化的时候可以获得 EGL 的主版本号与次版本号。

确定可用的渲染表面(Surface)配置

把 Surface 看成是一个渲染表面,渲染表面包含一些配置信息,也就是 EGLConfig 属性:

比如:

  • EGL_RED_SIZE:颜色缓冲区中红色用几位来表示

  • EGL_BLUE_SIZE:颜色缓冲区中蓝色用几位来表示

更多的 EGLConfig 属性参考这里:

https://www.jianshu.com/p/9db986365cda。

有两种方式用来确定可用的渲染表面配置。

通过 eglGetConfigs 的方法获取底层窗口系统支持的所有 EGL 渲染表面配置,再用 eglGetConfigAttrib 查询每个 EGLConfig 的信息。

1    public static native boolean eglGetConfigs(2        EGLDisplay dpy,3        EGLConfig[] configs,4        int configsOffset,5        int config_size,6        int[] num_config,7        int num_configOffset8    );9
10    public static native boolean eglGetConfigAttrib(
11        EGLDisplay dpy,
12        EGLConfig config,
13        int attribute,
14        int[] value,
15        int offset
16    );

另一种是创建好渲染表面配置列表,通过 eglChooseConfig 的方法,找到符合要求的 EGLConfig 配置。

1    public static native boolean eglChooseConfig(2        EGLDisplay dpy,3        int[] attrib_list,4        int attrib_listOffset,5        EGLConfig[] configs,6        int configsOffset,7        int config_size,8        int[] num_config,9        int num_configOffset
10    );

第二种方法相对于第一种来说,不用再查询每一个 EGLConfig 属性了。

具体使用如下:

1    // 定义 EGLConfig 属性配置2    private static final int[] EGL_CONFIG = {3            EGL14.EGL_RED_SIZE, CFG_RED_SIZE,4            EGL14.EGL_GREEN_SIZE, CFG_GREEN_SIZE,5            EGL14.EGL_BLUE_SIZE, CFG_BLUE_SIZE,6            EGL14.EGL_ALPHA_SIZE, CFG_ALPHA_SIZE,7            EGL14.EGL_DEPTH_SIZE, CFG_DEPTH_SIZE,8            EGL14.EGL_STENCIL_SIZE, CFG_STENCIL_SIZE,9            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
10            EGL14.EGL_NONE,
11    };

首先定义 EGLConfig 属性配置数组,定义红、绿、蓝、透明度、深度、模板缓冲的位数,最后要以 EGL14.EGL_NONE 结尾。

1        // 所有符合配置的 EGLConfig 个数2        int[] numConfigs = new int[1];3        // 所有符合配置的 EGLConfig4        EGLConfig[] configs = new EGLConfig[1];56        // configs 参数为 null,会在 numConfigs 中输出所有满足 EGL_CONFIG 条件的 config 个数7        EGL14.eglChooseConfig(mEGLDisplay, EGL_CONFIG, 0, null, 0, 0, numConfigs, 0);8        // 得到满足条件的个数9        int num = numConfigs[0];
10        if (num != 0) {
11            // 会获取所有满足 EGL_CONFIG 的 config
12            EGL14.eglChooseConfig(mEGLDisplay, EGL_CONFIG, 0, configs, 0, configs.length, numConfigs, 0);
13            // 去第一个
14            mEGLConfig = configs[0];
15        }

首先通过给 eglChooseConfig 相应参数设置为 null,找到符合条件的 EGLConfig 个数,如果不为空,则再一次调用,取第一个,就是想要的 EGLConfig 配置。

创建渲染上下文

有了 EGLDisplayEGLConfig 对象,就可以创建 渲染表面 EGLSurface和 渲染上下文 EGLContext

在创建渲染上下文时,同样要创建属性信息,主要是指定 OpenGL 使用版本。

1    private static final int[] EGL_ATTRIBUTE = {
2            EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
3            EGL14.EGL_NONE,
4    };

同样,还是以 EGL14.EGL_NONE 结尾。

1    private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
2    // 创建上下文
3    mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, EGL_ATTRIBUTE, 0);
4    if (mEGLContext == EGL14.EGL_NO_CONTEXT) {
5        // failed
6    }

创建渲染表面

EGL 提供了两种方式创建渲染表面,一种是可见的,渲染到屏幕上,一种是不可见的,也就离屏的。

  • eglCreatePbufferSurface:创建离屏的渲染表面

  • eglCreateWindowSurface:创建渲染到屏幕的渲染表面

无论使用哪种创建方式,也都需要创建配置信息。

1    // 创建渲染表面的配置信息
2    private static final int[] EGL_SURFACE = {
3            EGL14.EGL_NONE,
4    };
5    private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
6    // surface 参数是有 SurfaceView.getHolder 传递过来的
7    mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, EGL_SURFACE, 0);

对于渲染到屏幕上的创建,配置信息可以不添加什么,但还是要以 EGL14.EGL_NONE 结尾。

而对于离屏的渲染表面创建,就还需要提供宽、高等信息,但是却不需要提供 surface 的参数了。

1        // 使用 eglCreatePbufferSurface 就需要指定宽和高
2        int[] EGL_SURFACE = {
3                EGL10.EGL_WIDTH, w,
4                EGL10.EGL_HEIGHT, h,
5                EGL10.EGL_NONE,
6        };
7        mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, EGL_SURFACE,0);

绑定上下文

当有了 EGLDisplayEGLSurfaceEGLContext 对象之后,就可以为 EGLContext 绑定上下文了。

1 EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);

当绑定上下文之后,就可以执行具体的绘制操作了,调用 OpenGL 相关的方法绘制图形。

交换缓冲,进行显示

当绘制结束之后,就该把绘制结果显示在屏幕上了。

1        EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);

通过 eglSwapBuffers 方法,将后台绘制的缓冲显示到前台。

释放操作

当绘制结束时,要进行相应的释放操作。

1        EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
2        EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
3        EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
4        EGL14.eglReleaseThread();
5        EGL14.eglTerminate(mEGLDisplay);
6
7        mEGLContext = EGL14.EGL_NO_CONTEXT;
8        mEGLDisplay = EGL14.EGL_NO_DISPLAY;
9        mEGLConfig = null;

相当于就是把前面操作的 EGL 相关对象都释放掉。

当完成这样的一个流程之后,就基本上掌握了 EGL 的大部分使用。

EGL 操作的很多流程都是固定的,可以把它们再单独做一层封装,这里可以参考 grafika 的封装。

文章中具体代码部分,可以参考我的 Github 项目,欢迎 Star 。

https://github.com/glumes/AndroidOpenGLTutorial

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

私信领取相关资料

推荐阅读:

音视频开发工作经验分享 || 视频版

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

NDK 学习进阶免费视频来了

你想要的音视频开发资料库来了

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

觉得不错,点个在看呗~

OpenGL 之 EGL 使用实践相关推荐

  1. 《OpenGL ES应用开发实践指南:Android卷》—— 2.2 不要从头开始

    本节书摘来自华章出版社<OpenGL ES应用开发实践指南:Android卷>一 书中的第2章,第2.2节,作者:(美)Kevin Brothaler ,更多章节内容可以访问云栖社区&qu ...

  2. 《OpenGL ES应用开发实践指南:Android卷》—— 3.7 练习

    本节书摘来自华章出版社<OpenGL ES应用开发实践指南:Android卷>一 书中的第3章,第3.7节,作者:(美)Kevin Brothaler ,更多章节内容可以访问云栖社区&qu ...

  3. OpenGL ES应用开发实践指南:iOS卷

    <OpenGL ES应用开发实践指南:iOS卷> 基本信息 原书名:Learning OpenGL ES for iOS:A Hands-On Guide to Modern 3D Gra ...

  4. OpenGL与EGL最简流程(十八)

    为了方便深入研究OpenGL与EGL的关系,下面最简流程,可以理一下思路.  OpenGL与EGL最基本流程 //1.获取显示器 EGLDisplay display = eglGetDisplay ...

  5. OpenGL ES EGL eglDestroyContext

    目录 一. EGL 前言 二. EGL 绘制流程简介 三.eglDestroyContext 函数简介 四.eglDestroyContext 使用 四.猜你喜欢 零基础 OpenGL ES 学习路线 ...

  6. OpenGL ES EGL eglCreatePbufferSurface

    目录 一. EGL 前言 二. EGL 绘制流程简介 三.eglCreatePbufferSurface 函数简介 1.eglCreatePbufferSurface 简介 2.eglCreatePb ...

  7. OpenGL ES EGL eglQueryContext

    目录 一. EGL 前言 二. EGL 绘制流程简介 三.eglQueryContext 函数简介 四.eglQueryContext 使用 四.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : ...

  8. OpenGL ES EGL eglDestroySurface

    目录 一. EGL 前言 二. EGL 绘制流程简介 三.eglDestroySurface 函数简介 四.eglDestroySurface 使用 四.猜你喜欢 零基础 OpenGL ES 学习路线 ...

  9. OpenGL ES EGL 简介

    目录 一.EGL 简介 二.EGL 跨平台之 ANGLE 1.ANGLE 支持跨平台 2.ANGLE 支持渲染器 3.ANGLE 下载地址 三.EGL 坐标系 四.EGL 绘图步骤 五.猜你喜欢 零基 ...

  10. OpenGL ES EGL eglSwapBuffer

    目录 一. EGL 前言 二. EGL 绘制流程简介 三.eglSwapBuffer 函数简介 四.关于多个 EGLContext 五.共享 EGLContext 六.猜你喜欢 零基础 OpenGL ...

最新文章

  1. python使用imbalanced-learn的AllKNN方法进行下采样处理数据不平衡问题
  2. java.math.BigDecimal保留两位小数,保留小数,精确位数
  3. winform 鼠标 静止时间_赢得电竞的奥秘,你需要一块好鼠标垫
  4. Git生成ssh密钥指定文件
  5. 用OpenSSL写一个简单的Server/Client程序:证书与私钥
  6. 雷电模拟器android4.2,雷电安卓模拟器-雷电模拟器下载 v4.0.55.0官方版--pc6下载站...
  7. java缓存有几种_JAVA几种缓存技术介绍说明
  8. 微信小游戏源码(从入门到入坑-火柴人勇闯地下城))
  9. 计算机键盘上的每一个按键读音,电脑键盘上各键的名称功能及作用.doc
  10. DeepStream初步学习
  11. Windows上WinRAR.exe命令行参数说明
  12. 解决 Hyper-V R2 虚拟网卡影响网速变慢问题
  13. 【java后端】容器集合
  14. 【SSH】--SSH框架简介
  15. C++ 中父类与子类赋值,取地址,引用的理解关系
  16. HDFS 关闭安全模式
  17. 迪文DGUS智能屏如何轻松实现3D动画
  18. 比亚迪秦Pro修改默认导航为高德地图
  19. 2019.9.21-冒泡排序代码
  20. LoadRunner 是要保存此文件,还是要联机查找程序来打开此文件

热门文章

  1. 什么是 DNS MX 记录?
  2. python利用tcp搭建小的聊天室带文件传输
  3. 地理信息系统GIS小结
  4. java之自动化观看视频
  5. Simulink中scope变为白色背景
  6. Autovue 21.0.2.3 新功能介绍
  7. 面向对象思想----不看后悔!
  8. 【无机纳米材料科研制图——OriginLab 0209】Origin散点图线性拟合与非线性拟合
  9. su - root 和 su root 的区别
  10. python抓取京东商品评价总数_python爬虫抓取和分析京东商城评价