美文网首页OpenGL
OpenGL ES2.0 c++ 基础教程

OpenGL ES2.0 c++ 基础教程

作者: 可乐爱加菲猫 | 来源:发表于2019-05-31 20:40 被阅读0次

    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释放资源

    相关文章

      网友评论

        本文标题:OpenGL ES2.0 c++ 基础教程

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