OpenGL|OpenGL ES2.0编程三步曲

1. 保存全局变量的数据结构
以下例子程序均基于Linux平台。

typedef struct _escontext { void*userData; // Put your user data here... GLintwidth; // Window width GLintheight; // Window height EGLNativeWindowTypehWnd; // Window handle EGLDisplayeglDisplay; // EGL display EGLContexteglContext; // EGL context EGLSurfaceeglSurface; // 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;

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

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源码
【OpenGL|OpenGL ES2.0编程三步曲】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; }

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
其参考代码如下:

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的链接,现在还缺少真正供进行编译和链接的源码,其参考代码如下:


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 = https://www.it610.com/article/malloc(sizeof(UserData)); UserData *userData = https://www.it610.com/article/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(第三步)
void Render ( ESContext *esContext ) { UserData *userData = https://www.it610.com/article/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:
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( GLclampfred, GLclampfgreen,GLclampfblue,GLclampfalpha);
2)glClearIndex
指定color index buffer清除值。void glClearIndex( GLfloatc);
3)glClearDepth
指定depth buffer的清除值,取值范围为:0~1,默认值为1。
void glClearDepth( GLclampddepth);
4)glClearStencil
指定stencil buffer清除值的索引,初始值为0。void glClearStencil( GLints);
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处开始的位置和数据格式。其定义如下:

void glVertexAttribPointer( GLuintindex,// 指示将被修改的通用顶点属性的索引 GLintsize,// 指点每个顶点元素个数(1~4) GLenumtype,// 数组中每个元素的数据类型 GLbooleannormalized,//指示定点数据值是否被归一化(归一化:GL_TRUE,直接使用:GL_FALSE) GLsizeistride,// 连续顶点属性间的偏移量,如果为0,相邻顶点属性间紧紧相邻 const GLvoid *pointer); //顶点数组 //注:其index应该小于#define GL_MAX_VERTEX_ATTRIBS0x8869



4.4 glEnableVertexAttribArray
Enable由索引index指定的通用顶点属性数组。
void glEnableVertexAttribArray( GLuintindex);
void glDisableVertexAttribArray( GLuintindex);
默认状态下,所有客户端的能力被disabled,包括所有通用顶点属性数组。如果被Enable,通用顶点属性数组中的值将被访问并被用于rendering,通过调用顶点数组命令:glDrawArrays, glDrawElements, glDrawRangeElements, glArrayElement, glMultiDrawElements, or glMultiDrawArrays.
4.5 glDrawArrays
void glDrawArrays( GLenummode,
GLintfirst,
GLsizeicount);
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; UserDatauserData; int iFrames; unsigned long iStartTime,iEndTime; int iDeltaTime; memset( &esContext, 0, sizeof( ESContext) ); esContext.userData = https://www.it610.com/article/&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(&esContext); 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; }




    推荐阅读