Loading an Image File and Creating a Texture
OpenGL, unlike Direct3D, is independent of the operating system. While this eases the portability among different OS, it implies that we have to resort to external libraries for a number of tasks, loading images being one of the most used.
In this short tutorial we will use DevIL, an external image loader, to load an image from a file, and use this data to create an OpenGL texture. While it is true that DevIL has not been updated for some time now, this library really keeps it simple and is able to load a large number of image formats. Furthermore, it works in MacOS, Linux and Windows.
DevIL, formerly know as OpenIL, stands for Developers Image Library. DevIL and OpenGL work in a similar fashion. First we need to generate an image, then bind it, load it, and finally access its data. For the OpenGL side we have to generate a texture, bind it, specify its parameters and send the image data to the driver.
Visual Studio 2010 project with full source code and shaders: Simple Texture. DevIL installation is required.
The following snippet of code shows how to load an image with DevIL:
ILboolean success; unsigned int imageID; // init DevIL. This needs to be done only once per application ilInit(); // generate an image name ilGenImages(1, &imageID); // bind it ilBindImage(imageID); // match image origin to OpenGL’s ilEnable(IL_ORIGIN_SET); ilOriginFunc(IL_ORIGIN_LOWER_LEFT); // load the image success = ilLoadImage((ILstring)filename.c_str()); // check to see if everything went OK if (!success) { ilDeleteImages(1, &imageID); return 0; }
Notice that the image origin is set to be the lower left corner. Image formats can use the upper left or bottom left corner as the origin of the pixel coordinate system. Commonly the texture coordinates assume an origin at the bottom left and the above statements guarantee that the loaded image has a matching coordinate system.
Once the image is loaded, in order to create a texture, the image data must be accessed. Not only the pixels themselves but width, height, number of channels, data format, etc…
Getting information for a loaded image can be accomplished with function ilGetInteger
as shown in the next code sample.
printf("Width: %d, Height %d, Bytes per Pixel %d", ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL)); std::string s; switch(ilGetInteger(IL_IMAGE_FORMAT)) { case IL_COLOR_INDEX : s = "IL_COLOR_INDEX"; break; case IL_ALPHA : s = "IL_ALPHA"; break; case IL_RGB : s = "IL_RGB"; break; case IL_RGBA : s = "IL_RGBA"; break; case IL_BGR : s = "IL_BGR"; break; case IL_BGRA : s = "IL_BGRA"; break; case IL_LUMINANCE : s = "IL_LUMINANCE"; break; case IL_LUMINANCE_ALPHA : s = "IL_LUMINANCE_ALPHA"; break; } printf(" Format %s", s.c_str()); switch(ilGetInteger(IL_IMAGE_TYPE)) { case IL_BYTE : s = "IL_BYTE"; break; case IL_UNSIGNED_BYTE : s = "IL_UNSIGNED_BYTE"; break; case IL_SHORT : s = "IL_SHORT"; break; case IL_UNSIGNED_SHORT : s = "IL_UNSIGNED_SHORT"; break; case IL_INT : s = "IL_INT"; break; case IL_UNSIGNED_INT : s = "IL_UNSIGNED_INT"; break; case IL_FLOAT : s = "IL_FLOAT"; break; case IL_DOUBLE : s = "IL_DOUBLE"; break; case IL_HALF : s = "IL_HALF"; break; } printf(" Data type: %s", s.c_str());
As seen on the listing above images come in a large variety of combinations of types and formats.
A routine could be written to convert the IL constants to GL constants, however in some cases a simpler approach can be taken.
If we’re loading an image and we want to use it as a color texture, then we can take the lazy approach (for us, not the computer) and just ask DevIL to convert the image to a format that suits our application.
The following listing shows how to convert the bound image to an RGBA format with unsigned int data type.
/* Convert image to RGBA with unsigned byte data type */ ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
If we call ilConvertImage
before glTexImage* then we don’t need to worry about the original image data format and type.
/* Convert image to RGBA with unsigned byte data type */ ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); /* Create and load textures to OpenGL */ glGenTextures(1, &textureID); /* Texture name generation */ glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, type, ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0, GL_RGBA, GL_UNSIGNED_BYTE, ilGetData());