[2 OpenGL ES 3.0示例]

本章介绍如下概念:
1 用EGL创建屏幕上的渲染表面
2 加载顶点和片段着色器
3 创建一个程序对象,连接顶点和片段着色器,并链接程序对象
4 设置视口
5 清除颜色缓冲区
6 渲染简单图元
7 使颜色缓冲区的内容在EGL窗口表面中可见

2.1 代码框架

代码中使用es开始的函数,例如esCreateWindow()。

2.3 示例

OpenGL ES 3.0完全基于着色器,如果没有加载和绑定合适的着色器,就无法绘制任何几何形状。
Hello_Triangle.c

// The MIT License (MIT)
//
// Copyright (c) 2013 Dan Ginsburg, Budirijanto Purnomo
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//
// Book:      OpenGL(R) ES 3.0 Programming Guide, 2nd Edition
// Authors:   Dan Ginsburg, Budirijanto Purnomo, Dave Shreiner, Aaftab Munshi
// ISBN-10:   0-321-93388-5
// ISBN-13:   978-0-321-93388-1
// Publisher: Addison-Wesley Professional
// URLs:      http://www.opengles-book.com
//            http://my.safaribooksonline.com/book/animation-and-3d/9780133440133
//
// Hello_Triangle.c
//
//    This is a simple example that draws a single triangle with
//    a minimal vertex/fragment shader.  The purpose of this
//    example is to demonstrate the basic concepts of
//    OpenGL ES 3.0 rendering.
#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.0f,
                            -0.5f, -0.5f, 0.0f,
                            0.5f, -0.5f, 0.0f
                         };

   // 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", 320, 240, ES_WINDOW_RGB );

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

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

   return GL_TRUE;
}

todo UML流程图

示例代码组织目录:
Common/----包含OpenGL ES 3.0框架代码
chapter_x/----包含每一章的示例程序

2.4 使用OpenGL ES 3.0框架

主入口点esMain:

int esMain ( ESContext *esContext )
{
   esContext->userData = malloc ( sizeof ( UserData ) );
   esCreateWindow ( esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB );
   if ( !Init ( esContext ) )
   {
      return GL_FALSE;
   }
   esRegisterShutdownFunc ( esContext, Shutdown );
   esRegisterDrawFunc ( esContext, Draw );

   return GL_TRUE;
}

 ESContext有一个名为userData类型为void*的成员,应用程序的所有数据保存在userData中。esMain函数负责分配userData、创建窗口和初始化回调函数。esCreateWindow函数使用EGL创建一个屏幕上的渲染表面,该表面连接到一个窗口。EGL是平台无关的创建渲染表面和上下文的API。退出esMain后,框架进入主循环,该循环将调用注册的回调函数Draw,直到窗口关闭。

2.5 创建简单的顶点和片段着色器

Init函数的主要任务是加载一个顶点着色器和一个片段着色器。顶点着色器如下:

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

layout(location = 0)表示这个变量的位置是顶点属性0。in vec4 vPosition表示输入属性数组,Draw函数将传入这个变量中的每个顶点的位置。main函数表示着色器执行的开始。每个顶点着色器必须在gl_Position变量中输出一个位置。这个变量传递到管线下一个阶段。
片段着色器如下:

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

precision mediump float声明着色器中浮点变量的默认精度。写入到fragColor变量的值将被输出到颜色缓冲区。这个例子中,片段着色器的输出都是红色(1.0, 0.0, 0.0, 1.0)。大部分应用,着色器是从某数据文件中加载的。

2.6 编译和加载着色器

LoadShader函数负责加载着色器源代码,编译并检查其错误。它返回一个着色器对象(GLuint),这是一个OpenGL ES 3.0对象,以后可用于连接到程序对象。

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

首先glCreateShader创建指定类型的新着色器对象。glShaderSource将着色器源代码加载到着色器对象。着色器用glCompileShader函数编译。若着色器编译成功,则返回一个新的着色器对象,这个对象以后将连接到程序。

2.7 创建一个程序对象并链接着色器

程序对象可以视为最终链接的程序。不同的着色器(顶点,片段)编译为一个着色器对象之后,它们必须连接到一个程序对象并一起链接,才能绘制图形。
Init函数:

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

创建程序对象,并将顶点和片段着色器连接到对象上。
Draw函数:

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

程序对象成功链接后,用glUseProgram来绑定程序对象,进行渲染。

2.8 设置视口和清除颜色缓冲区

Draw函数:

void Draw ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f,
                            -0.5f, -0.5f, 0.0f,
                            0.5f, -0.5f, 0.0f
                         };

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

Draw回调函数用于绘制帧。在Draw中执行的第一条命令是glViewport,它通知OpenGL ES用于绘制的2D渲染表面的原点、宽度和高度。设置视口之后,是清除屏幕。

2.9 加载几何图形和绘制图元

Draw函数中,清除颜色缓冲区、设置视口和加载程序对象之后,必须指定三角形的几何形状。三角形的顶点由vVertices数组中的3个坐标(x,y,z)指定。前面我们已经将vPosition与输入属性位置0绑定。我们调用glVertexAttribPointer函数将数据加载到顶点属性0。glDrawArrays函数用来绘制图元。

2.10 显示后台缓冲区

如果我们直接绘制到帧缓冲区,那么用户在部分更新帧缓冲区时会看到伪像。
采用双缓冲区:前台缓冲区和后台缓冲区。所有渲染发生在后台缓冲区,当所有渲染完成时,这个缓冲区被交换到前台缓冲区,用作屏幕显示。
eglSwapBuffers函数(框架在调用Draw回调函数后调用该函数)通知EGL切换前台和后台缓冲区。

eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface)

两个参数分别代表物理显示器和渲染表面。

猜你喜欢

转载自blog.csdn.net/u012906122/article/details/120340317