Images & Textures






Image Files

Image data is often saved in or loaded from a file

There are many common image formats: JPEG, TIFF, PNG, GIF, etc

Features which may vary between formats are:






PIL

Provides support for reading, writing, and manipulating images in several formats.

PIL website: www.pythonware.com/products/pil/
Handbook: www.pythonware.com/library/pil/handbook/


PIL is installed on all the PCs in rooms 242 and 265

The PIL installer for Windows can be grabbed from my Python installation page.




PIL Example

>>> import Image
>>> img = Image.open("foo.jpg")
>>> img.size
(512, 512)
>>> img.mode
'RGB'
>>> newimg = img.resize((32,32))
>>> newimg.save("bar.tif")

Note: don't do "from Image import *", because that will replace the normal open() function with the Image package's open().






PIL Functions

open

img = Image.open('myimage.tif')

new

img = Image.new('RGB', (640,480), (255, 255, 255))

save

img.save('newimage.jpg')






PIL Functions

resize

img2 = img1.resize( (256, 256), Image.ANTIALIAS )

transpose

img2 = img1.transpose( Image.FLIP_TOP_BOTTOM )

rotate

img2 = img1.rotate(30)

convert

img2 = img1.convert( 'L' )






PIL Functions

tostring

imgdata = img.tostring()

fromstring

img.fromstring(imgdata)
    or
img = Image.fromstring('RGB', (640, 480), imgdata)

getpixel

rgb = img.getpixel( (10, 31) )

putpixel

img.putpixel( (10, 31), (255, 255, 0) )






OpenGL Images

Images are drawn with glDrawPixels

glDrawPixels(width, height, format, type, pixels)

width & height are the size of the image, in pixels.

format is GL_RGB for RGB images, or GL_LUMINANCE for greyscale (there are other formats as well).

type indicates the data type of the pixel data. GL_UNSIGNED_BYTE is standard for 8-bit-per-channel images.

pixels is the actual array of image data. Use the tostring() function with PIL images.

    img = Image.open("foo.jpg")
    glDrawPixels(img.size[0], img.size[1], GL_RGB,
                 GL_UNSIGNED_BYTE, img.tostring())





OpenGL Images

The position to draw an image at is given with glRasterPos

glRasterPos2f(x, y)
or
glRasterPos3f(x, y, z)

Example: drawimage.py






glReadPixels

glReadPixels reads from the frame buffer back into memory.
glReadPixels(x, y, width, height, format, type)

arguments are similar to glDrawPixels, except x & y give the starting location (in pixels from the lower left corner)
In C, there is one additional argument - a memory buffer pointer.

To grab a frame and save it, using PIL:

data = glReadPixels(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT), GL_RGB, GL_UNSIGNED_BYTE)
img = Image.fromstring('RGB', (glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)), data)
img.save('screendump.jpg')





Texturing






Textures vs. Images

Drawing an image (glDrawPixels)
Applies transformations to the glRasterPos to find the starting point
After that, skips the 3D rendering pipeline and just copies pixels directly from the image to the framebuffer

Texturing
Is a stage in the 3D rendering pipeline, for polygon data
Determines the color of each pixel when filling in a polygon





Texturing

Applying textures in OpenGL involves the following steps:

Enable texturing
glEnable(GL_TEXTURE_2D)
 
Define a texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
             0, GL_RGB, GL_UNSIGNED_BYTE, imageData)
 
Assign texture coordinates
glTexCoord2f(0,0)
glVertex3f(-1.0, -1.0, 0.0)
glTexCoord2f(1,0)
glVertex3f(1.0, -1.0, 0.0)





Named Textures

Defining a texture transfers the image data to the graphics card

Named textures let you do this once, and then use the texture again later by referring to its ID (its "name").

textureID1 = glGenTextures(1)

glBindTexture(GL_TEXTURE_2D, textureID1)
glTexImage2D(...)
glBindTexture(GL_TEXTURE_2D, 0)

...
glBindTexture(GL_TEXTURE_2D, textureID1)
# Draw textured object





glTexImage2D

glTexImage2D(target, level, internalFormat, width,
             height, border, format, type, pixels)

Underlined arguments are equivalent to those for glDrawPixels

target - GL_TEXTURE_2D
level - used in mipmapping; set to 0
internalFormat - usually the same as format; can be used for optimization
border - normally 0; set to 1 when image data includes a border

Important: The width & height of a texture image must be powers of 2 - i.e., 2, 4, 8, 16, 32, 64, 128, 256, 512, etc.
(except when a border is used - then it's 2 plus a power of 2)






Texture Coordinates

Objects need texture coordinates to define how a texture is applied

Texture coordinates form a coordinate system on the texture image, ranging from 0 to 1 in each direction

They are referred to as S and T
(to distinguish them from X & Y geometric coordinates)






Texture Coordinates

One texture coordinate should be assigned for each vertex

This defines what part of the texture should be used at that point

When a polygon is filled in, the vertices' texture coordinates are interpolated across its surface, to find the texture data for each pixel






Texture Coordinates

A texture does not have to be evenly applied to a polygon.

The image can appear distorted, either as a result of the geometry or the texture coordinates used.






Texture Tiling

Texture coordinates outside the range 0 - 1 can be used to tile a texture






Texture Tiling

Tiling is controlled by the WRAP texture parameter:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                   GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                   GL_REPEAT)

for no tiling:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                   GL_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                   GL_CLAMP)





Texture Environment

Textures provide color information, but so does glColor or lighting.

The texture environment controls how these colors interact.

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode)

A "GL_REPLACE" environment uses just the texture color, and ignores everything else.
    Color = Ct

A "GL_MODULATE" environment combines the texture and other color, multiplying them together.
    Color = Ct * Cf

Example: texenv.py






Texture Environment

GL_MODULATE mode is used when objects are both textured and lighted

Example: texlight.py






Filtering

Applying a texture involves sampling - for each pixel drawn on the screen, OpenGL must compute a color from the texture image.

Texels (texture pixels) rarely match up exactly 1-to-1 with the pixels on the screen .

Filtering modes select what to do when texels are larger than screen pixels (magnification), or when texels are smaller than screen pixels (minification).

    





Filtering

glTexParameteri(GL_TEXTURE_2D, filter, mode)

If filter is GL_TEX_MAG_FILTER, mode can be

If filter is GL_TEX_MIN_FILTER, mode can be

Example: texfilter.py






Mipmapping

Mipmapping uses versions of the texture at smaller & smaller sizes image, to effectively pre-compute the average of many texels.

As a texture gets more and more minified, many texels will correspond to a single screen pixel, and using a "nearest" or "linear" filter will not compute an accurate result. The texture will appear to flicker as the object moves.


A mipmapped texture avoids the flickering problem when minified. As a result, it can look a bit fuzzier.






Mipmapping

gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB,
                  width, height, GL_RGB,
                  GL_UNSIGNED_BYTE, pixels)

Use gluBuild2DMipmaps in place of glTexImage2D.
It will automatically generate all the smaller versions of the texture.


Example: mipmap.py



Creative Commons License
This document is by Dave Pape, and is released under a Creative Commons License.