Since the human visual system can only discriminate depth to about 200 yards, objects beyond that distance appear flat. Thus many times in graphics applications we would like to be able to use an image as the background for the window, e.g. a distant outdoor scene, which is known as billboarding. Unfortunately OpenGL (and even GLUT) does not provide any mechanism for loading images (which is also a problem for using textures). Thus we will use a third party library known as SOIL (Simple OpenGL Image Library) to load images from files (see Resources for installation instructions). OpenGL can then simply draw the pixels of the image directly into the framebuffer.

0. Getting Started

Download CS370_Lab18.zip, saving it into the labs directory.

Double-click on CS370_Lab18.zip and extract the contents of the archive into a subdirectory called CS370_Lab18

Navigate into the CS370_Lab18 directory and double-click on CS370_Lab18.sln (the file with the little Visual Studio icon with the 12 on it).

If the source file is not already open in the main window, open the source file by expanding the Source Files item in the Solution Explorer window and double-clicking FujiSan.cpp.

1. Loading Images

In order to import an image using SOIL, which will be stored in an array of unsigned char, we can use the command:

image = SOIL_load_image(filename, &width, &height, &channels, force_channels);

where image is a pointer to an array of unsigned char, filename is a string containing the name of the image file, width and height are int’s that are returned by the function containing the respective sizes of the image, channels is an int that is returned by the function that contains the number of color channels for the image, and force_channels is a flag telling SOIL the color type of the image being loaded (usually set to SOIL_LOAD_AUTO).

Tasks

2. Drawing Images

Once the image is loaded, we can draw it on the framebuffer using:

glDrawPixels(width,height,format,type,*image);

where width and height are the size of the image, format is the color format of the image pixels (usually GL_RGB), type is the data type (usually GL_UNSIGNED_BYTE), and image is a pointer to the image array. NOTE: The load function will properly store the data for rendering.

Two important things that need to be considered when rendering an image are blending and hidden surface removal. Since we typically do not want transparency when rendering the background image, we need to disable blending (via glDisable(GL_BLEND); and glEnable(GL_BLEND); around the background drawing code. Also since we usually want our image to be a background, we need to make the depth buffer read-only (similarly to transparency using glDepthMask(GL_FALSE); and glDepthMask(GL_TRUE); around the background drawing code), otherwise the image will set a depth z=0 for all the pixels thereby clipping any objects with negative z values.

Most of the time we will want to draw the background before we render any objects. Therefore we need to set a 2D orthographic projection using gluOrtho2D( ) and an identity modelview matrix for the background and then reset whatever 3D projection mode we are using (similar to when we wished to switch projection modes). One other major issue with many drawing formats (including .bmp and .jpg) is that the pixels are stored from top to bottom yet OpenGL renders them from bottom to top (remember OpenGL world-coordinates). Hence many images will be upside-down when displayed directly from the file. A simple option to correct this is to use an image utility program (such as Paint or Gimp) to create a new vertically mirrored version of the image. A better way is to do it programmatically with the function:

glPixelZoom(sx,sy);

where sx and sy are scale factors (similar to scaling transformations). By setting sx=1 and sy=-1, the image will be inverted in the vertical direction. Unfortunately as with normal vertex scaling, the problem with this technique is that the image will then be displayed below the screen (at negative y values). Hence we need to adjust where the image is displayed using the raster position (similar to fonts).

Finally, since typically it would be nice to scale the extents of the window to match the background image size, we can load the image before creating the window (in main( )) and then use the width and height returned by SOIL to create the window (unfortunately if the user drags a corner to resize the window, it will take additional code in the resize callback to keep the image filling the background).

Tasks

Compiling and running the program

Once you have completed typing in the code, you can build and run the program in one of two ways:

(On Linux/OSX: In a terminal window, navigate to the directory containing the source file and simply type make. To run the program type ./FujiSan.exe)

The output should look similar to below

image

To quit the program simply close the window.

Now that we can load images, the next effect that will greatly enhance our scenes is to apply these images to objects using what in known as texture mapping. Essentially we will attach the image onto the geometry by associating points in the image with vertices on the object.