20. OpenGL MipMap Generation (Version 2.0)
Introduction
Mipmaps are a group of scaled-down versions of a texture that OpenGL uses to enhance rendering performance and image quality. Instead of resizing the texture dynamically for each frame, OpenGL selects the mipmap level that best fits the object’s size. This approach smooths out textures, eliminates aliasing, and creates a visually pleasing result.
In this tutorial, you’ll learn how to generate mipmaps in OpenGL, update texture filters to support mipmapping, and integrate mipmaps into your rendering workflow.
Why Use Mipmaps?
Mipmaps improve rendering by:
- Reducing aliasing and moiré patterns when textures are minified (displayed smaller than their original size).
- Improving performance by using smaller texture levels when the full resolution is unnecessary.
Generating Mipmaps
To generate mipmaps, replace the typical glTexImage2D
call with gluBuild2DMipmaps
. Here’s an example:
// Replace this: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // With this: gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
In this function:
– GL_TEXTURE_2D
: Specifies the target texture.
– 3
: Specifies the internal format (e.g., RGB).
– width
and height
: Dimensions of the base texture.
– GL_RGB
, GL_UNSIGNED_BYTE
: Specifies the texture format and data type.
– data
: The raw texture data.
Configuring Texture Filters
When using mipmaps, the texture filters must also be updated to take advantage of them. Replace the default linear filters with mipmap filters:
// Default filters glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Mipmap filters glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
Tutorial Code
Here is the complete code demonstrating mipmap generation and usage:
#include <GL/gl.h> #include <GL/glut.h> #include <stdio.h> GLuint texture; // The texture object GLfloat angle = 0.0; // Rotation angle // Function to load texture with mipmaps GLuint LoadTexture(const char *filename, int width, int height) { GLuint texture; unsigned char *data; FILE *file; // Load the texture data from file file = fopen(filename, "rb"); if (file == NULL) return 0; data = (unsigned char *)malloc(width * height * 3); fread(data, width * height * 3, 1, file); fclose(file); glGenTextures(1, &texture); // Generate a texture object glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Set texture parameters for mipmapping glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Generate the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); free(data); // Free the texture data return texture; } void FreeTexture(GLuint texture) { glDeleteTextures(1, &texture); // Delete the texture object } void square(void) { glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture glPushMatrix(); glRotatef(angle, 1.0f, 1.0f, 1.0f); glBegin(GL_QUADS); glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, -1.0); glTexCoord2d(1.0, 0.0); glVertex2d(1.0, -1.0); glTexCoord2d(1.0, 1.0); glVertex2d(1.0, 1.0); glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, 1.0); glEnd(); glPopMatrix(); } void display(void) { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glEnable(GL_TEXTURE_2D); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); square(); glutSwapBuffers(); angle++; } void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(500, 500); glutCreateWindow("OpenGL Mipmaps Example"); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); texture = LoadTexture("texture.raw", 256, 256); // Load texture glutMainLoop(); FreeTexture(texture); return 0; }
If you have any questions or run into issues, feel free to email me at swiftless@gmail.com. Happy coding!
http://www.opengl.org/wiki/Common_Mistakes#gluBuild2DMipmaps
You should be using glGenerateMipmap(GL_TEXTURE_2D).
Never use gluBuild2DMipmaps
http://www.opengl.org/wiki/Common_Mistakes#gluBuild2DMipmaps
Right, so this line:
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
. . . will give undefined behavior.
Setting the mipmap flag in the mag filter will have undefined results. Using mipmapping for magnification doesn’t make sense.
Hi Geometrian,
You’re right, you can leave the mag filter as linear as this doesn’t make sense to set.
However it half makes sense in the case of magnification for effects such as bloom, where you take the smaller mipmap and apply it to a shape too large on purpose to get a blurry effect. I see this as magnification, albeit a skewed interpretation 😛
Cheers,
Swiftless
Magnification doesn’t work that way. Mipmapping is designed to work only in minification. This is why, according to the spec, no mipmapping is defined for mag filters. See (http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml).
While mipmap levels can be blown up for the purposes you describe, it can’t be done simply by setting the mag filter to a mipmap. You’d need to select the actual level when you draw it in the fullscreen pass.
Hi Geometrian,
I think we got our wires crossed, when you first mentioned magnification I was thinking as I described in my response (as an effect), I now understand where you are coming from (magnification as in the magnification level of the texture, not magnification as in the effect), and agree totally.
Cheers,
Swiftless