Modern OpenGL ES: ndk編程——畫一個三角形之創建EGL窗口

上一節我們 知道了如何用ndk 來創建一個 Activity
這一節,我們來創建 GL 的窗口
上一節已經知道, Khronos Group 爲我們提供了 EGL API 來幫助我們創建 窗口,同步窗口繪製,管理窗口渲染。

1 與窗口系統通信

EGL在OpenGL ES 和 本地窗口系統之間 提供了一個 "glue"層。 在EGL 決定那種類型的繪製surface可以用之前,它先開啓了與窗口系統通信的通道。

   1.1 EGLDisplay
   因爲,每個窗口系統都有其不同的實現機制,因此EGL提供了一種封裝了與本地窗口系統交互的系統庫的 類型: EGLDisplay。 
   使用EGL的應用程序所做的第一步就是創建和初始化與本地EGL display的連接。

       EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId)
       參數
          displayId: 定義了 display的連接, 默認爲 EGL_DEFAULT_DISPLAY
          它用來匹配本地窗口系統display的類

      返回值: 如果失敗, 它會返回EGL_NO_DISPLAY

    1.2 初始化EGL
    一旦獲取了正確的鏈接, EGL 需要初始化
         EGLBoolean eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion)

         參數
            display: 
            majorVersion:  定義了實現EGL的最大版本
            minorVersion:  定義了實現EGL的最小版本

         功能: 該函數初始化了EGL內部的數據結構,並返回其最大最小版本號。 

         返回值: 如果EGL初始化失敗,會返回EGL_FALSE,並返回EGL_BAD_DISPLAY,提示一個無效的EGLDisplay, 和返回    EGL_NOT_INITIALIZED 提示EGL沒有初始化

2  確定可用的Surfae配置
   一旦初始化了EGL, 我們就可以決定渲染Surface的類型和配置。 有兩種方式:
    確定每個Surface的配置,並找出最佳選擇
    自己定義一組配置,然後讓EGL推薦最佳配置

   在多數情況下,第二種比較簡單實現, 而且基本與第一種效果一樣。 不管哪種方法,EGL都會返回一個 EGLConfig。 EGLConfig是 EGL內部數據結構的標識, 該數據結構包含了特定Surface的信息和特性: 顏色分量的量化程度(bit數), 深度buffer。 

   2.1 讓EGL 選擇配置
    EGLBoolean eglChooseConfig(EGLDisplay display, const EGLint *attribList, EGLConfig *configs, EGLint maxReturnConfigs, EGLint *numConfigs)

      參數: 
       display:
       attribList: 定義了與 configs匹配的一組屬性
       configs: 返回的一組符合要求的配置
       maxReturnConfigs: 返回最大的配置數目
       numConfigs: 返回返回配置的數目
        
       返回值
         EGL_TRUE
         EGL_FALSE: 
         EGL_BAD_ATTRIBUTE: 如果attribList裏有不符合要求的。

        功能:一旦eglChooseConfig返回成功, 會返回一組符合要求的配置


    2.2 確定配置
        EGLConfig 包含了關於 EGL獲得的Surface的所有信息。
        可獲取的顏色表示範圍, depth buffers, stencil buffers, multisample buffers等。
   
        在選擇可用的Surface配置後,需要確定配置
         EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value)

         參數: 
             display: 定義EGL display連接
             config: 定義了確認的配置
             attribute: 返回的特定屬性
              value: 返回的特定的值
          返回值
                EGL_TRUE
                EGL_FALSE
                EGL_BAD_ATTRIBUTE: 如果attribute不是有效屬性 



  3 創建渲染區域: EGL Window
   一旦我們確定了與渲染相合適的 EGLConfig 後,我們就可以創建窗口了。
    EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType window, const EGLint *attribList)

    參數
      display:  
      config:
      window: 指定一個 本地窗口
      attribuList, 指定一組窗口屬性, 也許是NULL. 是Surface的屬性,以EGL_RENDER_BUFFER開頭, 以 EGL_NONE 結尾
       中間設置的參數有: EGL_SIGLE_BUFFER, EGL_BACK_BUFFER, 默認是 EGL_BACK_BUFFER

    說明
      它的第三個參數 就是上一節中 我們創建的Android窗口
      EGL_SIGLE_BUFFER 和 EGL_BACK_BUFFER代表了兩種渲染的方式。 前者是直接渲染到顯示的buffer裏, 後者是 先渲染到BACK_BUFFER,然後前後buffer交換。 

    返回值
      EGL_BAD_MATCH: EGLConfig 與 native window不匹配
      EGL_BAD_CONFIG:EGLConfig不被系統支持
      EGL_BAD_NATIVE_WINDOW: 當創建native window 無效
      EGL_BAD_ALLOCAT: eglCreateWindowSurface 不能爲EGL window分配資源

4 創建渲染的 Context
     一個渲染的Context是 OpenGL ES3.0的一個數據結構,包含了所有要操作的 狀態信息。 
     如, 它包含了 vertex/fragment shader以及頂點數據的所有引用。 
      EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint *attribList)

     參數
        display:
        config, 
        sharedContext: 運行多個 EGL Context共享某種類型的數據,如shader program和 texture map。 如果沒有共享的:EGL_NO_CONTEXT
       attribList:  指定EGL Context的版本

5 使EGLContext 稱爲當前Context
   這很重要。 我們可能會創建多個 Context, 因此需要當前渲染的Surface綁定一個 Context。
   
    EGLBoolean eglMakeCurrent(EGLDisplay display , EGLSurface draw, EGLSurface read, EGLContext context)
    參數
      display:
      draw: 指定現實的Surface  
      read: 指定渲染到的Surface
      context: 指定了Surface 依附的Context
   

______________________________________________________________________________
實現EGL 創建窗口
   按照上一帖,

   void  android_main(strut android_app *app)不僅是 我們程序的入口,而且app 還爲我們提供了 本地的window。

   struct android_app中 有一個 用於 處理app命令的回掉函數:
   
   static void onAppCmd(struct android_app *app, int32_t cmd)
   
       其中,cmd有幾種類型: APP_CMD_SAVE_STATE, APP_CMD_INIT_WINDOW, APP_CMD_TERM_WINDOW, APP_CMD_GAINED_FOCUS, APP_CMD_LOST_FOCUS。  
       這幾種類型 代表了 窗口的生命週期。  當窗口啓動時 會執行 APP_CAM_INIT_WINDOW, APP_CMD_GAINED_FOCUS; 當窗口銷燬時會有  APP_CMD_LOST_FOCUS, APP_CMD_TERM_WINDOW


在本地窗口處於 APP_CMD_INIT_WINDOW的時候,我們可以創建EGL窗口
     由於,android_app 有一個userData,可以用來接受 用戶自己定義的對象,如 

     
void android_main(struct android_app *app)
{
  ....
  winContext   wContext;  // 自己定義的窗口上下文結構體,裏面包含了創建窗口的所有信息
 app->userData = &wContext;

  ....
}
這樣 ,我們在 生命週期方法裏,就能夠對 我們定義的窗口上下文進行初始化,並創建窗口,如
void onAppCmd(struct android_app *app, int_32 md)
{
     winContext *wContext = (winContext) app->userData;

switch(cmd)
{
   ...
   case  APP_CMD_INIT_WINDOW:
     wContext->eglNativeDisplay = EGL_DEFAULT_DISPLAY;
    wContext->eglNativeWindow = app->window;
 ....
  //創建窗口
}
}



     在上面1-5 的分析中,可以看出,創建一個 EGL窗口,
         首先需要 EGLDisplay去獲取本地系統的一個連接
         其次,要選擇Surface的配置,並確定該配置
         然後,根據 android_app 提供的本地window,以及EGLDisplay 去創建一個Surface,並制定Surface的渲染模式。
         最後, 指定Surface的 Context。

     
     在上面1-5 的分析中,可以看出,創建一個 EGL窗口,
         首先需要 EGLDisplay去獲取本地系統的一個連接
         其次,要選擇Surface的配置,並確定該配置
         然後,根據 android_app 提供的本地window,以及EGLDisplay 去創建一個Surface,並制定Surface的渲染模式。
         最後, 指定Surface的 Context。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章