OpenGL ES2.0编程步骤
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
1. 保存全局变量的数据结构
以下例子程序均基于Linux平台。
1 typedef struct _escontext 2 { 3 void* userData; // Put your user data here... 4 GLint width; // Window width 5 GLint height; // Window height 6 EGLNativeWindowType hWnd; // Window handle 7 EGLDisplay eglDisplay; // EGL display 8 EGLContext eglContext; // EGL context 9 EGLSurface eglSurface; // EGL surface 10 11 // Callbacks 12 void (ESCALLBACK *drawFunc) ( struct _escontext * ); 13 void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int ); 14 void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime ); 15 }ESContext;
1 typedef struct 2 { 3 // Handle to a program object 4 GLuint programObject; 5 6 // Atrribute Location 7 GLint positionLoc; 8 GLint textureLoc; 9 10 // Uniform location 11 GLint matrixModeLoc; 12 GLint matrixViewLoc; 13 GLint matrixPerspectiveLoc; 14 15 // Sampler location 16 GLint samplerLoc; 17 18 // texture 19 GLuint texture; 20 } UserData;
2. 初始化EGL渲染环境和相关元素(第一步曲)
1 int InitEGL(ESContext * esContext) 2 { 3 NativeWindowType eglWindow = NULL; 4 5 EGLDisplay display; 6 EGLContext context; 7 EGLSurface surface; 8 9 EGLConfig configs[2]; 10 EGLBoolean eRetStatus; 11 EGLint majorVer, minorVer; 12 EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; 13 14 EGLint numConfigs; 15 EGLint cfg_attribs[] = {EGL_BUFFER_SIZE, EGL_DONT_CARE, 16 EGL_DEPTH_SIZE, 16, 17 EGL_RED_SIZE, 5, 18 EGL_GREEN_SIZE, 6, 19 EGL_BLUE_SIZE, 5, 20 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 21 EGL_NONE}; 22 23 // Get default display connection 24 display = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY); 25 if ( display == EGL_NO_DISPLAY ) 26 { 27 return EGL_FALSE; 28 } 29 30 // Initialize EGL display connection 31 eRetStatus = eglInitialize(display, &majorVer, &minorVer); 32 if( eRetStatus != EGL_TRUE ) 33 { 34 return EGL_FALSE; 35 } 36 37 //Get a list of all EGL frame buffer configurations for a display 38 eRetStatus = eglGetConfigs (display, configs, 2, &numConfigs); 39 if( eRetStatus != EGL_TRUE ) 40 { 41 return EGL_FALSE; 42 } 43 44 // Get a list of EGL frame buffer configurations that match specified attributes 45 eRetStatus = eglChooseConfig (display, cfg_attribs, configs, 2, &numConfigs); 46 if( eRetStatus != EGL_TRUE || !numConfigs) 47 { 48 return EGL_FALSE; 49 } 50 51 // Create a new EGL window surface 52 surface = eglCreateWindowSurface(display, configs[0], eglWindow, NULL); 53 if (surface == EGL_NO_SURFACE) 54 { 55 return EGL_FALSE; 56 } 57 58 // Set the current rendering API (EGL_OPENGL_API, EGL_OPENGL_ES_API,EGL_OPENVG_API) 59 eRetStatus = eglBindAPI(EGL_OPENGL_ES_API); 60 if (eRetStatus != EGL_TRUE) 61 { 62 return EGL_FALSE; 63 } 64 65 // Create a new EGL rendering context 66 context = eglCreateContext (display, configs[0], EGL_NO_CONTEXT, context_attribs); 67 if (context == EGL_NO_CONTEXT) 68 { 69 return EGL_FALSE; 70 } 71 72 // Attach an EGL rendering context to EGL surfaces 73 eRetStatus = eglMakeCurrent (display, surface, surface, context); 74 if( eRetStatus != EGL_TRUE ) 75 { 76 return EGL_FALSE; 77 } 78 //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. 79 eglSwapInterval(display,0); 80 81 // Return the context elements 82 esContext->eglDisplay = display; 83 esContext->eglSurface = surface; 84 esContext->eglContext = context; 85 86 return EGL_TRUE; 87 }
3. 生成Program (第二步曲)
3.1 LoadShader
LoadShader主要实现以下功能:
1) 创建Shader对象
2) 装载Shader源码
3) 编译Shader
其实现参考代码如下:
1 /* type specifies the Shader type: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER */ 2 GLuint LoadShader ( GLenum type, const char *shaderSrc ) 3 { 4 GLuint shader; 5 GLint compiled; 6 7 // Create an empty shader object, which maintain the source code strings that define a shader 8 shader = glCreateShader ( type ); 9 10 if ( shader == 0 ) 11 return 0; 12 13 // Replaces the source code in a shader object 14 glShaderSource ( shader, 1, &shaderSrc, NULL ); 15 16 // Compile the shader object 17 glCompileShader ( shader ); 18 19 // Check the shader object compile status 20 glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled ); 21 22 if ( !compiled ) 23 { 24 GLint infoLen = 0; 25 26 glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen ); 27 28 if ( infoLen > 1 ) 29 { 30 char* infoLog = malloc (sizeof(char) * infoLen ); 31 32 glGetShaderInfoLog ( shader, infoLen, NULL, infoLog ); 33 esLogMessage ( "Error compiling shader:\n%s\n", infoLog ); 34 35 free ( infoLog ); 36 } 37 38 glDeleteShader ( shader ); 39 return 0; 40 } 41 42 return shader; 43 }
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
其参考代码如下:
1 GLuint LoadProgram ( const char *vShaderStr, const char *fShaderStr ) 2 { 3 GLuint vertexShader; 4 GLuint fragmentShader; 5 GLuint programObject; 6 GLint linked; 7 8 // Load the vertex/fragment shaders 9 vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr ); 10 fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr ); 11 12 // Create the program object 13 programObject = glCreateProgram ( ); 14 if ( programObject == 0 ) 15 return 0; 16 17 // Attaches a shader object to a program object 18 glAttachShader ( programObject, vertexShader ); 19 glAttachShader ( programObject, fragmentShader ); 20 // Bind vPosition to attribute 0 21 glBindAttribLocation ( programObject, 0, "vPosition" ); 22 // Link the program object 23 glLinkProgram ( programObject ); 24 25 // Check the link status 26 glGetProgramiv ( programObject, GL_LINK_STATUS, &linked ); 27 28 if ( !linked ) 29 { 30 GLint infoLen = 0; 31 32 glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen ); 33 34 if ( infoLen > 1 ) 35 { 36 char* infoLog = malloc (sizeof(char) * infoLen ); 37 38 glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog ); 39 esLogMessage ( "Error linking program:\n%s\n", infoLog ); 40 41 free ( infoLog ); 42 } 43 44 glDeleteProgram ( programObject ); 45 return GL_FALSE; 46 } 47 48 // Free no longer needed shader resources 49 glDeleteShader ( vertexShader ); 50 glDeleteShader ( fragmentShader ); 51 52 return programObject; 53 }
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的链接,现在还缺少真正供进行编译和链接的源码,其参考代码如下:
1 int CreateProgram(ESContext * esContext) 2 { 3 GLuint programObject; 4 5 GLbyte vShaderStr[] = 6 "attribute vec4 vPosition; \n" 7 "void main() \n" 8 "{ \n" 9 " gl_Position = vPosition; \n" 10 "} \n"; 11 12 GLbyte fShaderStr[] = 13 "precision mediump float;\n"14 "void main() \n" 15 "{ \n" 16 " gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n" 17 "} \n"; 18 19 // Create user data 20 esContext->userData = malloc(sizeof(UserData)); 21 UserData *userData = esContext->userData; 22 23 // Load the shaders and get a linked program object 24 programObject = LoadProgram ( (const char*)vShaderStr, (const char*)fShaderStr ); 25 if(programObject == 0) 26 { 27 return GL_FALSE; 28 } 29 30 // Store the program object 31 userData->programObject = programObject; 32 33 // Get the attribute locations 34 userData->positionLoc = glGetAttribLocation ( g_programObject, "v_position" ); 35 glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f ); 36 return 0; 37 }
4. 安装并执行Program(第三步)
1 void Render ( ESContext *esContext ) 2 { 3 UserData *userData = esContext->userData; 4 GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f, 5 -0.5f, -0.5f, 0.0f, 6 0.5f, -0.5f, 0.0f }; 7 8 // Set the viewport 9 glViewport ( 0, 0, esContext->width, esContext->height ); 10 11 // Clear the color buffer 12 glClear ( GL_COLOR_BUFFER_BIT ); 13 14 // Use the program object 15 glUseProgram ( userData->programObject ); 16 17 // Load the vertex data 18 glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices ); 19 glEnableVertexAttribArray ( 0 ); 20 glDrawArrays ( GL_TRIANGLES, 0, 3 ); 21 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( 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处开始的位置和数据格式。其定义如下:
1 void glVertexAttribPointer( 2 GLuint index, // 指示将被修改的通用顶点属性的索引 3 GLint size, // 指点每个顶点元素个数(1~4) 4 GLenum type, // 数组中每个元素的数据类型 5 GLboolean normalized, //指示定点数据值是否被归一化(归一化<[-1,1]或[0,1]>:GL_TRUE,直接使用:GL_FALSE) 6 GLsizei stride, // 连续顶点属性间的偏移量,如果为0,相邻顶点属性间紧紧相邻 7 const GLvoid * pointer);//顶点数组 8 //注:其index应该小于#define GL_MAX_VERTEX_ATTRIBS 0x8869
4.4 glEnableVertexAttribArray
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
只有这三个关键人物,还不能运行,还需要一个协调组织者。其参考代码如下:
1 int main(int argc, char** argv) 2 { 3 ESContext esContext; 4 UserData userData; 5 int iFrames; 6 unsigned long iStartTime,iEndTime; 7 int iDeltaTime; 8 9 memset( &esContext, 0, sizeof( ESContext) ); 10 esContext.userData = &userData; 11 12 esContext.width = 1280; 13 esContext.height = 720; 14 // Init EGL display, surface and context 15 if(!InitEGL(&esContext)) 16 { 17 printf("Init EGL fail\n"); 18 return GL_FALSE; 19 } 20 // compile shader, link program 21 if(!CreateProgram(&esContext)) 22 { 23 printf("Create Program fail\n"); 24 return GL_FALSE; 25 } 26 27 28 iStartTime = GetCurTime(); 29 iFrames = 0; 30 31 while(1) 32 { // render a frame 33 Render(&esContext); 34 iFrames++; 35 36 iEndTime = GetCurTime(); 37 iDeltaTime = iEndTime - iStartTime; 38 if(iDeltaTime >= 5000) 39 { 40 iStartTime = iEndTime; 41 float fFrame = iFrames * 1000.0 / iDeltaTime; 42 iFrames = 0; 43 44 printf("Frame: %f\n", fFrame); 45 } 46 } 47 glDeleteProgram (esContext.userData->programObject); 48 return GL_TRUE; 49 }