1 // 2 // Book: OpenGL(R) ES 2.0 Programming Guide 3 // Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner 4 // ISBN-10: 0321502795 5 // ISBN-13: 9780321502797 6 // Publisher: Addison-Wesley Professional 7 // URLs: http://safari.informit.com/9780321563835 8 // http://www.opengles-book.com 9 // 10 11 // MipMap2D.c 12 // 13 // This is a simple example that demonstrates generating a mipmap chain 14 // and rendering with it 15 // 16 #include <stdlib.h> 17 #include "esUtil.h" 18 19 typedef struct 20 { 21 // Handle to a program object 22 GLuint programObject; 23 24 // Attribute locations 25 GLint positionLoc; 26 GLint texCoordLoc; 27 28 // Sampler location 29 GLint samplerLoc; 30 31 // Offset location 32 GLint offsetLoc; 33 34 // Texture handle 35 GLuint textureId; 36 37 } UserData; 38 39 40 /// 41 // From an RGB8 source image, generate the next level mipmap 42 // 43 GLboolean GenMipMap2D( GLubyte *src, GLubyte **dst, int srcWidth, int srcHeight, int *dstWidth, int *dstHeight ) 44 { 45 int x, 46 y; 47 int texelSize = 3; 48 49 *dstWidth = srcWidth / 2; 50 if ( *dstWidth <= 0 ) 51 *dstWidth = 1; 52 53 *dstHeight = srcHeight / 2; 54 if ( *dstHeight <= 0 ) 55 *dstHeight = 1; 56 57 *dst = malloc ( sizeof(GLubyte) * texelSize * (*dstWidth) * (*dstHeight) ); 58 if ( *dst == NULL ) 59 return GL_FALSE; 60 61 for ( y = 0; y < *dstHeight; y++ ) 62 { 63 for( x = 0; x < *dstWidth; x++ ) 64 { 65 int srcIndex[4]; 66 float r = 0.0f, 67 g = 0.0f, 68 b = 0.0f; 69 int sample; 70 71 // Compute the offsets for 2x2 grid of pixels in previous 72 // image to perform box filter 73 srcIndex[0] = 74 (((y * 2) * srcWidth) + (x * 2)) * texelSize; 75 srcIndex[1] = 76 (((y * 2) * srcWidth) + (x * 2 + 1)) * texelSize; 77 srcIndex[2] = 78 ((((y * 2) + 1) * srcWidth) + (x * 2)) * texelSize; 79 srcIndex[3] = 80 ((((y * 2) + 1) * srcWidth) + (x * 2 + 1)) * texelSize; 81 82 // Sum all pixels 83 for ( sample = 0; sample < 4; sample++ ) 84 { 85 r += src[srcIndex[sample]]; 86 g += src[srcIndex[sample] + 1]; 87 b += src[srcIndex[sample] + 2]; 88 } 89 90 // Average results 91 r /= 4.0; 92 g /= 4.0; 93 b /= 4.0; 94 95 // Store resulting pixels 96 (*dst)[ ( y * (*dstWidth) + x ) * texelSize ] = (GLubyte)( r ); 97 (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 1] = (GLubyte)( g ); 98 (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 2] = (GLubyte)( b ); 99 } 100 } 101 102 return GL_TRUE; 103 } 104 105 /// 106 // Generate an RGB8 checkerboard image 107 // 108 GLubyte* GenCheckImage( int width, int height, int checkSize ) 109 { 110 int x, 111 y; 112 GLubyte *pixels = malloc( width * height * 3 ); 113 114 if ( pixels == NULL ) 115 return NULL; 116 117 for ( y = 0; y < height; y++ ) 118 for ( x = 0; x < width; x++ ) 119 { 120 GLubyte rColor = 0; 121 GLubyte bColor = 0; 122 123 if ( ( x / checkSize ) % 2 == 0 ) 124 { 125 rColor = 255 * ( ( y / checkSize ) % 2 ); 126 bColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) ); 127 } 128 else 129 { 130 bColor = 255 * ( ( y / checkSize ) % 2 ); 131 rColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) ); 132 } 133 134 pixels[(y * height + x) * 3] = rColor; 135 pixels[(y * height + x) * 3 + 1] = 0; 136 pixels[(y * height + x) * 3 + 2] = bColor; 137 } 138 139 return pixels; 140 } 141 142 /// 143 // Create a mipmapped 2D texture image 144 // 145 GLuint CreateMipMappedTexture2D( ) 146 { 147 // Texture object handle 148 GLuint textureId; 149 int width = 256, 150 height = 256; 151 int level; 152 GLubyte *pixels; 153 GLubyte *prevImage; 154 GLubyte *newImage = NULL; 155 156 pixels = GenCheckImage( width, height, 8 ); 157 if ( pixels == NULL ) 158 return 0; 159 160 // Generate a texture object 161 glGenTextures ( 1, &textureId ); 162 163 // Bind the texture object 164 glBindTexture ( GL_TEXTURE_2D, textureId ); 165 166 // Load mipmap level 0 167 glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 168 0, GL_RGB, GL_UNSIGNED_BYTE, pixels ); 169 170 level = 1; 171 prevImage = &pixels[0]; 172 173 while ( width > 1 && height > 1 ) 174 { 175 int newWidth, 176 newHeight; 177 178 // Generate the next mipmap level 179 GenMipMap2D( prevImage, &newImage, width, height, 180 &newWidth, &newHeight ); 181 182 // Load the mipmap level 183 glTexImage2D( GL_TEXTURE_2D, level, GL_RGB, 184 newWidth, newHeight, 0, GL_RGB, 185 GL_UNSIGNED_BYTE, newImage ); 186 187 // Free the previous image 188 free ( prevImage ); 189 190 // Set the previous image for the next iteration 191 prevImage = newImage; 192 level++; 193 194 // Half the width and height 195 width = newWidth; 196 height = newHeight; 197 } 198 199 free ( newImage ); 200 201 // Set the filtering mode 202 glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); 203 glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 204 205 return textureId; 206 207 } 208 209 210 /// 211 // Initialize the shader and program object 212 // 213 int Init ( ESContext *esContext ) 214 { 215 UserData *userData = esContext->userData; 216 GLbyte vShaderStr[] = 217 "uniform float u_offset; \n" 218 "attribute vec4 a_position; \n" 219 "attribute vec2 a_texCoord; \n" 220 "varying vec2 v_texCoord; \n" 221 "void main() \n" 222 "{ \n" 223 " gl_Position = a_position; \n" 224 " gl_Position.x += u_offset;\n" 225 " v_texCoord = a_texCoord; \n" 226 "} \n"; 227 228 GLbyte fShaderStr[] = 229 "precision mediump float; \n" 230 "varying vec2 v_texCoord; \n" 231 "uniform sampler2D s_texture; \n" 232 "void main() \n" 233 "{ \n" 234 " gl_FragColor = texture2D( s_texture, v_texCoord );\n" 235 "} \n"; 236 237 // Load the shaders and get a linked program object 238 userData->programObject = esLoadProgram ( vShaderStr, fShaderStr ); 239 240 // Get the attribute locations 241 userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" ); 242 userData->texCoordLoc = glGetAttribLocation ( userData->programObject, "a_texCoord" ); 243 244 // Get the sampler location 245 userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" ); 246 247 // Get the offset location 248 userData->offsetLoc = glGetUniformLocation( userData->programObject, "u_offset" ); 249 250 // Load the texture 251 userData->textureId = CreateMipMappedTexture2D (); 252 253 glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f ); 254 return TRUE; 255 } 256 257 /// 258 // Draw a triangle using the shader pair created in Init() 259 // 260 void Draw ( ESContext *esContext ) 261 { 262 UserData *userData = esContext->userData; 263 GLfloat vVertices[] = { -0.5f, 0.5f, 0.0f, 1.5f, // Position 0 264 0.0f, 0.0f, // TexCoord 0 265 -0.5f, -0.5f, 0.0f, 0.75f, // Position 1 266 0.0f, 1.0f, // TexCoord 1 267 0.5f, -0.5f, 0.0f, 0.75f, // Position 2 268 1.0f, 1.0f, // TexCoord 2 269 0.5f, 0.5f, 0.0f, 1.5f, // Position 3 270 1.0f, 0.0f // TexCoord 3 271 }; 272 GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; 273 274 // Set the viewport 275 glViewport ( 0, 0, esContext->width, esContext->height ); 276 277 // Clear the color buffer 278 glClear ( GL_COLOR_BUFFER_BIT ); 279 280 // Use the program object 281 glUseProgram ( userData->programObject ); 282 283 // Load the vertex position 284 glVertexAttribPointer ( userData->positionLoc, 4, GL_FLOAT, 285 GL_FALSE, 6 * sizeof(GLfloat), vVertices ); 286 // Load the texture coordinate 287 glVertexAttribPointer ( userData->texCoordLoc, 2, GL_FLOAT, 288 GL_FALSE, 6 * sizeof(GLfloat), &vVertices[4] ); 289 290 glEnableVertexAttribArray ( userData->positionLoc ); 291 glEnableVertexAttribArray ( userData->texCoordLoc ); 292 293 // Bind the texture 294 glActiveTexture ( GL_TEXTURE0 ); 295 glBindTexture ( GL_TEXTURE_2D, userData->textureId ); 296 297 // Set the sampler texture unit to 0 298 glUniform1i ( userData->samplerLoc, 0 ); 299 300 // Draw quad with nearest sampling 301 glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 302 glUniform1f ( userData->offsetLoc, -0.6f ); 303 glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); 304 305 // Draw quad with trilinear filtering 306 glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); 307 glUniform1f ( userData->offsetLoc, 0.6f ); 308 glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); 309 310 eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface ); 311 } 312 313 /// 314 // Cleanup 315 // 316 void ShutDown ( ESContext *esContext ) 317 { 318 UserData *userData = esContext->userData; 319 320 // Delete texture object 321 glDeleteTextures ( 1, &userData->textureId ); 322 323 // Delete program object 324 glDeleteProgram ( userData->programObject ); 325 } 326 327 328 int main ( int argc, char *argv[] ) 329 { 330 ESContext esContext; 331 UserData userData; 332 333 esInitContext ( &esContext ); 334 esContext.userData = &userData; 335 336 esCreateWindow ( &esContext, TEXT("MipMap 2D"), 320, 240, ES_WINDOW_RGB ); 337 338 if ( !Init ( &esContext ) ) 339 return 0; 340 341 esRegisterDrawFunc ( &esContext, Draw ); 342 343 esMainLoop ( &esContext ); 344 345 ShutDown ( &esContext ); 346 } 347