由于《OpenGL ES 2.0 Programming Guide》原书第12章并没有提供相关的示例,为了加深理解,遂自己实现了一份C语言版本作为练习,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。
废话不多说,直接上代码:
#include <stdlib.h> #include <stdio.h> #include "esUtil.h" #include "userData.h" #define SIZE 512 /// // Load texture from disk // GLuint LoadTexture ( char *fileName ) { int width, height; char *buffer = esLoadTGA ( fileName, &width, &height ); GLuint texId; if ( buffer == NULL ) { esLogMessage ( "Error loading (%s) image.\n", fileName ); return 0; } glGenTextures ( 1, &texId ); glBindTexture ( GL_TEXTURE_2D, texId ); glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); free ( buffer ); return texId; } int initFBO(ESContext *esContext, GLint width, GLint height) { GLenum status; GLint maxRenderbufferSize; UserData *userData = (UserData *)esContext->userData; glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize); // check if GL_MAX_RENDERBUFFER_SIZE is >= texWidth and texHeight if ((maxRenderbufferSize <= width) || (maxRenderbufferSize <= height)) { // cannot use framebuffer objects as we need to create // a depth buffer as a renderbuffer object printf("Cannot use framebuffer objects!\n"); exit(EXIT_FAILURE); return FALSE; } // generate the framebuffer, renderbuffer names glGenFramebuffers(1, &userData->frameBuffer); glGenRenderbuffers(1, &userData->depthRenderBuffer); // bind renderbuffer and create a 16-bit depth buffer // width and height of renderbuffer = width and height of // the texture glBindRenderbuffer(GL_RENDERBUFFER, userData->depthRenderBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); // Texture glGenTextures(1, &userData->textureId); glBindTexture(GL_TEXTURE_2D, userData->textureId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); // bind the framebuffer glBindFramebuffer(GL_FRAMEBUFFER, userData->frameBuffer); // ☆ specify texture as color attachment ☆ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, userData->textureId, 0); // specify depth_renderbufer as depth attachment glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, userData->depthRenderBuffer); // check for framebuffer complete status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { printf("Framebuffer object is not complete!\n"); exit(EXIT_FAILURE); return FALSE; } //glBindFramebuffer(GL_FRAMEBUFFER, 0); return TRUE; } int InitFBOShader(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; const char vShaderStr[] = "attribute vec4 a_position; \n" "attribute vec2 a_texCoord; \n" "varying vec2 v_texCoord; \n" "void main() \n" "{ \n" " gl_Position = a_position; \n" " v_texCoord = a_texCoord; \n" "} \n"; const char fShaderStr[] = "precision mediump float; \n" "varying vec2 v_texCoord; \n" "uniform sampler2D s_texture; \n" "const vec2 texSize = vec2(640., 640.); \n" "const vec2 mosaicSize = vec2(16., 16.); \n" "void main() \n" "{ \n" " vec4 color; \n" " vec2 xy = vec2(v_texCoord.x * texSize.x, v_texCoord.y * texSize.y); \n" " vec2 xyMosaic = vec2(floor(xy.x / mosaicSize.x) * mosaicSize.x, \n" " floor(xy.y / mosaicSize.y) * mosaicSize.y ); \n" " //第几块mosaic \n" " vec2 xyFloor = vec2(floor(mod(xy.x, mosaicSize.x)), \n" " floor(mod(xy.y, mosaicSize.y))); \n" " vec2 uvMosaic = vec2(xyMosaic.x / texSize.x, xyMosaic.y / texSize.y);\n" " color = texture2D( s_texture, uvMosaic ); \n" " gl_FragColor = color; \n" "} \n"; userData->programFBOObject = esLoadProgram(vShaderStr, fShaderStr); // Bind vPosition to attribute 0 //glBindAttribLocation(userData->programFBOObject, 0, "a_position"); userData->positionFBOLoc = glGetAttribLocation ( userData->programFBOObject, "a_position" ); userData->mvpFBOLoc = glGetUniformLocation( userData->programFBOObject, "u_mvpMatrix" ); userData->texcoordFBOLoc = glGetAttribLocation(userData->programFBOObject, "a_texCoord"); userData->samplerFBOLoc = glGetUniformLocation(userData->programFBOObject, "s_texture"); // Load the texture ☆ userData->textureFBOId = LoadTexture("../beard.tga"); // VBO Id userData->vboFBOIds[0] = 0; userData->vboFBOIds[1] = 0; return TRUE; } int initShader(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; const char vShaderStr[] = "uniform mat4 u_mvpMatrix; \n" "attribute vec4 a_position; \n" "attribute vec2 a_texCoord; \n" "varying vec2 v_texCoord; \n" "void main() \n" "{ \n" " gl_Position = u_mvpMatrix * a_position; \n" " v_texCoord = a_texCoord; \n" "} \n"; const char fShaderStr[] = "precision mediump float; \n" "varying vec2 v_texCoord; \n" "uniform sampler2D s_texture; \n" "void main() \n" "{ \n" " vec4 color = texture2D(s_texture, v_texCoord); \n" " gl_FragColor = color; \n" "} \n"; // Load the shaders and get a linked program object userData->programObject = esLoadProgram ( vShaderStr, fShaderStr ); // Get the attribute locations userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" ); // Get the uniform locations userData->mvpLoc = glGetUniformLocation( userData->programObject, "u_mvpMatrix" ); // Get the texture attribute locations userData->texcoordLoc = glGetAttribLocation(userData->programObject, "a_texCoord"); userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture"); // VBO Id userData->vboIds[0] = 0; userData->vboIds[1] = 0; userData->vboIds[2] = 0; return TRUE; } int Init(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; if (!InitFBOShader(esContext)) { printf("initFBOShader exception ! \n"); return FALSE; } if (!initShader(esContext)) { printf("initShader exception ! \n"); return FALSE; } initFBO(esContext, SIZE, SIZE); // Generate the vertex data userData->numIndices = esGenCube( 1.0, &userData->vertices, NULL, &userData->texcoords, &userData->indices ); // Starting rotation angle for the cube userData->angle = 45.0f; glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f ); glClearDepthf( 1.0f ); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); //glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); return TRUE; } // Update the mvp matrix void Update(ESContext *esContext, float deltaTime) { UserData *userData = (UserData *) esContext->userData; ESMatrix perspective; ESMatrix modelview; float aspect; // Compute a rotation angle based on time to rotate the cube userData->angle += ( deltaTime * 40.0f ); if( userData->angle >= 360.0f ) userData->angle -= 360.0f; // Compute the window aspect ratio aspect = (GLfloat) esContext->width / (GLfloat) esContext->height; // Generate a perspective matrix with a 60 degree FOV esMatrixLoadIdentity( &perspective ); esPerspective( &perspective, 60.0f, aspect, 1.0f, 20.0f ); // Generate a model view matrix to rotate/translate the cube esMatrixLoadIdentity( &modelview ); // Translate away from the viewer esTranslate( &modelview, 0.0, 0.0, -2.0 ); // Rotate the cube esRotate( &modelview, userData->angle, 1.0, 0.0, 1.0 ); // Compute the final MVP by multiplying the // modevleiw and perspective matrices together esMatrixMultiply( &userData->mvpMatrix, &modelview, &perspective ); } void DrawToFBO(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; const GLfloat vVertices[] = { -1.f, 1.f, 0.0f, // Position 0 0.0f, 0.0f, // TexCoord 0 -1.f, -1.f, 0.0f, // Position 1 0.0f, 1.0f, // TexCoord 1 1.f, -1.f, 0.0f, // Position 2 1.0f, 1.0f, // TexCoord 2 1.f, 1.f, 0.0f, // Position 3 1.0f, 0.0f // TexCoord 3 }; const GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; const int numIndices = 6; const int numVertices = 4; glBindFramebuffer(GL_FRAMEBUFFER, userData->frameBuffer); // ---------------------------------------------------------- // Set the viewport glViewport ( 0, 0, esContext->width, esContext->height ); // Clear the color buffer glClearColor ( 1.0f, 1.0f, 1.0f, 1.0f ); glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Use the program object glUseProgram ( userData->programFBOObject ); // ---------------------------------------------------------- // Setup VBO if(userData->vboFBOIds[0] == 0 && userData->vboFBOIds[1] == 0) { // Only allocate on the first draw glGenBuffers ( 2, userData->vboFBOIds ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] ); glBufferData ( GL_ARRAY_BUFFER, (3 + 2) * sizeof(GLfloat) * numVertices, vVertices, GL_STATIC_DRAW ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboFBOIds[1] ); glBufferData ( GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort), indices, GL_STATIC_DRAW ); } // ----------------------------------------------------------- // Load the vertex position //glVertexAttribPointer ( userData->positionFBOLoc, 3, GL_FLOAT, // GL_FALSE, 5 * sizeof(GLfloat), vVertices); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] ); glVertexAttribPointer ( userData->positionFBOLoc, 3, GL_FLOAT, GL_FALSE, (3 + 2) * sizeof(GLfloat), 0 ); glEnableVertexAttribArray ( userData->positionFBOLoc ); // ----------------------------------------------------------- // Load the texture coordinate //glVertexAttribPointer(userData->texcoordFBOLoc, 2, GL_FLOAT, // GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] ); glVertexAttribPointer ( userData->texcoordFBOLoc, 2, GL_FLOAT, GL_FALSE, (3 + 2) * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(userData->texcoordFBOLoc); // ----------------------------------------------------------- // Bind the FBO texture // Set the sampler texture unit to 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, userData->textureFBOId); glUniform1i(userData->samplerFBOLoc, 0); // ----------------------------------------------------------- // Draw the quad glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboFBOIds[1] ); //glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices ); glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0 ); // ----------------------------------------------------------- // Clean glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer ( GL_ARRAY_BUFFER, 0 ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 ); glDisableVertexAttribArray ( userData->positionFBOLoc ); glDisableVertexAttribArray ( userData->texcoordFBOLoc ); } void Draw(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; DrawToFBO(esContext); glBindFramebuffer(GL_FRAMEBUFFER, 0); // ---------------------------------------------------------- // Set the viewport glViewport ( 0, 0, esContext->width, esContext->height ); // Clear the color buffer glClearColor (0.0f, 0.0f, 0.0f, 0.0f); glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Use the program object glUseProgram ( userData->programObject ); // ---------------------------------------------------------- const int numVertices = 24; // Setup VBO if(userData->vboIds[0] == 0 && userData->vboIds[1] == 0 && userData->vboIds[2] == 0) { // Only allocate on the first draw glGenBuffers ( 3, userData->vboIds ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glBufferData ( GL_ARRAY_BUFFER, 3 * sizeof(GLfloat) * numVertices, userData->vertices, GL_STATIC_DRAW ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] ); glBufferData ( GL_ARRAY_BUFFER, 2 * sizeof(GLfloat) * numVertices, userData->texcoords, GL_STATIC_DRAW ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] ); glBufferData ( GL_ELEMENT_ARRAY_BUFFER, userData->numIndices * sizeof(GLuint), userData->indices, GL_STATIC_DRAW ); } // ----------------------------------------------------------- // Load the vertex position //glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT, // GL_FALSE, 3 * sizeof(GLfloat), userData->vertices ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0 ); glEnableVertexAttribArray ( userData->positionLoc ); // ----------------------------------------------------------- // Load the texture coordinate //glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT, // GL_FALSE, 2 * sizeof(GLfloat), userData->texcoords); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] ); glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0); glEnableVertexAttribArray(userData->texcoordLoc); // ----------------------------------------------------------- // Load the MVP matrix glUniformMatrix4fv( userData->mvpLoc, 1, GL_FALSE, (GLfloat *) &userData->mvpMatrix.m[0][0] ); // ----------------------------------------------------------- // Bind the new texture // Set the sampler texture unit to 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, userData->textureId); glUniform1i(userData->samplerLoc, 0); // ----------------------------------------------------------- // Draw the cube glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] ); //glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices ); glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, 0 ); eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface ); // ----------------------------------------------------------- // Unbind the texture glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer ( GL_ARRAY_BUFFER, 0 ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 ); glDisableVertexAttribArray ( userData->positionLoc ); glDisableVertexAttribArray ( userData->texcoordLoc ); } void ShutDown(ESContext *esContext) { UserData *userData = (UserData *)esContext->userData; //glDisable(GL_BLEND); if ( userData->vertices != NULL ) { free ( userData->vertices ); } if ( userData->texcoords != NULL ) { free ( userData->texcoords ); } if ( userData->indices != NULL ) { free ( userData->indices ); } // Delete program object glDeleteProgram (userData->programObject); glDeleteProgram(userData->programFBOObject); // Delete texture glDeleteTextures(1, &userData->textureId); glDeleteTextures(1, &userData->textureFBOId); glDeleteRenderbuffers(1, &userData->depthRenderBuffer); glDeleteFramebuffers(1, &userData->frameBuffer); // Delete VBO glDeleteBuffers ( 2, userData->vboFBOIds ); glDeleteBuffers ( 3, userData->vboIds ); } int main ( int argc, char *argv[] ) { ESContext esContext; UserData userData; esInitContext ( &esContext ); esContext.userData = &userData; esCreateWindow ( &esContext, "Simple FBO", SIZE, SIZE, ES_WINDOW_RGB | ES_WINDOW_MULTISAMPLE ); if ( !Init ( &esContext ) ) return 0; esRegisterDrawFunc ( &esContext, Draw ); esRegisterUpdateFunc ( &esContext, Update ); esMainLoop ( &esContext ); ShutDown ( &esContext ); }
效果图: