# OpenGL Textures

CS 481 Lecture, Dr. Lawlor

Before I forget, here's the little "polka dot" texture we built last class:
`        vec3 v=clamp(1.0*fract(vec3(10.0*worldCoords)),0.0,+1.0)-0.5; // grid        float c=clamp(1.0e1*(length(v)-0.3),0.0,1.0); // radius=0.3 polka dots!        vec4 polka=c*vec4(1,0,0,0)+(1-c)*vec4(0,0,1,0); // red-blue color blend`
And here's the funky grid-of-bullseyes:
`        vec3 v=clamp(1.0*fract(vec3(10.0*worldCoords)),0.0,+1.0)-0.5; // grid        float c=sin(100.0*length(v)); // oscillating function of radius        vec4 funka=c*vec4(1,0,0,0)+(1-c)*vec4(0,0,1,0); // red-blue blend`
I've added these to a new "481_polkadot" example on the CS 481 main page.  Try it!

## Texture File Formats

Generally, there are two classes of file formats out there: simple but huge, and complicated but small.  The simple formats don't do any real data compression, so they're really easy for programs to read and write, but they don't do any data compression, so they take up a lot of space on disk.  The complicated formats do data compression, so they're really unreasonably difficult to read without a dedicated library, but on the plus side they take much less space on disk.

Typical compressed image formats:
• JPEG, a "lossy" format, uses discrete cosine transform.
• PNG, a lossless format, uses an encoding similar to zip.
Typical simple image formats:
• BMP, a simple format from Microsoft.
• PPM, a simple format popular on UNIX machines.
• TGA, the ancient "Targa" format, that's still suprisingly useful.
It's pretty easy to write code that reads these simple image formats from disk, since they're all just some sort of small binary header followed by RGB pixel data.  In the 481_texture example program, I'm using the "image.cpp" functions from Nigel Stewart's GLT library to read images, although I've slightly modified my versions to work outside of the rest of GLT.

 Name Stores Compression Advantages Disadvantages jpg JPEG RGB Lossy (DCT) Amazingly tight compression, especially for photos. libjpeg is big, and no alpha channel.  Sharp edges may ring or fuzz out. png Portable Network Graphics RGBA Lossless Good compression, can represent alpha channel properly. libpng is big, and not present by default on Windows machines. bmp Windows Bitmap RGB None (usually) Builtin editors on Windows.  Simple format.  Also known as "pcx", which is the same format.  Bottom-up. Files are big.  No alpha channel. tga TARGA RGBA None (usually; occasionally simple runlength encoding) Simple format--easy to read.  See C++ ltga library and docs.   Bottom-up (usually). Not completely standardized.  For example, some targas store un-premultiplied alpha (normal R, G, B, A); others store premultiplied alpha (RA,GA,BA,A).  Files are big. ppm Portable Pixel Map RGB None Very simple ASCII header followed by binary data. ASCII header can include comments.  Files are big.  No alpha channel.

Any decent image editing program, like the GIMP, can read or write all these formats, including the alpha channel.  Usually, you just convert your textures to whatever your program supports.

Once read in, you pass in the texture's pixel data with glTexImage2d or gluBuild2DMipmaps (you need the latter call if you want to use "mipmaps", described below).

Here's a typical call:
`	gluBuild2DMipmaps(GL_TEXTURE_2D,		GL_RGBA8, /* internal format (on graphics card) */		wid,ht, /* size of passed-in image, in pixels */		GL_RGB, /* format of passed-in data */		GL_UNSIGNED_BYTE, /* data type in our pixel_data array */		&pixel_data[0]);`
This call sets the current OpenGL texture to the given image data.

In the fixed-function (non-GLSL) pipeline, you now just have to glEnable(GL_TEXTURE_2D), and draw some vertices with "texture coordinates" (they run from 0 to 1 along the X and Y axes):
`	glEnable(GL_TEXTURE_2D); 	glColor4f(1,1,1,1);	glBegin (GL_TRIANGLE_FAN); 	glTexCoord2f(0,0); glVertex2d(0,0); 	glTexCoord2f(1,0); glVertex2d(1,0); 	glTexCoord2f(1,1); glVertex2d(1,1); 	glTexCoord2f(0,1); glVertex2d(0,1); 	glEnd();`
With GLSL, you can have several texture "units" active at once.  By default, texture unit 0 is active:
`(C++)	glActiveTexture(GL_TEXTURE0);`
You declare a texture in GLSL as an ordinary "uniform" variable, of the special type "sampler2D":
`(GLSL)	uniform sampler2D myTex;`
From C++, you set this uniform to the integer texture unit number (by default, 0):
`(C++)	glUniform1iARB(glGetUniformLocationARB(prog,"myTex"),0);`
Now in GLSL, you can call "texture2D" on the sampler to read texture colors from a given texture coordinate:
`(GLSL)	vec4 c = texture2D(myTex, vec2(worldCoords.x,worldCoords.y));`
Recall that in GLSL, you are not limited to simple fixed texture coordinates!  By shifting texture coordinates, you can get a cool reflective effect (environment mapping), simulate height on a single quad's surface (parallax mapping), or even look up arbitrary values by treating a texture as a look-up table.

Of course, you can also do anything you like with the texture color!  I've seen textures containing normals (a "normal map", useful for bump mapping), textures containing specularity ("specularity map"), textures that index into other textures (indirection map, or look-up table), and so on.

## Texture Filtering

You can change how colors interpolate between pixels in the texture map when blowing up the texture map onscreen ("magnification" mode):
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
Return the color of the nearest pixel.  Results in ugly boxy shapes, but it's really fast, and never blurry.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
Take a bilinear blend of neighboring pixels to create output values.  Gives nice smooth output, but may be strange around the outside edge without GL_CLAMP_TO_BORDER or GL_REPEAT.
The same options exist when shrinking a texture map down onscreen ("minification" mode), along with extra "mipmap" options:
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
Linearly interpolate pixels in the mipmaps, and then linearly interpolate between mipmaps.  The smoothest version by far, and the recommended one to avoid ugly aliasing.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
Linearly interpolate pixels in the mipmaps, but only use the closest mipmap level.  Slightly faster than linear-linear on older hardware.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST);
Use nearest-neighbor pixels in the mipmaps, and only use the closest mipmap level.  The ugliest approach, but good for checking for mipmap problems.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_LINEAR);
Use nearest-neighbor pixels in the mipmaps, but blend between mipmap levels.  Rarely useful.
You definitely have to just try these things out.  Fire up the example 'texture' code, crank up the scale factor to put a zillion copies of the texture onscreen, and tilt and rotate.

## Texture Coordinate Wrapping

You can change what happens outside normal texture coordinate bounds, on both the S (x axis) and T (y axis) texture coordinate axes.  With programmable shaders, this isn't as important as with fixed-function.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
Works like fract(texCoords).  Texture coordinates wrap back around to 0.0 when they exceed 1.0, which causes the texture to repeat.  This is the most common wrap mode.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
Works like clamp(texCoords,0.0,1.0).  Texture coordinates outside the normal range are flattened to 0.0-1.0.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
Works like clamp(texCoords,0.0+0.5/pixelSize,1.0-0.5/pixelSize), where "pixelSize" is the corresponding dimension of the texture in *texture* pixels.  This has the effect of extending out the edge pixels.  With GL_NEAREST filtering, it's identical to GL_CLAMP, but it's different for GL_LINEAR.
• glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
Works like clamp(texCoords,0.0-0.5/pixelSize,1.0+0.5/pixelSize).  The edge pixels linearly blend down to the "border color" (usually a transparent black), which then extends outward.   This is probably what GL_CLAMP should have been.