• OpenGL ES EGL介绍


    前面已经在android平台上使用OpenGL ES的API了解了如何创建3D图形已经使用FBO渲染到纹理进行一些其他的操作,起初我学习OpenGL ES的目的就是为了研究Android平台上录制屏幕的方案。到目前为止,基础知识已经具备了,还差一点需要了解的是Embedded Graphics Library (EGL),EGL是连接OpenGL ES和本地窗口系统的接口,由于OpenGL ES是跨平台的,引入EGL就是为了屏蔽不同平台上的区别。本地窗口相关的API提供了访问本地窗口系统的接口,EGL提供了创建渲染表面,接下来OpenGL ES就可以在这个渲染表面上绘制,同时提供了图形上下文,用来进行状态管理。更详细的信息可以参考Khronos EGL Registry
    OpenGL和EGL.pngOpenGL和EGL.png
    下面这几篇文章有助于对EGL的理解

    Using EGL to connect a native window and OpenGL ES
    EGL use for Android native OpenGL ES applications
    USING OPENGL ES ON WINDOWS DESKTOPS VIA EG

    使用EGL一般为下面的顺序
    1.使用EGL首先必须创建,建立本地窗口系统和OpenGL ES的连接。

    EGLDisplay eglDisplay(EGLNativeDisplayType displayId)

    EGL提供了平台无关类型EGLDisplay表示窗口。定义EGLNativeDisplayType是为了匹配原生窗口系统的显示类型,对于Windows,EGLNativeDisplayType被定义为HDC,对于Linux系统,被定义为Display*类型,对于Android系统,定义为ANativeWindow *类型,为了方便的将代码转移到不同的操作系统上,应该传入EGL_DEFAULT_DISPLAY,返回与默认原生窗口的连接。如果连接不可用,则返回EGL_NO_DISPLAY。

    2.初始化EGL

    创建与本地原生窗口的连接后需要初始化EGL,使用函数eglInitialize进行初始化操作。如果 EGL 不能初始化,它将返回EGL_FALSE,并将EGL错误码设置为EGL_BAD_DISPLAY表示制定了不合法的EGLDisplay,或者EGL_NOT_INITIALIZED表示EGL不能初始化。使用函数eglGetError用来获取最近一次调用EGL函数出错的错误代码

    EGLBoolean eglInitialize(EGLDisplay display, // 创建的EGL连接
                             EGLint *majorVersion, // 返回EGL主板版本号
                             EGLint *minorVersion); // 返回EGL次版本号

    初始化EGL示例

    EGLint majorVersion;
    EGLint minorVersion;
    EGLDisplay display;
    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(display == EGL_NO_DISPLAY)
    {
        // Unable to open connection to local windowing system
    }
    if(!eglInitialize(display, &majorVersion, &minorVersion))
    {
        // Unable to initialize EGL. Handle and recover
    }

    3.确定可用的渲染表面(Surface)的配置。一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了。

    一种方式是使用eglGetConfigs函数获取底层窗口系统支持的所有EGL表面配置,然后再使用eglGetConfigAttrib依次查询每个EGLConfig相关的信息,EGLConfig包含了渲染表面的所有信息,包括可用颜色、缓冲区等其他特性。

    EGLBoolean eglGetConfigs(EGLDisplay display, EGLConfig *configs, EGLint maxReturnConfigs,EGLint *numConfigs);
    
    EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value)

    另一种方式是指定我们需要的渲染表面配置,让EGL自己选择一个符合条件的EGLConfig配置。eglChooseChofig调用成功返回EGL_TRUE,失败时返回EGL_FALSE,如果attribList包含了未定义的EGL属性,或者属性值不合法,EGL代码被设置为EGL_BAD_ATTRIBUTR

    EGLBoolean eglChooseChofig(EGLDispay display, // 创建的和本地窗口系统的连接
                               const EGLint *attribList, // 指定渲染表面的参数列表,可以为null
                               EGLConfig *config,   // 调用成功,返会符合条件的EGLConfig列表
                               EGLint maxReturnConfigs, //最多返回的符合条件的EGLConfig个数
                               ELGint *numConfigs );  // 实际返回的符合条件的EGLConfig个数

    attribList参数在EGL函数中可以为null或者指向一组以EGL_NONE结尾的键对值

    // we need this config
    EGLint attribList[] ={
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    EGL_RED_SIZE, 5,
    EGL_GREEN_SIZE, 6,
    EGL_BLUE_SIZE, 5,
    EGL_DEPTH_SIZE, 1,
    EGL_NONE
    };
    const EGLint MaxConfigs = 10;
    EGLConfig configs[MaxConfigs]; // We'll only accept 10 configs
    EGLint numConfigs;
    if(!eglChooseConfig(dpy, attribList, configs, MaxConfigs,
    &numConfigs))
    {
    // Something didn't work … handle error situation
    }
    else
    {
    // Everything's okay. Continue to create a rendering surface
    }

    4.创建渲染表面

    有了符合条件的EGLConfig后,就可以通过eglCreateWindowSurface函数创建渲染表面。使用这个函数的前提是要使用原生窗口系统提供的API创建一个窗口。eglCreateWindowSurface中attribList一般可以使用null即可。函数调用失败会返回EGL_NO_SURFACE,并设置对应的错误码。

    EGLSurface eglCreateWindowSurface(EGLDisplay display,
                                      EGLConfig config, // 前面选好的可用EGLConfig
                                      EGLNatvieWindowType window, // 指定原生窗口
                                      const EGLint *attribList) // 指定窗口属性列表,可以为null,一般指定渲染所用的缓冲区使用但缓冲或者后台缓冲,默认为后者。

    创建EGL渲染表面示例

    EGLRenderSurface window;
    EGLint attribList[] =
    {
      EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
      EGL_NONE
    );
    window = eglCreateWindowSurface(dpy, config, window, attribList);
    if(window == EGL_NO_SURFACE)
    {
    switch(eglGetError())
    {
      case EGL_BAD_MATCH:
        // Check window and EGLConfig attributes to determine
        // compatibility, or verify that the EGLConfig
        // supports rendering to a window,
      break;
      case EGL_BAD_CONFIG:
          // Verify that provided EGLConfig is valid
      break;
      case EGL_BAD_NATIVE_WINDOW:
          // Verify that provided EGLNativeWindow is valid
      break;
      case EGL_BAD_ALLOC:
          // Not enough resources available. Handle and recover
      break;
    }
    }

    使用eglCreateWindowSurface函数创建在窗口上的渲染表面,此外还可以使用eglCreatePbufferSurface创建屏幕外渲染表面(Pixel Buffer 像素缓冲区)。使用Pbuffer一般用于生成纹理贴图,不过该功能已经被FrameBuffer替代了,使用帧缓冲对象的好处是所有的操作都由OpenGL ES来控制。使用Pbuffer的方法和前面创建窗口渲染表面一样,需要改动的地方是在选取EGLConfig时,增加EGL_SURFACE_TYPE参数使其值包含EGL_PBUFFER_BIT。而该参数默认值为EGL_WINDOW_BIT。

    EGLSurface eglCreatePbufferSurface( EGLDisplay display,
                                       EGLConfig config,
                                       EGLint const * attrib_list // 指定像素缓冲区属性列表
                                      );

    5.创建渲染上下文

    使用eglCreateContext为当前的渲染API创建EGL渲染上下文,返回一个上下文,当前的渲染API是由函数eglBindAPI设置的。OpenGL ES是一个状态机,用一系列变量描述OpenGL ES当前的状态如何运行,我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。比如我想告诉OpenGL ES接下来要绘制三角形,可以通过一些上下文变量来改变OpenGL ES的状态,一旦改变了OpenGL ES的状态为绘制三角形,下一个命令就会画出三角形。通过这些状态设置函数就会改变上下文,接下来的操作总会根据当前上下文的状态来执行,除非再次重新改变状态。

    // 设置当前的渲染API
    EGLBoolean eglBindAPI(
      EGLenum api //可选 EGL_OPENGL_API, EGL_OPENGL_ES_API, or EGL_OPENVG_API
    );
    
    EGLContext eglCreateContext(EGLDisplay display, 
                                EGLConfig config, // 前面选好的可用EGLConfig
                                EGLContext shareContext, // 允许多个EGLContext共享特定类型的数据,传递EGL_NO_CONTEXT表示不与其他上下文共享资源
                                const EGLint* attribList // 指定操作的属性列表,只能接受一个属性EGL_CONTEXT_CLIENT_VERSION用来表示使用的OpenGL ES版本
                               );

    创建上下文示例

    const ELGint attribList[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };
    EGLContext context;
    context = eglCreateContext(dpy, config, EGL_NO_CONTEXT, attribList);
    if(context == EGL_NO_CONTEXT)
    {
      EGLError error = eglGetError();
      if(error == EGL_BAD_CONFIG)
      {
        // Handle error and recover
      }
    }

    6.指定某个EGLContext为当前上下文。使用eglMakeCurrent函数进行当前上下文的绑定。一个程序可能创建多个EGLContext,所以需要关联特定的EGLContext和渲染表面,一般情况下两个EGLSurface参数设置成一样的。

    EGLBoolean eglMakeCurrent(EGLDisplay display,
                              EGLSurface draw, // EGL绘图表面
                              EGLSurface read, // EGL读取表面
                              EGLContext context // 指定连接到该表面的渲染上下文
                             );

    7.使用OpenGL相关的API进行绘制操作。
    8.交换EGL的Surface的内部缓冲和EGL创建的和平台无关的窗口diaplay。EGL实际上维护了两个buffer,前台buffer显示的时候,绘制操作会在后台buffer上进行。

    EGLBoolean eglSwapBuffers(EGLDisplay display, // 指定的EGL和本地窗口的连接
                              EGLSurface surface  // 指定要交换缓冲的EGL绘制表面
                             );

    如果surface是一个window surface,那么该函数执行的结果将是将数据给本地窗口,即显示在屏幕上。如果surface是一个屏幕外渲染surface(pixel buffer),执行该函数没有效果。

    可以看到想要使用OpenGL是比较麻烦的,而这些操作又是固定的,如果把大量的代码用在做这些重复的操作上,对于学习OpenGL来说是不必要的,因此就有了一些框架,封装好了创建EGL环境以及处理事件部分,只需要将重点放在OpenGL的学习上即可。比如C语言库GLFW,还有OpenGL ES3.0编程指南的作者封装的库https://github.com/danginsburg/opengles3-book/,esMain函数作为入口,其他的处消息循环,创建窗口等操作不需要我们去处理,只需要关注OpenGL代码的编写即可。在Android系统中提供的GLSurfaceView可以很方便的用来使用OpenGL ES进行编码,其实也是因为Android的GUI系统将创建EGL环境等部分封装好了。

  • 相关阅读:
    libevent
    STL中mem_fun和mem_fun_ref的用法
    java的awt和swing的区别于联系
    数据库流程控制的使用IF CASE LOOP LEAVE ITERETA REPEAT WHILE
    mysql的常用函数
    数据库的基本知识点
    使用myeclipse 打包并运行普通java项目
    getClass()与getName()方法
    Java中的final关键字
    基本类型包装类的常量池技术
  • 原文地址:https://www.cnblogs.com/qhyuan1992/p/6071961.html
Copyright © 2020-2023  润新知