mipmaps

In memory of my great friend and colleague, late Dr. Mark Harris. This page was made possible by his great lectures in this course in 2000.

An OpenGL texture map starts as a 2^n x 2^n image, for some integer n. For example, we might have a 32 x 32 array of RGB triplets. Taking texels in clumps of four and averaging, it is easy to generate a reduced 16 x 16 texture map which is appropriate for small regions (thus avoiding aliasing). We can continue to pre-calculate 8 x 8, 4 x 4, 2 x 2 and 1 x 1 texture maps. All this is done automatically by calling the GLU function

gluBuild2DMipmaps

NAME

gluBuild2DMipmaps -- create 2-D mipmaps

C SPECIFICATION

int gluBuild2DMipmaps(GLenum target,
GLint components,
GLint width,
GLint height,
GLenum format,
GLenum type,
const void *data)

PARAMETERS

target
Specifies the target texture. Must be GL_TEXTURE_2D.
components
Specifies the number of color components in the texture. Must be 1, 2, 3, or 4.
width
, height
Specifies the width and height, respectively, of the texture image.
format
Specifies the format of the pixel data. Must be one of: GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, and GL_LUMINANCE_ALPHA.
type
Specifies the data type for data. Must be one of: GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, or GL_FLOAT.
data
Specifies a pointer to the image data in memory.

DESCRIPTION

gluBuild2DMipmaps obtains the input image and generates all mipmap images (using gluScaleImage) so that the input image can be used as a mipmapped texture image. glTexImage2D is then called to load each of the images. If the dimensions of the input image are not powers of two, then the image is scaled so that both the width and height are powers of two before the mipmaps are generated.

A return value of 0 indicates success. Otherwise, a GLU error code is returned (see gluErrorString).

(From the OpenGL documentation.)

Mipmaps are useful for reducing images, not fixed checkerboard patterns. Nonetheless, here is an example which shows a series of cubes of different sizes. The mipmaps only "kick in" in the last cube, since no texture map reduction is needed at the other scales. Note that even the first mipmap of a red/white checkered pattern results in solid pink, since we average two white texels with two red texels.

Here's the image:

And here's the program:

#include <GL/glut.h>
#pragma warning( disable : 4305 )

typedef GLfloat point3[3];

GLfloat w=.3; //x- and y- bounds for frustum

// List all vertices in array and reference later by index
point3 p[8]={
{0,0,0},
{0,0,1},
{1,0,1},
{1,0,0},
{0,1,0},
{0,1,1},
{1,1,1},
{1,1,0}
};

typedef unsigned char rgb[3];
const int DIM=32;
rgb texture[DIM][DIM];

void drawPoint(int i){
// Draw point i
glVertex3fv(p[i]);
}

void drawFace(int i1,int i2,int i3,int i4){
// draw a face of the cube, mapping texture
glBegin(GL_POLYGON);
glTexCoord2d(0,0);
drawPoint(i1);
glTexCoord2d(1,0);
drawPoint(i2);
glTexCoord2d(1,1);
drawPoint(i3);
glTexCoord2d(0,1);
drawPoint(i4);
glEnd();
}

void drawCube()
{
drawFace(0,3,2,1); //bottom
drawFace(1,2,6,5); //front
drawFace(2,3,7,6); //right side
drawFace(0,1,5,4); //left
drawFace(3,0,4,7); //back side
drawFace(4,5,6,7); //top
}

void myinit(void)
{
int i,j;
//generate checkerboard texture map
for(i=0;i<DIM;i++){
for(j=0;j<DIM;j++){
texture[i][j][0]=255;
texture[i][j][1]=(i+j)%2?255:0;
texture[i][j][2]=(i+j)%2?255:0;
}
}
gluBuild2DMipmaps(GL_TEXTURE_2D,3,DIM,DIM,GL_RGB,GL_UNSIGNED_BYTE,texture);


/* attributes */
glClearColor(1.0, 1.0, 1.0, 1.0); /* white background */
/* set up viewing */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-w,w,-w,w,.4,8);
glEnable(GL_TEXTURE_2D);
// Point to our texture map
glTexImage2D(GL_TEXTURE_2D,0,3,DIM,DIM,0,GL_RGB,GL_UNSIGNED_BYTE,texture);
// Describe how to enlarge or reduce texture:
// when texture area is small, bilinear filter the closest MIP map
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
// when texture area is large, bilinear filter the first MIP map
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
void display( void )
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.5,1.5,1.5,1,.5,.5,0,1,0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /*clear the window */
double x=1;
for(int i=0;i<4;i++){
drawCube();
glTranslatef(1,0,0);
glScalef(.5,.5,.5);
}
glFlush(); /* clear buffers */
}

void key(unsigned char key,int x, int y)
{
static int level=1;
glTexImage2D(GL_TEXTURE_2D,level++,3,DIM,DIM,0,GL_RGB,GL_UNSIGNED_BYTE,texture);
glutPostRedisplay();
}


void main(int argc, char** argv)
{
/* Standard GLUT initialization */
glutInit(&argc,argv);
glutInitDisplayMode (GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(500,500); /* 500 x 500 pixel window */
glutInitWindowPosition(0,0); /* place window top left on display */
glutCreateWindow("Cube"); /* window title */
glutDisplayFunc(display); /* display callback invoked when window opened */
glutKeyboardFunc(key);

myinit(); /* set attributes */
glEnable(GL_DEPTH_TEST); /* Enable hidden--surface--removal */
glutMainLoop(); /* enter event loop */
}

Next we looked at the issue of mapping textures onto nonplanar surfaces. Let's look at the sphere in particular. A unit sphere can be described parametrically by:

x = cos(2*Pi*s)
y = sin(2*Pi*s)cos(2*Pi*t)
z = sin(2*Pi*s)sin(2*Pi*t)

To check that you understand this

Unfortunately, we need s and t as functions of x,y,z to apply the texture map, rather than x,y,z as functions of s and t. Fortunately, these equations are easy to solve for s and t. The first equation gives s = arccos(x)/(2*Pi). We can then substitute into the second equation and solve for t. This was applied to the program below and obtained the following image, mapping the unit square onto the unit sphere:

Modify the above program to create the above image. Try to get this on your own, at least give it a try so you what needs to be done. If you need hint, here is the program.