由于《OpenGL ES 2.0 Programming Guide》原书第9章并没有提供相关的示例,为了加深理解,遂自己实现了一份C语言版本作为练习,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。
废话不多说,直接上代码:
#include <stdlib.h> #include "esUtil.h" #define VERTEX_POS_SIZE 3 // x, y and z #define TEXCOORD_SIZE 2 // u,v #define INDICES_SIZE 6 typedef struct { // Handle to a program object GLuint programObject; // Attribute locations GLint positionLoc; GLint texCoordLoc; // Sampler location GLint samplerLoc; // Texture handle GLuint textureId; // VertexBufferObject Ids ☆ GLuint vboIds[2]; } UserData; /// // 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; } /// // Initialize the shader and program object // int Init ( ESContext *esContext ) { char *fileName = "../Fieldstone.tga"; 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" "void main() \n" "{ \n" " gl_FragColor = texture2D( s_texture, v_texCoord );\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" ); userData->texCoordLoc = glGetAttribLocation ( userData->programObject, "a_texCoord" ); // Get the sampler location userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" ); // Load the texture ☆ userData->textureId = LoadTexture(fileName); // Init VBO ids ☆ userData->vboIds[0] = 0; userData->vboIds[1] = 0; glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f ); return TRUE; } /// // Draw a triangle using the shader pair created in Init() // void Draw ( ESContext *esContext ) { UserData *userData = (UserData *)esContext->userData; 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 }; GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; int numVertices = 4; // vboIds[0] - used to store vertex position AND texture coordination // vboIds[1] - used to store vertex indices // run only once ☆ if ( userData->vboIds[0] == 0 && userData->vboIds[1] == 0) { // Only allocate on the first draw glGenBuffers ( 2, userData->vboIds ); glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glBufferData ( GL_ARRAY_BUFFER, (VERTEX_POS_SIZE + TEXCOORD_SIZE) * sizeof(GLfloat) * numVertices, vVertices, GL_STATIC_DRAW ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] ); glBufferData ( GL_ELEMENT_ARRAY_BUFFER, INDICES_SIZE * sizeof(GLushort), indices, GL_STATIC_DRAW ); } // 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 position glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT, GL_FALSE, (VERTEX_POS_SIZE + TEXCOORD_SIZE) * sizeof(GLfloat), 0 ); // Load the texture coordinate glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] ); glVertexAttribPointer ( userData->texCoordLoc, 2, GL_FLOAT, GL_FALSE, (VERTEX_POS_SIZE + TEXCOORD_SIZE) * sizeof(GLfloat), (void*)(VERTEX_POS_SIZE * sizeof(GLfloat))); glEnableVertexAttribArray ( userData->positionLoc ); glEnableVertexAttribArray ( userData->texCoordLoc ); // Bind the texture glActiveTexture ( GL_TEXTURE0 ); glBindTexture ( GL_TEXTURE_2D, userData->textureId ); // Set the sampler texture unit to 0 glUniform1i ( userData->samplerLoc, 0 ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] ); glDrawElements ( GL_TRIANGLES, INDICES_SIZE, GL_UNSIGNED_SHORT, 0); eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface ); glDisableVertexAttribArray ( userData->positionLoc ); glDisableVertexAttribArray ( userData->texCoordLoc ); glBindBuffer ( GL_ARRAY_BUFFER, 0 ); glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 ); } /// // Cleanup // void ShutDown ( ESContext *esContext ) { UserData *userData = (UserData *)esContext->userData; <span style="white-space:pre"> // Delete Buffers glDeleteBuffers ( 2, userData->vboIds );</span> // Delete texture object glDeleteTextures ( 1, &userData->textureId ); // Delete program object glDeleteProgram ( userData->programObject ); } int main ( int argc, char *argv[] ) { ESContext esContext; UserData userData; esInitContext ( &esContext ); esContext.userData = &userData; esCreateWindow ( &esContext, "Simple VBO Texture 2D", 512, 512, ES_WINDOW_RGB ); if ( !Init ( &esContext ) ) return 0; esRegisterDrawFunc ( &esContext, Draw ); esMainLoop ( &esContext ); ShutDown ( &esContext ); }
效果图: