OpenGL ES2 0程式設計三步曲
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
1. 儲存全域性變數的資料結構
以下例子程式均基於Linux平臺。
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;
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原始碼
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 = 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(第三步)
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:
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處開始的位置和資料格式。其定義如下:
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.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
只有這三個關鍵人物,還不能執行,還需要一個協調組織者。其參考程式碼如下:
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(&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;}