Lazy Foo' Productions


Loading a Texture

Loading a Texture screenshot

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:
  1. Your compiler can find the header files
  2. Your compiler can find the library files
  3. You tell the linker to link against the library. In this case we need to link against DevIL and ilu
  4. 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.
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.
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.
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.
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.