美文网首页
OpenGL ES 3.0 入门

OpenGL ES 3.0 入门

作者: lugic | 来源:发表于2016-11-10 17:40 被阅读3771次

    一、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毁掉函数之后调用该函数)。

    相关文章

      网友评论

          本文标题:OpenGL ES 3.0 入门

          本文链接:https://www.haomeiwen.com/subject/xwexpttx.html