OpenGL ES 3.0 入门

一、OpenGL ES简介

OpenGL(Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。(OpenGL ES可以在iOS上实现2D和3D图形编程。)

** OpenGL ES 1.0、2.0、3.0的区别: **

OpenGL ES1.0:
针对固定管线硬件(fixed pipeline),通过它内建的functions来设置诸如灯光、vertexes(图形的顶点数)、颜色、camera等等的东西。

OpenGL ES2.0:
针对可编程管线硬件(programmable pipeline),需要自己动手编写任何功能。与此同时,2.0相比于1.0更具灵活性,功能也更强大。可以自定义顶点和像素计算,可以让表现方式更加准确。

OpenGL ES3.0:
OpenGL ES3.0扩展了OpenGL ES2.0,支持许多新的渲染技术、优化和显示质量改进,包括——引入了许多和纹理相关的新功能,对着色语言进行了重大更新和支持着色器新功能的API特性,引入了多种与几何形状规范和图元渲染控制相关的新功能,引入了新的缓冲区对象,增添了许多与屏幕外渲染到帧缓冲区对象相关的新功能。具体功能在后边的文章详细说明。(可能:))

** 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)和原生窗口系统之间的接口(在iOS上则是EAGL),任何OpenGL ES应用程序都必须在开始渲染之前使用EGL执行如下任务:

  • 查询并初始化设备商可用的显示器
  • 创建渲染表面
  • 创建渲染上下文

** 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函数为应用程序的主入口,参数是ESContext。ESContext有一个名为userData、类型为void
的成员变量,应用程序所需的所有数据保存在userData中。ESContext结构中的其他元素在头文件中描述,供用户的用用程序读取。ESContext结构中的其他数据包括窗口宽度和高度、EGL上下文和回调函数指针等信息。
在本程序的esMain中分配了userData、创建了窗口并初始化绘图回调函数。

** 2.创建顶点着色器和片断着色器 **

** int Init ( ESContext *esContext ) **
要进行任何渲染,OpenGL ES 3.0程序必须至少有一个顶点着色器和一个片断着色器。本程序中的Init函数最主要的任务是建在一个顶点着色器和一个片断着色器。

以顶点着色器为例说明:

   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)限定符表示这个变量的位置是顶点属性0;
第三行开始声明一个main函数,表示着色器执行的开始,{}中是着色器主体,将vPosition输入属性拷贝到名为gl_Position的特殊输出变量。每个顶点着色器必须在gl_Position变量中输出一个位置,这个变量定义传递到管线下一个阶段的位置。

** 3.编译和加载着色器 **

** GLuint LoadShader ( GLenum type, const char *shaderSrc ) **
定义完着色器源代码后,可以将着色器加载到OpenGL ES。LoadShader函数负责加载着色器源代码、编译并检查错误,它返回一个着色器对象,这是一个OpenGL ES 3.0对象,以后可用于连接到程序对象(后边会讲到)。
以本程序的LoadShader为例:

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

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

glCreateShader创建指定类型的新着色器对象。

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

// Compile the shader
glCompileShader ( shader );

着色器源代码本身用glShaderSource加载到着色器对象,着色器用glCompileShader函数编译。

// 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;

编译着色器之后,确定编译的状态,打印输出生成的错误。

如果着色器编译成功,则返回一个新的着色器对象。

** 4.创建一个程序对象并连接着色器 **

顶点和片段着色器需要连接到一个程序对象才能绘制图形,可以说程序对象是最终连接的程序。

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

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

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

用glCreateProgram创建程序对象,并用glAttachShader将顶点着色器和片断着色器连接到对象上。

// 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;

连接程序,检查错误。对象连接成功后,可以使用程序对象进行渲染。

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

调用glUseProgram来使用程序对象。

** 5.设置视口和清除颜色缓冲区 **

** void Draw ( ESContext *esContext ) **
Draw回调函数用于绘制帧。

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

设置视口的原点和宽高

// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );

绘图中涉及多种缓冲区类型:颜色、深度和模板。在本程序中只向颜色缓冲区中绘制图形。在每一帧开始的时候,我们用glClear清除缓冲区,清除颜色为glClearColor指定的颜色。在本程序中,清除颜色被设为(1.0, 1.0, 1.0, 1.0)。

** 6.加载几何形状和绘制图元 **

清除颜色缓冲区、设置视口和加载程序对象之后,需要制定三角形的几何形状。

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 );

三角形的顶点由vVertices数组中的三个坐标(x, y, z)指定。顶点位置使用glVertexAttribPointer加载到GL,并连接到顶点着色器生命的vPosition属性。
最后,使用glDrawArrays告诉OpenGL ES绘制图元。

** 7.显示后台缓冲区 **

屏幕上可见的帧缓冲区有一个像素数据的二维数组表示。但是如果我们直接绘制到缓冲区,那么用户在部分更新帧缓冲区的时候会看到伪像。为了解决这个问题,我们使用双缓冲区(前台缓冲区和后台缓冲区)。所有渲染都发生在后台缓冲区(不可见缓冲区),当渲染完成之后,这个缓冲区被交换到前台缓冲区(可见缓冲区)。然后前台缓冲区变成下一帧的后台缓冲区。这种活动通过eglSwapBuffers控制(调用Draw毁掉函数之后调用该函数)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容