More on texture Mapping

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. We can create this image:

with this program:


#include <stdlib.h>
#include <iostream.h>
#include <GL/glut.h>
#include <math.h>

double PI,PI2;

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

void cylinder(int sides){
int n=sides;
double h=PI2/n;
double x,z,xn,zn;
double t=0;
x=cos(t);
z=sin(t);
glBegin(GL_QUADS);
for(int i=0;i<n/2;i++){
t=t+h;
xn=cos(t);
zn=sin(t);
glVertex3f(xn,0,zn);
glVertex3f(xn,1,zn);
glVertex3f(x,1,z);
glVertex3f(x,0,z);
x=xn; z=zn;
}
glEnd();
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0,1,3,0,.5,0,0,1,0);
cylinder(24);
glFlush();
glutSwapBuffers();
}

void myinit()
{
PI=atan(1)*4;
PI2=2*PI;
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;
}
}

glEnable(GL_DEPTH_TEST); /* enable z buffer */
glClearColor (1.0, 1.0, 1.0, 1.0);
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:
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

}

void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutCreateWindow("Cylinder");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2.0, 2.0, -2.0 , 2.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
myinit();
glutDisplayFunc(display);
glutMainLoop();
}

Missing from the function

void cylinder(int sides)

are four calls to glTexCoord2d (which takes two GLfloat parameters). Show the required modifications necessary to produce the picture shown.

My solution was the following:

for(int i=0;i<n/2;i++){
double T=t;
t=t+h;
xn=cos(t);
zn=sin(t);
glTexCoord2d(t,0);
glVertex3f(xn,0,zn);
glTexCoord2d(t,1);
glVertex3f(xn,1,zn);
glTexCoord2d(T,1);
glVertex3f(x,1,z);
glTexCoord2d(T,0);
glVertex3f(x,0,z);
x=xn; z=zn;
}

(where T is an added double variable). The idea is to use the angle for the horizontal coordinate in the texture map, and let the vertical value be 0 or 1 (mapping the full height of the texture image onto the full height of the cylinder).

We looked at loading ".raw" images as texture maps. Courtesy of Blaine Hodges, I downloaded the following function which loades a ".raw" file:

// load a size x size RGB .RAW file as a texture
//http://www.geocities.com/SiliconValley/Code/1219/gltexture.html#RAW%20Texture%20Loader
GLuint LoadTextureRAW( const char * filename, int wrap, int size )
{
GLuint texture;
int width, height;
BYTE * data;
FILE * file;

// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;

// allocate buffer
width = height = size;
data = (BYTE*)malloc( width * height * 3 );

// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );

// allocate a texture name
glGenTextures( 1, &texture );

// select our current texture
glBindTexture( GL_TEXTURE_2D, texture );

// select modulate to mix texture with color for shading
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

// when texture area is small, bilinear filter the closest MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST );
// when texture area is large, bilinear filter the first MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

// if wrap is true, the texture wraps over at the edges (repeat)
// ... false, the texture ends at the edges (clamp)
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
wrap ? GL_REPEAT : GL_CLAMP );

// build our texture MIP maps
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,height, GL_RGB, GL_UNSIGNED_BYTE, data );

// free buffer
free( data );

return texture;

}

Note: this function will require the inclusion of malloc.h and stdio.h (old C headers!).

I used this function to map this picture (Click here to save in raw format):

onto the cylinder from above:

Here's the program:


#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <GL/glut.h>
#include <math.h>

double PI,PI2;

// load a size x size RGB .RAW file as a texture
//http://www.geocities.com/SiliconValley/Code/1219/gltexture.html#RAW%20Texture%20Loader
GLuint LoadTextureRAW( const char * filename, int wrap, int size )
{
GLuint texture;
int width, height;
BYTE * data;
FILE * file;

// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;

// allocate buffer
width = height = size;
data = (BYTE*)malloc( width * height * 3 );

// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );

// allocate a texture name
glGenTextures( 1, &texture );

// select our current texture
glBindTexture( GL_TEXTURE_2D, texture );

// select modulate to mix texture with color for shading
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

// when texture area is small, bilinear filter the closest MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST );
// when texture area is large, bilinear filter the first MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

// if wrap is true, the texture wraps over at the edges (repeat)
// ... false, the texture ends at the edges (clamp)
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
wrap ? GL_REPEAT : GL_CLAMP );

// build our texture MIP maps
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,height, GL_RGB, GL_UNSIGNED_BYTE, data );

// free buffer
free( data );

return texture;

}


void cylinder(int sides){
int n=sides;
double h=PI2/n;
double x,z,xn,zn;
double T,t=0;
x=cos(t);
z=sin(t);
glBegin(GL_QUADS);
for(int i=0;i<n/2;i++){
double T=t;
t=t+h;
xn=cos(t);
zn=sin(t);
glTexCoord2d(t,0);
glVertex3f(xn,0,zn);
glTexCoord2d(t,1);
glVertex3f(xn,1,zn);
glTexCoord2d(T,1);
glVertex3f(x,1,z);
glTexCoord2d(T,0);
glVertex3f(x,0,z);
x=xn; z=zn;
}
glEnd();
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0,1,3,0,.5,0,0,1,0);
cylinder(24);
glFlush();
glutSwapBuffers();
}

void myinit()
{
PI=atan(1)*4;
PI2=2*PI;


glEnable(GL_DEPTH_TEST); /* enable z buffer */
glClearColor (1.0, 1.0, 1.0, 1.0);
glEnable(GL_TEXTURE_2D);
LoadTextureRAW("Crowell.raw",1,256);
}

void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutCreateWindow("Cylinder");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2.0, 2.0, -2.0 , 2.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
myinit();
glutDisplayFunc(display);
glutMainLoop();
}

Next we looked at texture mapping in POV-Ray (free ray tracing software available from www.povray.org). We rendered this simple scene:


background {color rgb <1,1,1>}
camera {
location <0, 2, -2.5>
look_at <0, .5, 0>
}

light_source { <15, 10, -10> color rgb 1}

plane { y, 0
pigment { checker color rgb <1,0,0> , color rgb 1 }
finish { ambient .5 diffuse .5 }
scale 0.5
}

sphere { <0,.5,.0> .5}

to get this image:

By the way, this was rendered with "320 x 240 No AA" selected -- this means it's a little 320 x 240 pixel image with no anti-aliasing employed. This is very fast to render. When you get an image looking right, you can switch to higher resolution and anti-aliasing. Here's the same image, same size but with anti-aliasing:

Well, the sphere in our first example isn't very interesting. We can at least give it a little color with the change


sphere { <0,.5,.0> .5
texture{pigment {color rgb <0,0,1>}}
}

Here's the new image:

We can use "Blue" instead of "rgb <0,0,1>" if we add


#include "colors.inc"

at the top of our file.

Want a little more zing from our sphere? Let's add a little specular lighting:


sphere { <0,.5,.0> .5
pigment {color Blue}
finish {specular 1}
}

produces:

Finally, let's paste an image (in "gif" format) onto our sphere:


sphere { <0,1,0> 1
pigment {
image_map {gif "snow.gif"} translate <-.5,-.5,0> scale 2 translate <0,1,0>}
finish {ambient .6 diffuse .6}
}
A couple of minor changes in the viewer perspective will make things look like the image below.

Here's the image produced:



An excellent reference for POV-Ray is www.povray.org.