您的位置:首页 > 编程语言

OpenGL ES 2.0 编程三步曲

2015-03-20 10:37 417 查看





Android OpenGL库加载过程源码分析//http://mobile.51cto.com/aengine-437165.htm



本文简单介绍OpenGL库的加载过程。OpenGL以动态库的方式提供,因此在使用OpenGL的接口函数绘图前,需要加载OpenGL库,并得到接口函数指针。函数EGLBoolean
egl_init_drivers()就是负责OpenGL库的加载。





很重要

///////////////////////////////////////////////////////////////

初始化EGL

OpenGL ES 是一个平台中立的图形库,在它能够工作之前,需要与一个实际的窗 口系统关联起来,这与 OpenGL 是一样的。但不一样的是,这部份工作有标准,
这个标准就是 EGL 。而 OpenGL 时代在不同平台上有不同的机制以关联窗口系 统,在 Windows 上是 wgl,在 X-Window 上是 xgl,在 Apple OS 上是 agl 等。 EGL 的工作方式和部份术语都接近于 xgl。

OpenGL ES的初始化过程如下图所示意:
Display → Config → Surface
                        ↑
                      Context
                        ↑
Application → OpenGL Command


1. 获取 Display。

Display 代表显示器,在有些系统上可以有多个显示器,也就会有多个 Display。 获得 Display 要调用
EGLboolean eglGetDisplay(NativeDisplay dpy)


参数一般为 EGL_DEFAULT_DISPLAY 。该参数实际的意义是平台实现相关的,在 X-Window 下是 XDisplay ID,在 MS Windows 下是 Window DC。

2. 初始化 egl。

初始化 egl 调用
EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)


该函数会进行一些内部初始化工作,并传回 EGL 版本号(major.minor)。

3. 选择Config。

所谓 Config 实际指的是 FrameBuffer 的参数,在 MS Windows 下对应于 PixelFormat,在 X-Window 下对应 Visual。一般用
EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list,
            EGLConfig * config, EGLint config_size, EGLint *num_config)


其中 attr_list 是以 EGL_NONE 结束的参数数组,通常以 id,value 依次存放, 对于个别标识性的属性可以只有 id,没有 value。

另一个办法是用
EGLboolean eglGetConfigs(EGLDisplay dpy, EGLConfig * config, EGLint
config_size, EGLint *num_config)


来获得所有 config。

这两个函数都会返回不多于 config_size 个 Config,结果保存在 config[] 中, 系统的总 Config 个数保存在 num_config 中。可以利用 eglGetConfig() 中间 两个参数为 0 来查询系统支持的 Config 总个数。

Config 有众多的 Attribute,这些 Attribute 决定 FrameBuffer 的格式和能 力,通过
eglGetConfigAttrib ()


来读取,但不能修改。

4. 构造Surface。

Surface 实际上就是一个 FrameBuffer,通过
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg,
                NativeWindow win, EGLint *cfg_attr)


来创建一个可实际显示的 Surface。系统通常还支持另外两种 Surface: PixmapSurface 和 PBufferSurface,这两种都不是可显示的 Surface, PixmapSurface 是保存在系统内存中的位图,PBuffer 则是保存在显存中的帧。

Surface 也有一些 attribute,基本上都可以故名思意,
EGL_HEIGHT
EGL_WIDTH
EGL_LARGEST_PBUFFER
EGL_TEXTURE_FORMAT
EGL_TEXTURE_TARGET
EGL_MIPMAP_TEXTURE
EGL_MIPMAP_LEVEL


通过 eglSurfaceAttrib() 设置、eglQuerySurface()读取。
eglSurfaceAttrib()

eglQuerySurface()


5. 创建Context。

OpenGL 的 pipeline 从程序的角度看就是一个状态机,有当前的颜色、纹理坐 标、变换矩阵、绚染模式等一大堆状态,这些状态作用于程序提交的顶点坐标等 图元从而形成帧缓冲内的像素。

OpenGL 的编程接口中,Context 就代表这个状态机,程序的主要工作就是向 Context 提供图元、设置状态,偶尔也从 Context 里获取一些信息。


EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write,
                EGLSurface read, EGLContext * share_list)


来创建一个 Context。

6. 绘制。

应用程序通过 OpenGL API 进行绘制,一帧完成之后,调用
eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)


来显示。

OpenGL ES 2.0 编程三步曲





OpenGL 与 OpenGL ES的关系OpenGL ES 是基于桌面版本OpenGL 的:

OpenGL ES 1.0 基于OpenGL 1.3 , 在2003年发布

OpenGL ES 1.1 基于OpenGL 1.5 , 在2004年发布

OpenGL ES 2.0 基于OpenGL2.0, 在2007年发布

OpenGL 2.0 向下兼容OpenGL 1.5 而 OpenGL ES 2.0 和OpenGL ES 1.x 不兼容,是两种完全不同的实现。

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

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

[cpp]view plaincopy
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;


[cpp]view plaincopy
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渲染环境和相关元素(第一步曲)

[cpp]view plaincopy
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};   
     // 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->eglDisplay = display;   
     esContext->eglSurface = surface;   
     esContext->eglContext = context;   
     return EGL_TRUE;   
}


3. 生成Program (第二步曲)

3.1 LoadShader

LoadShader主要实现以下功能:

1) 创建Shader对象

2) 装载Shader源码

3) 编译Shader

其实现参考代码如下:

[cpp]view plaincopy
/* 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;   
}


1)glCreateShader

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

(1) GL_VERTEX_SHADER: 它运行在可编程的“顶点处理器”上,用于代替固定功能的顶点处理;

(2) GL_FRAGMENT_SHADER: 它运行在可编程的“片断处理器”上,用于代替固定功能的片段处理;

2)glShaderSource

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

3)glCompileShader

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

4)glGetShaderiv

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

3.2 LoadProgram

其参考代码如下:

[cpp]view plaincopy
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;   
}


1)glCreateProgram

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

2)glAttachShader

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

3)glBindAttribLocation

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

4)glLinkProgram

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

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

在3.1中只实现了Shader的编译,在3.2中只实现了Program的链接,现在还缺少真正供进行编译和链接的源码,其参考代码如下:

[cpp]view plaincopy
int CreateProgram(ESContext * esContext)   
{   
     GLuint programObject;   
     GLbyte vShaderStr[] =     
      "attribute vec4 vPosition;    \n"   
      "void main()                  \n"   
      "{                            \n"   
      "   gl_Position = vPosition;  \n"   
      "}                            \n";   
      
     GLbyte fShaderStr[] =     
      "precision mediump float;\n"\   
      "void main()                                  \n"   
      "{                                            \n"   
      "  gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n"   
      "}                                                    \n";   
       
    // 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 0;   
}


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

[cpp]view plaincopy
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);


[cpp]view plaincopy

4.1 glClear

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

1)GL_COLOR_BUFFER_BIT

2)GL_DEPTH_BUFFER_BIT

3)GL_ACCUM_BUFFER_BIT

4)GL_STENCIL_BUFFER_BIT

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

1)gClearColor

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


2)glClearIndex

指定color index buffer清除值。void glClearIndex( GLfloat c);

3)glClearDepth

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

void glClearDepth( GLclampd depth);

4)glClearStencil

指定stencil buffer清除值的索引,初始值为0。void glClearStencil( GLint s);

5)glClearAccum

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

void glClearAccum( GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha);

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处开始的位置和数据格式。其定义如下:

[cpp]view plaincopy
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

只有这三个关键人物,还不能运行,还需要一个协调组织者。其参考代码如下:

[cpp]view plaincopy
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;   
}


//http://mobile.51cto.com/app-show-436809.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: