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:
Typical simple image formats:
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.

Lossy (DCT)
Amazingly tight compression, especially for photos.
libjpeg is big, and no alpha channel.  Sharp edges may ring or fuzz out.
Portable Network Graphics
Good compression, can represent alpha channel properly.
libpng is big, and not present by default on Windows machines.
Windows Bitmap
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.
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.
Portable Pixel Map
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.

Uploading Texture Data to OpenGL

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:
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 */
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):
glTexCoord2f(0,0); glVertex2d(0,0);
glTexCoord2f(1,0); glVertex2d(1,0);
glTexCoord2f(1,1); glVertex2d(1,1);
glTexCoord2f(0,1); glVertex2d(0,1);
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):
The same options exist when shrinking a texture map down onscreen ("minification" mode), along with extra "mipmap" options:
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.