Loading a Texture
Last Updated: Oct 19th, 2014
Pixels are pixels whether you get them from a GLuint array or in this case a PNG. With the help of the Developers Image Library AKA DevIL, we're going to get pixel data from a file and display it on the screen.Setting up DevIL
As I mentioned before, OpenGL has no file loading capabilities. In this tutorial we'll use DevIL as our image file loader which you can download here. Windows users will want to download the Windows SDK (again, I'm assuming we're using 32bit binaries for this tutorial). Linux users you can use your package manager or install from source. OS X users you can also install DevIL the classic unix way of downloading the source and running the 3 commands in the source directory of./configure
-> make
-> sudo make install
.As with freeGLUT you need to make sure:
- Your compiler can find the header files
- Your compiler can find the library files
- You tell the linker to link against the library. In this case we need to link against DevIL and ilu
- The library binaries are in a place where the OS can find them
Since we're going to be loading files in this tutorial, it's important that the folder containing the media for this tutorial is in a place your exectuable can find it. Usually, the exe looks for files in the same directory it runs in. If you're using Visual Studio and you run your executable from the IDE, it will look for files in the same directory as your vcxproj file.
From LTexture.cpp
#include "LTexture.h" #include <IL/il.h>
At the top of LTexture.cpp, we include il.h. DevIL was originally known as OpenIL, and the source files still follow the old naming convention.
From LTexture.cpp
bool LTexture::loadTextureFromFile( std::string path ) { //Texture loading success bool textureLoaded = false; //Generate and set current image ID ILuint imgID = 0; ilGenImages( 1, &imgID ); ilBindImage( imgID );
Here's our new function to load a texture from a file, which takes in the path for the file. At the top of our image loading function, we initialize our texture loading
flag.
The next few lines of code should look familiar. DevIL has a similar state machine design as OpenGL. We declare an integer ID, generate an DevIL image ID and bind it as the current image.
The next few lines of code should look familiar. DevIL has a similar state machine design as OpenGL. We declare an integer ID, generate an DevIL image ID and bind it as the current image.
From LTexture.cpp
//Load image ILboolean success = ilLoadImage( path.c_str() ); //Image loaded successfully if( success == IL_TRUE ) { //Convert image to RGBA success = ilConvertImage( IL_RGBA, IL_UNSIGNED_BYTE );
After we bind our DevIL image ID, we load the image using ilLoadImage(). If the image loaded successfully, we call ilConvertImage() on the current loaded image to make sure the
pixel data is in RGBA format.
Note: if you have Unicode enabled, this code is going to give you an error. ilConvertImage() will want wchar_t which are unicode characters. All you have to do is convert the std::string into a wstring, and then get the wchar_t array from the wstring. You can Google how to convert string into wstrings.
Note: if you have Unicode enabled, this code is going to give you an error. ilConvertImage() will want wchar_t which are unicode characters. All you have to do is convert the std::string into a wstring, and then get the wchar_t array from the wstring. You can Google how to convert string into wstrings.
From LTexture.cpp
if( success == IL_TRUE ) { //Create texture from file pixels textureLoaded = loadTextureFromPixels32( (GLuint*)ilGetData(), (GLuint)ilGetInteger( IL_IMAGE_WIDTH ), (GLuint)ilGetInteger( IL_IMAGE_HEIGHT ) ); } //Delete file from memory ilDeleteImages( 1, &imgID ); } //Report error if( !textureLoaded ) { printf( "Unable to load %s\n", path.c_str() ); } return textureLoaded; }
After the image pixels are converted, we simply pass in the pixel data to our loadTextureFromPixels32() function to generate the texture. The function ilGetData() gets the pixel
data from the current DevIL image, and we use ilGetInteger() to get the current DevIL image width/height.
With our texture image loaded into OpenGL memory, we delete it from DevIL memory using ilDeleteImages(). After that, we report any errors if needed and return our success flag.
With our texture image loaded into OpenGL memory, we delete it from DevIL memory using ilDeleteImages(). After that, we report any errors if needed and return our success flag.
From LUtil.cpp
#include "LUtil.h" #include <IL/il.h> #include <IL/ilu.h> #include "LTexture.h" //File loaded texture LTexture gLoadedTexture; bool initGL() { //Set the viewport glViewport( 0.f, 0.f, SCREEN_WIDTH, SCREEN_HEIGHT ); //Initialize Projection Matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, 1.0, -1.0 ); //Initialize Modelview Matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); //Initialize clear color glClearColor( 0.f, 0.f, 0.f, 1.f ); //Enable texturing glEnable( GL_TEXTURE_2D ); //Check for error GLenum error = glGetError(); if( error != GL_NO_ERROR ) { printf( "Error initializing OpenGL! %s\n", gluErrorString( error ) ); return false; } //Initialize DevIL ilInit(); ilClearColour( 255, 255, 255, 000 ); //Check for error ILenum ilError = ilGetError(); if( ilError != IL_NO_ERROR ) { printf( "Error initializing DevIL! %s\n", iluErrorString( ilError ) ); return false; } return true; }
At the top of LUtil.cpp, we include il.h and ilu.h since we're going to be using DevIL utilities.
In initGL() after we initialize OpenGL, we call ilInit() to initialize DevIL. Then ilClearColour() is called to set the DevIL clear color to transparent white. DevIL actually has its own internal rendering fuctions which we'll be using in future tutorials.
After initializing DevIL, we check for errors and return.
In initGL() after we initialize OpenGL, we call ilInit() to initialize DevIL. Then ilClearColour() is called to set the DevIL clear color to transparent white. DevIL actually has its own internal rendering fuctions which we'll be using in future tutorials.
After initializing DevIL, we check for errors and return.
From LUtil.cpp
bool loadMedia() { //Load texture if( !gLoadedTexture.loadTextureFromFile( "06_loading_a_texture/texture.png" ) ) { printf( "Unable to load file texture!\n" ); return false; } return true; }
In our loadMedia() function, we call loadTextureFromFile() to load our PNG file. Make sure that when you run this program that the "06_loading_a_texture" folder containing
"texture.png" is in the right place.
From LUtil.cpp
void render() { //Clear color buffer glClear( GL_COLOR_BUFFER_BIT ); //Calculate centered offsets GLfloat x = ( SCREEN_WIDTH - gLoadedTexture.textureWidth() ) / 2.f; GLfloat y = ( SCREEN_HEIGHT - gLoadedTexture.textureHeight() ) / 2.f; //Render texture gLoadedTexture.render( x, y ); //Update screen glutSwapBuffers(); }
Finally, in our render() function we render our PNG file the same way we rendered our texture we made from memory in the last tutorial.