OpenGL ES 3.0 入門

一、OpenGL ES簡介

OpenGL(Open Graphics Library)是指定義了一個跨編程語言、跨平臺的編程接口規(guī)格的專業(yè)的圖形程序接口。它用于三維圖像(二維的亦可),是一個功能強(qiáng)大,調(diào)用方便的底層圖形庫。
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三維圖形 API 的子集,針對手機(jī)、PDA和游戲主機(jī)等嵌入式設(shè)備而設(shè)計(jì)。(OpenGL ES可以在iOS上實(shí)現(xiàn)2D和3D圖形編程。)

** OpenGL ES 1.0、2.0、3.0的區(qū)別: **

OpenGL ES1.0:
針對固定管線硬件(fixed pipeline),通過它內(nèi)建的functions來設(shè)置諸如燈光、vertexes(圖形的頂點(diǎn)數(shù))、顏色、camera等等的東西。

OpenGL ES2.0:
針對可編程管線硬件(programmable pipeline),需要自己動手編寫任何功能。與此同時,2.0相比于1.0更具靈活性,功能也更強(qiáng)大??梢宰远x頂點(diǎn)和像素計(jì)算,可以讓表現(xiàn)方式更加準(zhǔn)確。

OpenGL ES3.0:
OpenGL ES3.0擴(kuò)展了OpenGL ES2.0,支持許多新的渲染技術(shù)、優(yōu)化和顯示質(zhì)量改進(jìn),包括——引入了許多和紋理相關(guān)的新功能,對著色語言進(jìn)行了重大更新和支持著色器新功能的API特性,引入了多種與幾何形狀規(guī)范和圖元渲染控制相關(guān)的新功能,引入了新的緩沖區(qū)對象,增添了許多與屏幕外渲染到幀緩沖區(qū)對象相關(guān)的新功能。具體功能在后邊的文章詳細(xì)說明。(可能:))

** OpenGL ES 3.0的向后兼容新 **

OpenGL ES 3.0向后兼容OpenGL ES 2.0,但由于3.0/2.0不支持1.x支持的固定功能管線,3.0/2.0不能向后兼容1.x。

** EGL/EAGL **

EGL是Khronos渲染API(如OpenGL ES)和原生窗口系統(tǒng)之間的接口(在iOS上則是EAGL),任何OpenGL ES應(yīng)用程序都必須在開始渲染之前使用EGL執(zhí)行如下任務(wù):

  • 查詢并初始化設(shè)備商可用的顯示器
  • 創(chuàng)建渲染表面
  • 創(chuàng)建渲染上下文

** OpenGL ES 3.0圖形管線的各個階段 **

OpenGL ES 3.0圖形管線的各個階段

二、Hello,Triangle:第一個OpenGL ES 3.0程序

** 代碼地址如下 Hello_Triangle **

在此解釋一下** Hello_Triangle.c **的代碼

#include "esUtil.h"

typedef struct
{
   // Handle to a program object
   GLuint programObject;

} UserData;

///
// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;

   // Create the shader object
   shader = glCreateShader ( type );

   if ( shader == 0 )
   {
      return 0;
   }

   // Load the shader source
   glShaderSource ( shader, 1, &shaderSrc, NULL );

   // Compile the shader
   glCompileShader ( shader );

   // Check the compile status
   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

   if ( !compiled )
   {
      GLint infoLen = 0;

      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

      if ( infoLen > 1 )
      {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
         esLogMessage ( "Error compiling shader:\n%s\n", infoLog );

         free ( infoLog );
      }

      glDeleteShader ( shader );
      return 0;
   }

   return shader;

}

///
// Initialize the shader and program object
//
int Init ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   char vShaderStr[] =
      "#version 300 es                          \n"
      "layout(location = 0) in vec4 vPosition;  \n"
      "void main()                              \n"
      "{                                        \n"
      "   gl_Position = vPosition;              \n"
      "}                                        \n";

   char fShaderStr[] =
      "#version 300 es                              \n"
      "precision mediump float;                     \n"
      "out vec4 fragColor;                          \n"
      "void main()                                  \n"
      "{                                            \n"
      "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
      "}                                            \n";

   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;

   // Load the vertex/fragment shaders
   vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
   fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );

   // Create the program object
   programObject = glCreateProgram ( );

   if ( programObject == 0 )
   {
      return 0;
   }

   glAttachShader ( programObject, vertexShader );
   glAttachShader ( programObject, fragmentShader );

   // Link the program
   glLinkProgram ( programObject );

   // Check the link status
   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

   if ( !linked )
   {
      GLint infoLen = 0;

      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

      if ( infoLen > 1 )
      {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
         esLogMessage ( "Error linking program:\n%s\n", infoLog );

         free ( infoLog );
      }

      glDeleteProgram ( programObject );
      return FALSE;
   }

   // Store the program object
   userData->programObject = programObject;

   glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
   return TRUE;
}

///
// Draw a triangle using the shader pair created in Init()
//
void Draw ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   GLfloat vVertices[] = {  0.0f,  0.5f, 0.3f,
                            -0.5f, -0.5f, 0.5f,
                            0.5f, -0.5f, -0.4f
                         };

   // Set the viewport
   glViewport ( 0, 0, esContext->width, esContext->height );

   // Clear the color buffer
   glClear ( GL_COLOR_BUFFER_BIT );

   // Use the program object
   glUseProgram ( userData->programObject );

   // Load the vertex data
   glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
   glEnableVertexAttribArray ( 0 );

   glDrawArrays ( GL_TRIANGLES, 0, 3 );
}

void Shutdown ( ESContext *esContext )
{
   UserData *userData = esContext->userData;

   glDeleteProgram ( userData->programObject );
}

int esMain ( ESContext *esContext )
{
   esContext->userData = malloc ( sizeof ( UserData ) );

   esCreateWindow ( esContext, "Hello Triangle", 375, 667, ES_WINDOW_RGB );

   if ( !Init ( esContext ) )
   {
      return GL_FALSE;
   }

   esRegisterShutdownFunc ( esContext, Shutdown );
   esRegisterDrawFunc ( esContext, Draw );

   return GL_TRUE;
}

** 1.使用OpenGL ES 3.0 框架 **

** int esMain ( ESContext esContext ) **
esMain函數(shù)為應(yīng)用程序的主入口,參數(shù)是ESContext。ESContext有一個名為userData、類型為void
的成員變量,應(yīng)用程序所需的所有數(shù)據(jù)保存在userData中。ESContext結(jié)構(gòu)中的其他元素在頭文件中描述,供用戶的用用程序讀取。ESContext結(jié)構(gòu)中的其他數(shù)據(jù)包括窗口寬度和高度、EGL上下文和回調(diào)函數(shù)指針等信息。
在本程序的esMain中分配了userData、創(chuàng)建了窗口并初始化繪圖回調(diào)函數(shù)。

** 2.創(chuàng)建頂點(diǎn)著色器和片斷著色器 **

** int Init ( ESContext *esContext ) **
要進(jìn)行任何渲染,OpenGL ES 3.0程序必須至少有一個頂點(diǎn)著色器和一個片斷著色器。本程序中的Init函數(shù)最主要的任務(wù)是建在一個頂點(diǎn)著色器和一個片斷著色器。

以頂點(diǎn)著色器為例說明:

   char vShaderStr[] =
      "#version 300 es                          \n"
      "layout(location = 0) in vec4 vPosition;  \n"
      "void main()                              \n"
      "{                                        \n"
      "   gl_Position = vPosition;              \n"
      "}

第一行聲明著色器版本;
第二行聲明一個名為vPosition的4分量向量輸入屬性,layout(location=0)限定符表示這個變量的位置是頂點(diǎn)屬性0;
第三行開始聲明一個main函數(shù),表示著色器執(zhí)行的開始,{}中是著色器主體,將vPosition輸入屬性拷貝到名為gl_Position的特殊輸出變量。每個頂點(diǎn)著色器必須在gl_Position變量中輸出一個位置,這個變量定義傳遞到管線下一個階段的位置。

** 3.編譯和加載著色器 **

** GLuint LoadShader ( GLenum type, const char *shaderSrc ) **
定義完著色器源代碼后,可以將著色器加載到OpenGL ES。LoadShader函數(shù)負(fù)責(zé)加載著色器源代碼、編譯并檢查錯誤,它返回一個著色器對象,這是一個OpenGL ES 3.0對象,以后可用于連接到程序?qū)ο螅ê筮厱v到)。
以本程序的LoadShader為例:

GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );

if ( shader == 0 )
{
  return 0;
}

glCreateShader創(chuàng)建指定類型的新著色器對象。

// Load the shader source
glShaderSource ( shader, 1, &shaderSrc, NULL );

// Compile the shader
glCompileShader ( shader );

著色器源代碼本身用glShaderSource加載到著色器對象,著色器用glCompileShader函數(shù)編譯。

// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

if ( !compiled )
{
  GLint infoLen = 0;

  glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

  if ( infoLen > 1 )
  {
     char *infoLog = malloc ( sizeof ( char ) * infoLen );

     glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
     esLogMessage ( "Error compiling shader:\n%s\n", infoLog );

     free ( infoLog );
  }

  glDeleteShader ( shader );
  return 0;
}

return shader;

編譯著色器之后,確定編譯的狀態(tài),打印輸出生成的錯誤。

如果著色器編譯成功,則返回一個新的著色器對象。

** 4.創(chuàng)建一個程序?qū)ο蟛⑦B接著色器 **

頂點(diǎn)和片段著色器需要連接到一個程序?qū)ο蟛拍芾L制圖形,可以說程序?qū)ο笫亲罱K連接的程序。

// Create the program object
programObject = glCreateProgram ( );

if ( programObject == 0 )
{
return 0;
}

glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );

用glCreateProgram創(chuàng)建程序?qū)ο?,并用glAttachShader將頂點(diǎn)著色器和片斷著色器連接到對象上。

// Link the program
glLinkProgram ( programObject );

// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

if ( !linked )
{
  GLint infoLen = 0;

  glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

  if ( infoLen > 1 )
  {
     char *infoLog = malloc ( sizeof ( char ) * infoLen );

     glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
     esLogMessage ( "Error linking program:\n%s\n", infoLog );

     free ( infoLog );
  }

  glDeleteProgram ( programObject );
  return FALSE;
}

// Store the program object
userData->programObject = programObject;

連接程序,檢查錯誤。對象連接成功后,可以使用程序?qū)ο筮M(jìn)行渲染。

// Use the program object
glUseProgram ( userData->programObject );

調(diào)用glUseProgram來使用程序?qū)ο蟆?/p>

** 5.設(shè)置視口和清除顏色緩沖區(qū) **

** void Draw ( ESContext *esContext ) **
Draw回調(diào)函數(shù)用于繪制幀。

// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );

設(shè)置視口的原點(diǎn)和寬高

// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );

繪圖中涉及多種緩沖區(qū)類型:顏色、深度和模板。在本程序中只向顏色緩沖區(qū)中繪制圖形。在每一幀開始的時候,我們用glClear清除緩沖區(qū),清除顏色為glClearColor指定的顏色。在本程序中,清除顏色被設(shè)為(1.0, 1.0, 1.0, 1.0)。

** 6.加載幾何形狀和繪制圖元 **

清除顏色緩沖區(qū)、設(shè)置視口和加載程序?qū)ο笾?,需要制定三角形的幾何形狀?/p>

GLfloat vVertices[] = {  0.0f,  0.5f, 0.3f,
                            -0.5f, -0.5f, 0.5f,
                            0.5f, -0.5f, -0.4f
                         };
...
// Load the vertex data
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );

glDrawArrays ( GL_TRIANGLES, 0, 3 );

三角形的頂點(diǎn)由vVertices數(shù)組中的三個坐標(biāo)(x, y, z)指定。頂點(diǎn)位置使用glVertexAttribPointer加載到GL,并連接到頂點(diǎn)著色器生命的vPosition屬性。
最后,使用glDrawArrays告訴OpenGL ES繪制圖元。

** 7.顯示后臺緩沖區(qū) **

屏幕上可見的幀緩沖區(qū)有一個像素?cái)?shù)據(jù)的二維數(shù)組表示。但是如果我們直接繪制到緩沖區(qū),那么用戶在部分更新幀緩沖區(qū)的時候會看到偽像。為了解決這個問題,我們使用雙緩沖區(qū)(前臺緩沖區(qū)和后臺緩沖區(qū))。所有渲染都發(fā)生在后臺緩沖區(qū)(不可見緩沖區(qū)),當(dāng)渲染完成之后,這個緩沖區(qū)被交換到前臺緩沖區(qū)(可見緩沖區(qū))。然后前臺緩沖區(qū)變成下一幀的后臺緩沖區(qū)。這種活動通過eglSwapBuffers控制(調(diào)用Draw毀掉函數(shù)之后調(diào)用該函數(shù))。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容