Image to GL Texture
The previous post, http://multimediaprogram.blogspot.com/2012/12/textures-in-opengl.html, discusses how to use a BMP image as a GL texture. A very similar process can be used for making textures from other image formats. The first step is still to select an image, and for this example, I will use the brick texture in its JPG format:
|
Next, the image should be loaded in as an SDL_Surface. Use the IMG_Load function from the SDL_image library to obtain an SDL_Surface with the image data. Also, since the saved data can vary among image formats, we can use the SDL_DisplayFormat function to create an SDL_Surface with the same format as the screen.
To make the GL texture, we can use the same OpenGL functions as last time. Here is an example of loading and displaying the brick texture:
While this is basically a copy-paste of the first BMP example, there are some important differences to note. As stated above, IMG_Load is now used in place of SDL_LoadBMP. Also, it is given the file path to a JPG image rather than a BMP image. It could also accept a file path to a PNG, TGA, TIFF, or other image file.
However, the produced SDL_Surface may store its data similar to how its stored in the file. To account for this, the example uses SDL_DisplayFormat to produce a second SDL_Surface with a predictable data storage. Since the screen has been set to 32-bit, the SDL_Surface will also have an additional, fourth color channel. This is usually the transparency and is referred to as alpha.
As a result, the function glTexImage2D is given the values GL_RGBA and GL_BGRA. For a non-transparent texture, like the one above, the alpha channel is not necessary, so GL_RGBA could be replaced with GL_RGB since this arguement tells OpenGL how to store the texture. The GL_BGRA is very much needed since the SDL_Surface still stores the unused, fourth channel.
This also works with BMP images. You can give IMG_Load the file path to a BMP image and it will still place that image on screen. Like BMP images, glTexImage2D is told that the image is stored with the color order blue, green, then red. I'm not sure why this order works, but changing GL_BGRA to GL_RGBA will swap the red and blue channels in the image (try it!).
Using Multiple Textures
The previous texture post had an example which drew shapes with and without texture. There is also a way to draw different shapes with different textures. In other words, we can load two textures and apply each to a different shape. For the following example, I will use the JPG brick texture, as well as this PNG:
|
Each loaded image can be converted to a GL texture and assigned an ID. Rather than disable GL_TEXTURE_2D, we can use the ID with the command glBindTexture in order to specify which texture we currently want. However, glBindTexture does not work inside a glBegin/glEnd block; so to draw two quads of different textures, we need two separate glBegin and glEnd calls. The following example should demonstrate this:
/*thanks to tohtml.com for syntax highlighting*/ #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <SDL/SDL_opengl.h> #include <stdio.h> SDL_Surface* screen; SDL_Event input; int loop = 1; /*GL texture ID's*/ GLuint brick; GLuint wood; /*function to load images into textures*/ GLuint loadTexture(char* filepath); int main(int argc, char** argv) { SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/ screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL); /*load images into textures*/ brick = loadTexture("texture.jpg"); wood = loadTexture("woodTexture.png"); /*set clear color to blue*/ glClearColor(0, 0, 0.5, 1.0); /*set origin to lower left, use 4:3 ratio*/ glMatrixMode(GL_PROJECTION); gluOrtho2D(0, 4, 0, 3); while (loop) { glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/ while (SDL_PollEvent(&input)) { if (input.type == SDL_QUIT) loop = 0; } /*set color to white*/ glColor3ub(255, 255, 255); /*set current texture to brick image*/ glBindTexture(GL_TEXTURE_2D, brick); /*draw quadrilateral*/ glBegin(GL_QUADS); /*left square*/ glTexCoord2i(0, 1); glVertex2f(0, 0); glTexCoord2i(0, 0); glVertex2f(0, 2); glTexCoord2i(1, 0); glVertex2f(2, 2); glTexCoord2i(1, 1); glVertex2f(2, 0); glEnd(); /*need to end to call glBindTexture*/ /*set current texture to wood image*/ glBindTexture(GL_TEXTURE_2D, wood); /*draw quadrilateral*/ glBegin(GL_QUADS); /*right square*/ glTexCoord2i(0, 1); glVertex2f(2, 1); glTexCoord2i(0, 0); glVertex2f(2, 3); glTexCoord2i(1, 0); glVertex2f(4, 3); glTexCoord2i(1, 1); glVertex2f(4, 1); glEnd(); SDL_GL_SwapBuffers(); SDL_Delay(20); /*wait 20ms*/ } /*remove texture-related stuff from memory*/ glDeleteTextures(1, &brick); glDeleteTextures(1, &wood); /*perform final commands then exit*/ SDL_Quit(); /*close SDL*/ fflush(stdout); /*update stdout*/ return 0; } /*function definition*/ GLuint loadTexture(char* filepath) { SDL_Surface *temp; SDL_Surface *image; GLuint output; /*try to load image, exit if there are problems*/ temp = IMG_Load(filepath); if (temp == NULL) { printf("Image failed to load\n"); SDL_Delay(3000); SDL_Quit(); /*shutdown SDL*/ fflush(stdout); /*update stdout*/ exit(0); /*force quit program*/ } /*convert loaded data into screen format*/ image = SDL_DisplayFormat(temp); SDL_FreeSurface(temp); /*done with temp*/ /*convert SDL_Surface into GL texture*/ glEnable(GL_TEXTURE_2D); /*enable 2D texturing*/ glGenTextures(1, &output); /*create a texture, store ID*/ glBindTexture(GL_TEXTURE_2D, output); /*set current texture*/ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, image->pixels); /*free image from memory since its a texture now*/ SDL_FreeSurface(image); /*return texture ID*/ return output; } | ||
prompt/stdout: | ||
output inside window:
|
For this example, a function called loadTexture performs all the necessary steps to convert a file path into a GL texture ID, which is returned. These are the same commands used in the previous example, with the exception of "exit(0);". Although the main function returns an integer to quit, exit allows the program to end from inside any function and return the input integer (0 in this case). This exit will end the program in the event that either texture cannot be loaded.
The GLuint brick is given the texture ID from the file path "texture.jpg" and GLuint wood is the result of "woodTexture.png" input to loadTexture. By using these ID's in the glBindTexture function, we select which loaded image we want placed on the upcoming shape. For this example, the left quad is drawn first with the brick texture, followed by the right quad with the wood texture.
One last thing to mention: rather than disabling GL_TEXTURE_2D for untextured shapes, you can also call "glBindTexture(GL_TEXTURE_2D, 0);". The value 0 simply means no texture in this case. Both this option and "glDisable(GL_TEXTURE_2D);" will draw later shapes without texture.
Thank you for reading, I hope this post was helpful. The next post will discuss OpenGL transformations and may be two parts.