OpenGL ES2.0 c++ 基础教程

OpenGL ES2.0 c++ 基础

什么是OpenGL ES?OpenGL ES (为OpenGL for Embedded System的缩写) 为适用于嵌入式系统的一个免费二维和三维图形库。为桌面版本OpenGL 的一个子集。OpenGL ES 定义了一个在移动平台上能够支持

hello world 开始吧

首先环境搭建引入必要库。Linux windows ios android 嵌入式设备 自己的目标设备支持什么库。

基本库

  • EGL
  • openGL ES2.0

以下例子程序均基于Linux平台。

1. 保存全局变量的数据结构

ESContext

typedef struct _escontext   
{   
   void*       userData;        // Put your user data here...   
   GLint       width;           // Window width   
   GLint       height;          // Window height   
   EGLNativeWindowType  hWnd;   // Window handle   
   EGLDisplay  eglDisplay;      // EGL display   
   EGLContext  eglContext;      // EGL context   
   EGLSurface  eglSurface;      // EGL surface   
   // Callbacks   
   void (ESCALLBACK *drawFunc) ( struct _escontext * );   
   void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int );   
   void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime );   
}ESContext;  

UserData

typedef struct   
{   
   // Handle to a program object   
   GLuint programObject;   
   // Atrribute Location   
   GLint positionLoc;   
   GLint textureLoc;   
   // Uniform location   
   GLint matrixModeLoc;   
   GLint matrixViewLoc;   
   GLint matrixPerspectiveLoc;   
   // Sampler location   
   GLint samplerLoc;   
   // texture   
   GLuint texture;   
} UserData;
2. 初始化EGL渲染环境和相关元素(第一步)
  1. 获取 EGL Display 对象:eglGetDisplay()
  2. 初始化与 EGLDisplay 之间的连接:eglInitialize()
  3. 获取 EGLConfig 全部列表对象 :eglGetConfigs()
  4. 获取 EGLConfig 选择EGL对象:eglChooseConfig()
  5. 创建 EGLContext 实例:eglCreateContext()
  6. 创建 EGLSurface 实例:eglCreateWindowSurface()
  7. 设置当前渲染API : eglBindAPI();
    1. EGL_OPENGL_API
    2. EGL_OPENGL_ES_API
    3. EGL_OPENVG_API
  8. 连接 EGLContext 和 EGLSurface:eglMakeCurrent()
int InitEGL(ESContext * esContext)   
{   
     NativeWindowType eglWindow = NULL;   
     EGLDisplay display;   
     EGLContext context;   
     EGLSurface surface;   
     EGLConfig configs[2];   
     EGLBoolean eRetStatus;   
     EGLint majorVer, minorVer;   
     EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};   
     EGLint numConfigs;   
     EGLint cfg_attribs[] = {EGL_BUFFER_SIZE,    EGL_DONT_CARE,   
                             EGL_DEPTH_SIZE,     16,   
                             EGL_RED_SIZE,       5,   
                             EGL_GREEN_SIZE,     6,   
                             EGL_BLUE_SIZE,      5,   
                             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,   
                             EGL_NONE};   
    
    //my function       
    eglWindow = my_getEglNativeWindowType();
     // Get default display connection    
     display = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY);   
     if ( display == EGL_NO_DISPLAY )   
     {   
          return EGL_FALSE;   
     }   
     // Initialize EGL display connection   
     eRetStatus = eglInitialize(display, &majorVer, &minorVer);   
     if( eRetStatus != EGL_TRUE )   
     {   
          return EGL_FALSE;   
     }   
     //Get a list of all EGL frame buffer configurations for a display   
     eRetStatus = eglGetConfigs (display, configs, 2, &numConfigs);   
     if( eRetStatus != EGL_TRUE )   
     {   
          return EGL_FALSE;   
     }   
     // Get a list of EGL frame buffer configurations that match specified attributes   
     eRetStatus = eglChooseConfig (display, cfg_attribs, configs, 2, &numConfigs);   
     if( eRetStatus != EGL_TRUE  || !numConfigs)   
     {   
          return EGL_FALSE;   
     }   
     // Create a new EGL window surface   
     surface = eglCreateWindowSurface(display, configs[0], eglWindow, NULL);   
     if (surface == EGL_NO_SURFACE)   
     {   
          return EGL_FALSE;   
     }   
     // Set the current rendering API (EGL_OPENGL_API, EGL_OPENGL_ES_API,EGL_OPENVG_API)   
     eRetStatus = eglBindAPI(EGL_OPENGL_ES_API);   
     if (eRetStatus != EGL_TRUE)   
     {   
          return EGL_FALSE;   
     }   
     // Create a new EGL rendering context   
     context = eglCreateContext (display, configs[0], EGL_NO_CONTEXT, context_attribs);   
     if (context == EGL_NO_CONTEXT)   
     {   
          return EGL_FALSE;   
     }   
     // Attach an EGL rendering context to EGL surfaces   
     eRetStatus = eglMakeCurrent (display, surface, surface, context);   
     if( eRetStatus != EGL_TRUE )   
     {   
          return EGL_FALSE;   
     }   
     //If interval is set to a value of 0, buffer swaps are not synchronized to a video frame, and the swap happens as soon as the render is complete.   
     eglSwapInterval(display,0);   
     // Return the context elements
     esContext->eglWindow = eglWindow;
     esContext->eglDisplay = display;   
     esContext->eglSurface = surface;   
     esContext->eglContext = context;   
     return EGL_TRUE;   
}   
获取 my_getEglNativeWindowType();
  • NativeDisplayType 平台显示数据类型,标识你所开发设备的物理屏幕
  • NativeWindowType 平台窗口数据类型,标识系统窗口

下面的代码是一个 NativeWindowType 获取的例子。这只是一个例子,不同平台之间的实现千差万别。使用 native 类型的关键作用在于为开发者抽象化这些细节。

NativeWindowType getEglNativeWindowType(){
    NativeDisplayType EGL_display = fbGetDisplayByIndex(0);
    NativeDisplayType window;
    unsigned lond physical;
    int width, height, stride, bitsPerPixel;
    fbgetDisplayInfo(egl_display, &width, &height, &physical, &stride, &bitsPerPixel);
    window = fbCreateWindow(egl_display, 0, 0, width, height);
    assert(window);
    return window;
}
3. 生成Program (第二步)
3.1 LoadShader

实现shader的编译
LoadShader主要实现以下功能:

  1. 创建Shader对象
  2. 装载Shader源码
  3. 编译Shader
/* type specifies the Shader type: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER */   
GLuint LoadShader ( GLenum type, const char *shaderSrc )   
{   
   GLuint shader;   
   GLint compiled;   
      
   // Create an empty shader object, which maintain the source code strings that define a shader   
   shader = glCreateShader ( type );   
   if ( shader == 0 )   
    return 0;   
   // Replaces the source code in a shader object   
   glShaderSource ( shader, 1, &shaderSrc, NULL );   
      
   // Compile the shader object   
   glCompileShader ( shader );   
   // Check the shader object 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;   
}   
3.1.1. 创建Shader对象 :glCreateShader()

它创建一个空的shader对象,它用于维护用来定义shader的源码字符串。支持以下两种

  1. GL_VERTEX_SHADER:它运行在可编程的“顶点处理器”上,用于代替固定功能的顶点处理
  2. GL_FRAGMENT_SHADER:它运行在可编程的“片断处理器”上,用于代替固定功能的片段处理
3.1.2. 装载Shader源码:glShaderSource()

shader对象中原来的源码全部被新的源码所代替。

3.1.3. 编译Shader:glCompileShader()

编译存储在shader对象中的源码字符串,编译结果被当作shader对象状态的一部分被保存起来,可通过glGetShaderiv函数获取编译状态。

3.1.4. 获取shader对象参数:glGetShaderiv()

获取shader对象参数,参数包括:GL_SHADER_TYPE, GL_DELETE_STATUS, GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, GL_SHADER_SOURCE_LENGTH.

3.2 LoadProgram

实现了Program的链接

GLuint LoadProgram ( const char *vShaderStr, const char *fShaderStr )   
{   
   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;   
   // Attaches a shader object to a program object   
   glAttachShader ( programObject, vertexShader );   
   glAttachShader ( programObject, fragmentShader );   
   // Bind vPosition to attribute 0      
   glBindAttribLocation ( programObject, 0, "vPosition" );   
   // Link the program object   
   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 GL_FALSE;   
   }   
    
   // Free no longer needed shader resources   
   glDeleteShader ( vertexShader );   
   glDeleteShader ( fragmentShader );   
   return programObject;   
}   
3.2.1 创建Program对象 :glCreateProgram()

建立一个空的program对象,shader对象可以被连接到program对像

3.2.2 glAttachShader

program对象提供了把需要做的事连接在一起的机制。在一个program中,在shader对象被连接在一起之前,必须先把shader连接到program上。

3.2.3 glBindAttribLocation

把program的顶点属性索引与顶点shader中的变量名进行绑定。

3.2.4 glLinkProgram

连接程序对象。如果任何类型为GL_VERTEX_SHADER的shader对象连接到program,它将产生在“可编程顶点处理器”上可执行的程 序;如果任何类型为GL_FRAGMENT_SHADER的shader对象连接到program,它将

产生在“可编程片断处理器”上可执行的程序。

3.2.5 glGetProgramiv

获取program对象的参数值,参数有:GL_DELETE_STATUS, GL_LINK_STATUS, GL_VALIDATE_STATUS, GL_INFO_LOG_LENGTH, GL_ATTACHED_SHADERS, GL_ACTIVE_ATTRIBUTES, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,

GL_ACTIVE_UNIFORMS, GL_ACTIVE_UNIFORM_MAX_LENGTH.

3.3 CreateProgram

实现了Program的源码加载
在3.1中只实现了Shader的编译,在3.2中只实现了Program的链接,现在还缺少真正供进行编译和链接源码

int CreateProgram(ESContext * esContext)   
{   
     GLuint programObject;   
     GLbyte vShaderStr[] =     
      "attribute vec4 vPosition;"   
      "void main()"   
      "{"   
      " gl_Position = vPosition;"   
      "}";   
      
     GLbyte fShaderStr[] =     
      "precision mediump float;"
      "void main()"   
      "{"   
      " gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );"   
      "}";   
       
    // Create user data    
    esContext->userData = malloc(sizeof(UserData));   
    UserData *userData = esContext->userData;   
    // Load the shaders and get a linked program object   
    programObject = LoadProgram ( (const char*)vShaderStr, (const char*)fShaderStr );   
    if(programObject == 0)   
    {   
    return GL_FALSE;   
    }   
    // Store the program object   
    userData->programObject = programObject;   
    // Get the attribute locations   
    userData->positionLoc = glGetAttribLocation ( g_programObject, "v_position" );   
    glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f );   
    return ELG_TRUE;   
}   

4. 安装并执行Program(第三步)

void Render ( 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 );   
   eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface); 
4.1 glClear

清除指定的buffer到预设值。可清除以下四类buffer:

  • GL_COLOR_BUFFER_BIT
  • GL_DEPTH_BUFFER_BIT
  • GL_ACCUM_BUFFER_BIT
  • GL_STENCIL_BUFFER_BIT

预设值通过glClearColor, glClearIndex, glClearDepth, glClearStencil, 和glClearAccum来设置。

1)glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)

指定color buffer的清除值,当调用glClear(GL_COLOR_BUFFER_BIT)时才真正用设定的颜色值清除color buffer。参数值的范围为:0~1。

2)glClearIndex(GLfloat c)

指定color index buffer清除值。

3)glClearDepth(GLclampd depth)

指定depth buffer的清除值,取值范围为:0~1,默认值为1。

4)glClearStencil(GLint s)

指定stencil buffer清除值的索引,初始值为0。

5)glClearAccum( GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)

指定accumulation buffer的清除值,初始值为0,取值范围为:-1~1

4.2 glUseProgram

安装一个program object,并把它作为当前rendering state的一部分。

1) 当一个可执行程序被安装到vertex processor,下列OpenGL固定功能将被disable:
  • The modelview matrix is not applied to vertex coordinates.
  • The projection matrix is not applied to vertex coordinates.
  • The texture matrices are not applied to texture coordinates.
  • Normals are not transformed to eye coordinates.
  • Normals are not rescaled or normalized.
  • Normalization of GL_AUTO_NORMAL evaluated normals is not performed.
  • Texture coordinates are not generated automatically.
  • Per-vertex lighting is not performed.
  • Color material computations are not performed.
  • Color index lighting is not performed.
  • This list also applies when setting the current raster position.
2)当一个可执行程序被安装到fragment processor,下列OpenGL固定功能将被disable:
  • Texture environment and texture functions are not applied.
  • Texture application is not applied.
  • Color sum is not applied.
  • Fog is not applied.
4.3 glVertexAttribPointer

定义一个通用顶点属性数组。当渲染时,它指定了通用顶点属性数组从索引index处开始的位置和数据格式。其定义如下:

void glVertexAttribPointer(   
    GLuint   index,           // 指示将被修改的通用顶点属性的索引   
    GLint   size,             // 指点每个顶点元素个数(1~4)   
    GLenum   type,            // 数组中每个元素的数据类型   
    GLboolean   normalized,   //指示定点数据值是否被归一化(归一化<[-1,1]或[0,1]>:GL_TRUE,直接使用:GL_FALSE)   
    GLsizei   stride,         // 连续顶点属性间的偏移量,如果为0,相邻顶点属性间紧紧相邻   
    const GLvoid *   pointer);//顶点数组:其index应该小于#define GL_MAX_VERTEX_ATTRIBS 0x8869
    )
4.4glEnableVertexAttribArray

Enable由索引index指定的通用顶点属性数组。

void glEnableVertexAttribArray(GLuint index); 
void glDisableVertexAttribArray(GLuint index); 

默认状态下,所有客户端的能力被disabled,包括所有通用顶点属性数组。如果被Enable,通用顶点属性数组中的值将被访问并被用于rendering,通过调用顶点数组命令:glDrawArrays, glDrawElements,

glDrawRangeElements, glArrayElement, glMultiDrawElements, or glMultiDrawArrays.

4.5 glDrawArrays
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
  1. mode:指明render原语,如:GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS, 和 GL_POLYGON。
  2. first: 指明Enable数组中起始索引。
  3. count: 指明被render的原语个数。

可以预先使用单独的数据定义vertex、normal和color,然后通过一个简单的glDrawArrays构造一系列原语。当调用 glDrawArrays时,它使用每个enable的数组中的count个连续的元素,来构造一系列几何原

语,从第first个元素开始。

4.6 eglSwapBuffers

把EGL surface中的color buffer提交到native window进行显示。

EGLBoolean eglSwapBuffers(EGLDisplay display,EGLSurface surface)

5. 协调组织

在前面的描述中,三步曲已经完成了:

  1. 初始化EGL环境,为绘图做好准备
  2. 生成Program
  3. 安装并执行Program

只有这三个关键人物,还不能运行,还需要一个协调组织者。

int main(int argc, char** argv)   
{   
    ESContext esContext;   
    UserData  userData;   
    int iFrames;    
    unsigned long iStartTime,iEndTime;   
    int iDeltaTime;   
    memset( &esContext, 0, sizeof( ESContext) );   
    esContext.userData = &userData;   
    esContext.width = 1280;   
    esContext.height = 720;   
    // Init EGL display, surface and context   
    if(!InitEGL(&esContext))   
    {   
        printf("Init EGL fail\n");   
        return GL_FALSE;   
    }   
    // compile shader, link program    
    if(!CreateProgram(&esContext))   
    {   
        printf("Create Program fail\n");   
        return GL_FALSE;   
    }   
    iStartTime = GetCurTime();   
    iFrames = 0;   
    while(1) {    // render a frame   
         Render();   
       iFrames++;   
           
       iEndTime = GetCurTime();   
      iDeltaTime  = iEndTime - iStartTime;   
    if(iDeltaTime >= 5000)   
    {   
        iStartTime = iEndTime;   
        float fFrame = iFrames * 1000.0 / iDeltaTime;   
        iFrames = 0;   
        printf("Frame.: %f\n", fFrame);   
    }   
    }   
    glDeleteProgram (esContext.userData->programObject);   
    return GL_TRUE;   
}  

[TOC]

总结

  1. 初始化EGL渲染环境和相关元素 (第一步)
  2. 生成Program (第二步)
    1. shader的编译
    2. Program的链接
    3. CreateProgram 程序创建 vertex(顶点)fragment(片元)shader(着色器)源码加载
  3. 安装并执行Program (第三步)
    1. 使用 OpenGL ES API 绘制图形:gl_*()
  4. 协调组织
  5. EGL释放资源
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342