About this Blog

This is my first blog. Ever.

It is simply going to be about my hobby; playing with computer programming. I do not know much about blogging, but I will use this one to learn a bit more about it.

Programming has always been a bit of a passion for me, as from those early days when I first tapped in a sample BASIC program on my old Sinclair Spectrum back in 1986. I have been through many platforms, languages and OS's since, but always carried the hobby with me. I am not particularly good at it; perfection requires a large time investment and continuous practice. I do not have the luxury of the amount of time required to keep the fire burning constantly, so the hobby has inevitably gone through periods of extreme withering. I have, however, finally settled for C++, as the title of this blog implies, and play around with it for some entertainment when ever I can.

This here will serve me as a written record of what I am up to, and hopefully be a reinforcement to my memory every now and then. That is all there is to it.

So, if you read this blog, please don't expect anything snazzy, but be you welcome just the same!

Thursday 1 December 2011

Open GL / Allegro 5.1 Basics: Textures

So, after a year, I decided to tackle OpenGL again to find that I have forgotten almost everything about it. Previously, I had used glut and explored the possibility, with some limited success, of using Allegro 4's OpenGL functionality. However, now there is a new version of Allegro; 5.1.0. These posts are going to be about using OpenGL with that particular programming API, on Linux (Debian Wheezy), with the Code::Blocks IDE.

Here are some links...

Video Tutorials Rock
Allegro Tutorial
OpenGL Reference (2.1)
OpenGL Reference (1.1)
Code::Blocks
Allegro.cc (find out more about Allegro API here...)

I believe the support for the version of OpenGL on my Linux (libgl1-mesa-dev and libglu-mesa-dev) is for version 2.1, complete. In any case, here's the pertinent output of my versions from glxinfo run in a Linux terminal (that goes with the binary NVIDIA-Linux driver for my on board GPU)....
direct rendering: Yes
client glx vendor string: NVIDIA Corporation
client glx version string: 1.4
...
OpenGL vendor string: NVIDIA Corporation
OpenGL renderer string: GeForce 7025 / nForce 630a/PCI/SSE2
OpenGL version string: 2.1.2 NVIDIA 290.10
OpenGL shading language version string: 1.20 NVIDIA via Cg compiler

With all that in mind, here we go...

The objective here is simply render a texture on a GL_QUAD-rilateral. But first it will be necessary to initialize Allegro, and make it acceptable for working with OpenGL. This is relatively easy...

(Note: The following blocks of code are completely contiguous. They can be pasted together in the order that they appear to create the complete, working listing, unless a particular block excludes itself specifically with a CAPITALIZED comment saying //DEMO EXAMPLE. Such commented blocks need not or should not be placed in the listing).

#include "allegro5/allegro.h"
#include "allegro5/allegro_opengl.h"
#include "allegro5/allegro_image.h"
#include "GL/glu.h"


int main()
{
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_BITMAP *bmp = NULL;

GLuint ogl_tex; // The OpenGL texture id.

al_init();
al_init_image_addon();

al_set_new_display_flags(ALLEGRO_OPENGL);
display = al_create_display(1200, 800);

// Load a bitmap to use as a texture
bmp = al_load_bitmap("texture.jpg"); // 256 x 256 pixels
ogl_tex = al_get_opengl_texture(bmp);

...and here is the "texture.jpg" I am using, by the way. It is some screen background I got off the web and "Gimped" down to 256 x 256.
 

So, what's important here? Several things, straight off the bat, which are going to be useful, if not indispensable, for using OpenGL with Allegro 5.1.0. In the include headers lines is "allegro_opengl.h", required to use OpenGL with the API. Also, there are the "allegro_image.h" and "glu.h" headers. The image addon is initiated in the code with the line al_init_image_addon(), and will be needed for handling the loading of the jpg image that will be used as the texture. The glu.h is needed for for using the gluPerspective() function, which will provide the camera for my OpenGL world.

The respective libraries are linked the following way, for Allegro-5.1.0;
`pkg-config --libs allegro-5.1 allegro_image-5.1`
-lGL
-lGLU
Now, before creating the ALLEGRO_DISPLAY, the call to al_set_new_display_flags(ALLEGRO_OPENGL) is, apparently, required. That said, I have tried running OpenGL through Allegro-5.1.0 excluding this call, and it works perfectly well and without any notable difference, which is strange(?).

Finally, that little block of code loads texture.jpg as an ALLEGRO_BITMAP with al_load_bitmap(), and then assigns that bitmap as a (type) GLuint texture id (ogl_tex) with al_get_opengl_texture(). It is now usable as an OpenGL texture for later on.

Onwards...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, 1200.0 / 800.0, 1.0, 400.0);

This is the camera view on the OpenGL world.

glMatrixMode(GL_PROJECTION) puts OpenGL in the mode that allows the definition of the view camera. glLoadIdentity() then pr oceeds to enable, or execute that mode, replacing any glMatrixMode that may have been in use before. Finally, gluPerspective() sets up the qualities of the camera. In order, the first parameter sets the angle, in degrees that the camera sees. With it, you can simulate a telephoto lens (with low FOV angles), or fish eye lenses (with wide FOV angles). The second parameter is the aspect ratio of the view port, or display. It is obtained by dividing the display width by the display height, (1200 / 800) in this case. The last two parameters are the closest render distance and farthest render distance, respectively, in OpenGL units. Anything closer than 1.0 or farther than 400.0 units from the Camera will not be rendered. Yeah, okay. I understand all the above better worded that way! And that's the camera done.

The next bit of code builds the object (a flat board!) that we are going to see, and applies the texture we prepared earlier...
al_clear_to_color(al_map_rgb(0,0,50));

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef(2.0f, -1.5f, -18.0f);
glRotatef(75.0, -1.0f, 0.0f, 0.0f);
glRotatef(10.0, 0.0f, -1.0f, 0.0f);
glRotatef(0.0, 0.0f, 0.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ogl_tex);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

// Other options for GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T...
// GL_CLAMP, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT

glBegin(GL_QUADS);

glTexCoord2f(0.0, 0.0); // Bottom left hand corner
glVertex3f(-3.5, -6.5, 0.0); // X,Y,Z
glTexCoord2f(6.0, 0.0); // Bottom right hand corner
glVertex3f(3.5, -6.5, 0.0); // X,Y,Z
glTexCoord2f(6.0, 8.0); // Top right hand corner
glVertex3f(3.5, 6.5, 0.0); // X,Y,Z
glTexCoord2f(0.0, 8.0); // Top left hand corner
glVertex3f(-3.5, 6.5, 0.0); // X,Y,Z

glEnd();

glDisable(GL_TEXTURE_2D);

Okay, that looks quite long and confusing right now, but it is not the end of the world. I will dissect it...

al_clear_to_color() is self explanatory, really. The only reason I mention it is to show that it is perfectly compatible with OpenGL.

And again, we use glMatrixMode() and glLoadIdentity(), though this time we set the GL_MODELVIEW mode, as we are going to make a flat quadrilateral (that is, a GL_QUAD).

glTranslatef(X,Y,Z) needs a very quick review. It moves the "origin" (let's say) of our object to be created to a point in 3D space, defined by X,Y,Z, in OpenGL units. In this case, I moved the origin +2.0 X (to the right), -1.5 Y (down) and -18.0 Z (into the screen). Basically, it is a matrix translate, as the name implies.

glRotatef(angle, X,Y,Z) is a matrix rotate , around the origin. Here I break the rotation process up into three separate operations, one for each axis, with it's own angular change. To put it simply, positive rotation values rotate the object around it's origin counter-clockwise, as viewed parallel from the positive side of local object's local axis it is being rotated around.

We now come to the texture handling part. The first thing to do is to enable the texture with glEnable(GL_TEXTURE_2D). There's a hell of a lot of things that can be "enabled" with this function in OpenGL. Here, suffice to say that our texture(s) are enabled so that we can use them on our object, okay? The function has an opposite number, glDisable(), which is used to "disable" just about everything that can be enabled, as the program situations require, and is described on the same reference page as glEnable(). Note that at the end of the code block above, I disable GL_TEXTURE_2D, after I have used it.

Following that, I invoked glBindTexture(GL_TEXTURE_2D, ogl_tex). As "bind" suggests, this function "attaches" or "associates" (but does not specifically "place" or "position") the texture, referenced by its GLui nt identifier, to the object we are about to build. Enough said about that, really. Let's get onto setting that texture and making the object...

It's time to look at setting some parameters for that texture. For this, I use glTexParameterf() or glTexParameteri(). GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER basically define how the texture image is "compressed" or "spread" onto the object when the rendered siz e of the object is not equal to the true size of the texture bitmap. This will occur most of the time. Right here, I set both of them as GL_LINEAR, as I am not doing any mip-mapping just yet.

glTexParameter can also be used to set the "tiling" (wrapping) behavior characteristics of the texture on the object. GL_TEXTURE_WRAP_S determines how the texture will behave horizontally across the object, if it is repeated more than once, that is. More on how that is done in a minute. I want the texture to repeat itself mirroring itself every time it is rendered horizontally, so I use GL_MIRRORED_REPEAT.

GL_TEXTURE_WRAP_T determines how the texture will repeat vertically. Here I just want it to repeat and be a non-mirrored replica of itself, so I use GL_REPEAT.

In all cases above the target (the first parameter of glTexParameter) was my 2D texture, that is GL_TEXTURE_2D.

At last, I can get down to making my object. Fir st I will tell it what, exactly, I am planning to build, so that OpenGL has an idea of how many vertices will be involved. I emp loy glBegin(GL_QUADS) for this. A quadrilateral, with four vertices.

Now, I could just go ahead and make a QUAD with the following;
// DEMO EXAMPLE
glVertex3f(-3.5, -6.5, 0.0); // X,Y,Z bottom left
glVertex3f(3.5, -6.5, 0.0); // X,Y,Z bottom right
glVertex3f(3.5, 6.5, 0.0); // X,Y,Z top right
glVertex3f(-3.5, 6.5, 0.0); // X,Y,Z top left

...which is fairly self explanatory, but I will have wasted all that effort in setting up the texture.

I will "place", or "position" if you like, the texture on my QUAD by using glTexCoord2f(). Now that reference description I found a bit overly complicated for the present purposes. It would suffice here to say that these are more or less the same thing as u/v coords, as used in 3D modeling programs. In this case, take a look at the picture of the texture up above in this post. The lower left corner is (X,Y) position (0,0), the lower right (1,0), the top right (1,1) and the top left (0,1). The function glTexCoord2f() uses these coords to map the 2D texture onto the object, as many times as we want, abiding by the repeat properties set with the GL_TEXTURE_WRAP parameter. glTexCoord2f() is specified just before creating the respective vertex point with glVertex3f().

The following example would map the texture exactly onto the QUAD once;
// DEMO EXAMPLE
// Bottom left
glTexCoord2f(0.0, 0.0);
glVertex3f(-3.5, -6.5, 0.0);

// Bottom right
glTexCoord2f(1.0, 0.0);
glVertex3f(3.5, -6.5, 0.0);

// Top right
glTexCoord2f(1.0, 1.0);
glVertex3f(3.5, 6.5, 0.0);

// Top left
glTexCoord2f(0.0, 1.0);
glVertex3f(-3.5, 6.5, 0.0);
...and this would map it 2 times horizontally and once vertically...
// DEMO EXAMPLE
// Bottom left
glTexCoord2f(0.0, 0.0);
glVertex3f(-3.5, -6.5, 0.0);

// Bottom right
glTexCoord2f(2.0, 0.0);
glVertex3f(3.5, -6.5, 0.0);

// Top right
glTexCoord2f(2.0, 1.0);
glVertex3f(3.5, 6.5, 0.0);

// Top left
glTexCoord2f(0.0, 1.0);
glVertex3f(-3.5, 6.5, 0.0);
Once I have finished drawing my QUAD and placing the texture as I want it, I end the drawing with glEnd(), and disable work on the texture with glDisable(GL_TEXTURE_2D). Then it is just a case of flipping the buffer to the display, letting the image show for a few seconds, and cleaning up...
al_flip_display();
al_rest(10.0);

glDeleteTextures(1, &ogl_tex);
al_destroy_bitmap(bmp);
al_destroy_display(display);
return 0;
}

That would be it. You would get something like this...


More coming soon...

2 comments: