由于《OpenGL ES 2.0 Programming Guide》原书第9章的示例代码使用的纹理是程序生成的,作者还自己实现了Mipmap的过程,对于理解Mipmap的原理很有帮助,但是并不实用,遂自己实现了一份C语言版本的(加载本地纹理+调用glGenerateMipmap),希望能够帮助到同样喜欢OpenGL ES 2.0的同学。
废话不多说,直接上代码:
// MipMap2D.c // // This is a simple example that demonstrates generating a mipmap chain // and rendering with it // #include <stdlib.h> #include "esUtil.h" typedef struct { // Handle to a program object GLuint programObject; // Attribute locations GLint positionLoc; GLint texCoordLoc; // Sampler location GLint samplerLoc; // Offset location GLint offsetLoc; // Texture handle GLuint textureId; } UserData; /// // From an RGB8 source image, generate the next level mipmap // GLboolean GenMipMap2D( GLubyte *src, GLubyte **dst, int srcWidth, int srcHeight, int *dstWidth, int *dstHeight ) { int x, y; int texelSize = 3; *dstWidth = srcWidth / 2; if ( *dstWidth <= 0 ) *dstWidth = 1; *dstHeight = srcHeight / 2; if ( *dstHeight <= 0 ) *dstHeight = 1; *dst = malloc ( sizeof(GLubyte) * texelSize * (*dstWidth) * (*dstHeight) ); if ( *dst == NULL ) return GL_FALSE; for ( y = 0; y < *dstHeight; y++ ) { for( x = 0; x < *dstWidth; x++ ) { int srcIndex[4]; float r = 0.0f, g = 0.0f, b = 0.0f; int sample; // Compute the offsets for 2x2 grid of pixels in previous // image to perform box filter srcIndex[0] = (((y * 2) * srcWidth) + (x * 2)) * texelSize; srcIndex[1] = (((y * 2) * srcWidth) + (x * 2 + 1)) * texelSize; srcIndex[2] = ((((y * 2) + 1) * srcWidth) + (x * 2)) * texelSize; srcIndex[3] = ((((y * 2) + 1) * srcWidth) + (x * 2 + 1)) * texelSize; // Sum all pixels for ( sample = 0; sample < 4; sample++ ) { r += src[srcIndex[sample]]; g += src[srcIndex[sample] + 1]; b += src[srcIndex[sample] + 2]; } // Average results r /= 4.0; g /= 4.0; b /= 4.0; // Store resulting pixels (*dst)[ ( y * (*dstWidth) + x ) * texelSize ] = (GLubyte)( r ); (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 1] = (GLubyte)( g ); (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 2] = (GLubyte)( b ); } } return GL_TRUE; } /// // Generate an RGB8 checkerboard image // GLubyte* GenCheckImage( int width, int height, int checkSize ) { int x, y; GLubyte *pixels = malloc( width * height * 3 ); if ( pixels == NULL ) return NULL; for ( y = 0; y < height; y++ ) for ( x = 0; x < width; x++ ) { GLubyte rColor = 0; GLubyte bColor = 0; if ( ( x / checkSize ) % 2 == 0 ) { rColor = 255 * ( ( y / checkSize ) % 2 ); bColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) ); } else { bColor = 255 * ( ( y / checkSize ) % 2 ); rColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) ); } pixels[(y * height + x) * 3] = rColor; pixels[(y * height + x) * 3 + 1] = 0; pixels[(y * height + x) * 3 + 2] = bColor; } return pixels; } /// // 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; } /// // Create a mipmapped 2D texture image // GLuint CreateMipMappedTexture2D( ) { // Texture object handle GLuint textureId; #if 0 int width = 256, height = 256; int level; GLubyte *pixels; pixels = GenCheckImage( width, height, 8 ); if ( pixels == NULL ) return 0; // Generate a texture object glGenTextures ( 1, &textureId ); // Bind the texture object glBindTexture ( GL_TEXTURE_2D, textureId ); // Load mipmap level 0 glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels ); GLubyte *prevImage; GLubyte *newImage; level = 1; prevImage = &pixels[0]; while ( width > 1 && height > 1 ) { int newWidth, newHeight; // Generate the next mipmap level GenMipMap2D( prevImage, &newImage, width, height, &newWidth, &newHeight ); // Load the mipmap level glTexImage2D( GL_TEXTURE_2D, level, GL_RGB, newWidth, newHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, newImage ); // Free the previous image free ( prevImage ); // Set the previous image for the next iteration prevImage = newImage; level++; // Half the width and height width = newWidth; height = newHeight; } free ( newImage ); #else char *fileName = "D:/Projects/Visual Studio 2012/OpenGL_Demo/opengles-book-samples-master/Windows/Chapter_9/Simple_Texture2D/Fieldstone.tga"; textureId = LoadTexture (fileName); glGenerateMipmap(GL_TEXTURE_2D); #endif // Set the filtering mode glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); return textureId; } /// // Initialize the shader and program object // int Init ( ESContext *esContext ) { UserData *userData = esContext->userData; GLbyte vShaderStr[] = "uniform float u_offset; \n" "attribute vec4 a_position; \n" "attribute vec2 a_texCoord; \n" "varying vec2 v_texCoord; \n" "void main() \n" "{ \n" " gl_Position = a_position; \n" " gl_Position.x += u_offset;\n" " v_texCoord = a_texCoord; \n" "} \n"; GLbyte 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" ); // Get the offset location userData->offsetLoc = glGetUniformLocation( userData->programObject, "u_offset" ); // Load the texture userData->textureId = CreateMipMappedTexture2D (); 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 = esContext->userData; GLfloat vVertices[] = { -0.5f, 0.5f, 0.0f, 1.5f, // Position 0 0.0f, 0.0f, // TexCoord 0 -0.5f, -0.5f, 0.0f, 0.75f, // Position 1 0.0f, 1.0f, // TexCoord 1 0.5f, -0.5f, 0.0f, 0.75f, // Position 2 1.0f, 1.0f, // TexCoord 2 0.5f, 0.5f, 0.0f, 1.5f, // Position 3 1.0f, 0.0f // TexCoord 3 }; GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; // 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 glVertexAttribPointer ( userData->positionLoc, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), vVertices ); // 偏移量 4+2 // Load the texture coordinate glVertexAttribPointer ( userData->texCoordLoc, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), &vVertices[4] ); glEnableVertexAttribArray ( userData->positionLoc ); glEnableVertexAttribArray ( userData->texCoordLoc ); // Bind the texture glActiveTexture ( GL_TEXTURE0 ); // 激活 texture unit 0 glBindTexture ( GL_TEXTURE_2D, userData->textureId ); // Set the sampler texture unit to 0 glUniform1i ( userData->samplerLoc, 0 ); // Draw quad with nearest sampling (LEFT) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glUniform1f ( userData->offsetLoc, -0.6f ); glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); // Draw quad with trilinear filtering (RIGHT) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glUniform1f ( userData->offsetLoc, 0.6f ); glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface ); } /// // Cleanup // void ShutDown ( ESContext *esContext ) { UserData *userData = esContext->userData; // 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, "MipMap 2D", 640, 480, ES_WINDOW_RGB ); if ( !Init ( &esContext ) ) return 0; esRegisterDrawFunc ( &esContext, Draw ); esMainLoop ( &esContext ); ShutDown ( &esContext ); }
效果如图: