Monday, December 31, 2012

Textures in OpenGL

    Aside from simple colors, OpenGL can also apply an image to simple shapes.  This can help approximate a real world surface like a textured wall or carpet, because otherwise we would need millions of small, colored surfaces.  Like the SDL post before, this will only use BMP images.

BMP Image to GL Texture
    We must start with a valid BMP image file in order for this procedure to work.  Any image of the BMP format will suffice, but for the following examples I shall use this image:
Brick Texture

    As a middle step, the image will be loaded as an SDL_Surface.  This procedure is similar to the one described in the SDL post; but to recap, we need a valid file path.  This image is called "texture.bmp" and the name alone can be used to load the image file.

    Finally, we can use a few OpenGL functions to translate the SDL_Surface into a working texture.  Once the texture is ready, it can be applied to a surface by giving each point a texture coordinate.  This is similar to coloring vertices, but will only require 2 inputs.

Putting Texture on a Square
    We can refer to which texture we want OpenGL to use with an integer value.  The function glBindTexture requires an enumeration and a GLuint as input.  The enumeration GL_TEXTURE_2D means a two dimensional texture recognized by OpenGL.  The GLuint is OpenGL's term for unsigned integer, similar to SDL's Uint32, and for this function refers to the ID of the texture we wish to use.  Similar to color, we can set the currently used texture in OpenGL.

    In order for OpenGL to recognize a two dimensional texture and allow us to use an integer to specify it, we must call glGenTextures.  This requires the number of textures to create and an array of integers to assign ID values to.  Since the examples will only use one texture, the input will be 1 followed by the address to store the integer, or texture ID, value.

    Also, in order to use the enumeration GL_TEXTURE_2D, we must first enable two dimensional texturing in OpenGL.  The function glEnable can allow us to use many features of OpenGL, but for 2D textures simply give it the argument GL_TEXTURE_2D.  The following example should show how to use these commands:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Surface* image; /*will store the loaded image*/
SDL_Event input;
int loop = 1;
GLuint texture; /*holds the texture ID*/

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*try to load image, exit if there are problems*/
    image = SDL_LoadBMP("texture.bmp");
    if (image == NULL) {
        printf("Image failed to load\n");
        SDL_Delay(3000);
        SDL_Quit(); /*shutdown SDL*/
        fflush(stdout); /*update stdout*/
        return 0;
    }

    /*print dimensions of loaded image*/
    printf("image dimensions: %d x %d\n", image->w, image->h);

    /*convert SDL_Surface into GL texture*/
    glEnable(GL_TEXTURE_2D); /*enable 2D texturing*/
    glGenTextures(1, &texture); /*create a texture, store ID*/
    glBindTexture(GL_TEXTURE_2D, texture); /*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_RGB, image->w, image->h, 0,
       GL_BGR, GL_UNSIGNED_BYTE, image->pixels);

    /*set clear color to purple*/
    glClearColor(0.25, 0.0, 0.75, 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;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*set color to white*/
       glColor3ub(255, 255, 255);

       /*square*/
       glTexCoord2i(0, 0); /*Texture Coordinate before vertex*/
       glVertex2f(1, 0.5);
       glTexCoord2i(0, 1);
       glVertex2f(1, 2.5);
       glTexCoord2i(1, 1);
       glVertex2f(3, 2.5);
       glTexCoord2i(1, 0);
       glVertex2f(3, 0.5);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*remove texture-related stuff from memory*/
    glDeleteTextures(1, &texture);
    SDL_FreeSurface(image);

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
image dimensions: 256 x 256
User has quit the program

output inside window:
Texture Applied to Quadrilateral

    The image loading and printing of dimensions was taken from the SDL BMP images post.  Converting the SDL_Surface to a GL texture consists of several OpenGL commands: glEnable will allow us to enable 2D textures, glGenTextures will create a texture an give us its ID (GLuint), glBindTexture will set the current texture, and the others.


    The function glTexParameteri can be used to change some aspect of a texture.  There are many options to change, but among them are magnification filtering (GL_MAG_FILTER) and "minification" filtering (GL_MIN_FILTER).  These control how the texture image will appear when expanded or contracted.  The last argument is what we wish to change our property to, in this case GL_LINEAR.  The reason these are set to linear filtering is because the default setting requires mipmaps, which would require many extra steps.

    Most of the actual work is done in the function glTexImage2D, which reads image data and applies it to the GL texture.  GL_TEXTURE_2D is the first argument since the function is being applied to the current texture.  GL_RGB refers to how we want the image store in OpenGL (with red, green, and blue channels).  GL_BGR and GL_UNSIGNED_BYTE refer to how the image is stored in the BMP file: 8-bit channels blue, green, and red respectively.

    This function also requires information about the loaded image, which is provided with image->w (width in pixels), image->h (height), and image->pixels (the loaded pixel data).  There are also parameters for level-of-detail and pixel borders, but these are set to 0 since they are not used in this example.

    Finally, each vertex is given a texture coordinate.  Like color, it is specified before vertices since each glTexCoord2i call will update OpenGL's current texture coordinate.  The coordinates can range from 0.0 to 1.0, which spans the width and height of the texture.  This example could use glTexCoord2f, but since it maps the entire image on four points, it only needs the integers 0 and 1.  

    The result is flipped vertically, but by inverting the second values (0 to 1 and 1 to 0), it can be made upright.  Also, the command glDeleteTexures requires the same arguments as glGenTextures in order to remove the GL texture from memory.  This is not always required though since OpenGL tends to manage its own memory usage.  It is included just to be sure though.

Filtering
    As mentioned above, we can set filter options for the current texture with the function glTexParameteri.  Previously, both magnification and minification were set to linear, but they can also use nearest-neighbor filtering.  Linear filtering tends to look the best, but certain situations may call for nearest-neighbor filtering.

    Since texture images can be scaled and rotated to any degree, there must be some way to account for when the texture image pixels don't align with the screen.  Imagine that the texture image will appear twice as big on the screen: this will require four times as many pixels to display the image as are present in the image.  The new pixels must be given a color value, so this is where filtering comes in handy.

    Linear filtering will take the four nearest pixels and use a weighted average of their colors for the new pixel.  Nearest-neighbor will take the nearest existing image pixel and simply use its color in the new pixel.  When the image is scaled above its normal dimensions, this is referred to as magnification while minification refers to a smaller version of the original.  For more information about OpenGL filtering, see http://gregs-blog.com/2008/01/17/opengl-texture-filter-parameters-explained/.

    Unfortunately, it appears that these options can only be set during the creation of the texture, so in order to see any changes, code may have to be rewritten.  The following example showcases nearest filtering for magnification, but feel free to change it to linear to see its effect as well.
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Surface* image; /*will store the loaded image*/
SDL_Event input;
int loop = 1;
GLuint texture; /*holds the texture ID*/

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*try to load image, exit if there are problems*/
    image = SDL_LoadBMP("texture.bmp");
    if (image == NULL) {
        printf("Image failed to load\n");
        SDL_Delay(3000);
        SDL_Quit(); /*shutdown SDL*/
        fflush(stdout); /*update stdout*/
        return 0;
    }

    /*print dimensions of loaded image*/
    printf("image dimensions: %d x %d\n", image->w, image->h);

    /*convert SDL_Surface into GL texture*/
    glEnable(GL_TEXTURE_2D); /*enable 2D texturing*/
    glGenTextures(1, &texture); /*create a texture, store ID*/
    glBindTexture(GL_TEXTURE_2D, texture); /*set current texture*/
    /*filtering options: try changing between LINEAR and NEAREST*/
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->w, image->h, 0,
       GL_BGR, GL_UNSIGNED_BYTE, image->pixels);

    /*set clear color to purple*/
    glClearColor(0.25, 0.0, 0.75, 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;
       }

       /*draw triangles*/
       glBegin(GL_TRIANGLES);

       /*set color to white*/
       glColor3ub(255, 255, 255);

       /*triangle*/
       glTexCoord2f(0, 0.2); /*Texture Coordinate before vertex*/
       glVertex2f(1, 0.5);
       glTexCoord2f(0.1, 0);
       glVertex2f(2, 2.5);
       glTexCoord2f(0.2, 0.2);
       glVertex2f(3, 0.5);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*remove texture-related stuff from memory*/
    glDeleteTextures(1, &texture);
    SDL_FreeSurface(image);

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
image dimensions: 256 x 256
User has quit the program

output inside window:
Nearest Filtering on a Triangle


    Shrinking the coordinates into the 0.0 to 0.2 range causes the resulting texture to only be part of the original image.  Since this small part is stretched over a normal-size triangle, we see magnification at work.  Also, the nearest filtering results in a pixelated texture.  If it were changed to linear filtering, it would appear smooth and blurry due to all the pixels with linearly interpolated colors.

    A triangle is used simply to show that texture can be applied to arbitrary shapes.  However, for the texture to display well, the texture coordinates must be changed in a way similar to the vertices.  For this example, two vertices were exchanged for one by averaging their positions, so the texture coordinates were averaged as well.

Wrapping
    For texture coordinates beyond the 0 to 1 range, we can tell OpenGL what to do.  This property is called texture wrapping and OpenGL can simply stop values beyond 0 to 1, or use them to repeat the texture.  Stopping the coordinates is called clamping while continuing the pattern is called repeating.

    Notice that the texture selected for this tutorial can be placed next to itself in order to form a larger pattern.  For most images, it would be obvious where one starts and the other begins.  An ideal texture will seamlessly blend in when placed next to itself.  For a collection of other seamless or tiled textures, see http://www.cgtextures.com/.

    To set the wrapping behavior for OpenGL, the function glTexParameteri will be used again.  This time, the properties will be GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, where S and T are the two axes used for 2D texture coordinates (similar to x, y, and z for vertices or r, g, and b for colors).  The following example should show clamp and repeat wrapping behaviors:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>

SDL_Surface* screen;
SDL_Surface* image; /*will store the loaded image*/
SDL_Event input;
int loop = 1;
GLuint texture; /*holds the texture ID*/

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*try to load image, exit if there are problems*/
    image = SDL_LoadBMP("texture.bmp");
    if (image == NULL) {
        printf("Image failed to load\n");
        SDL_Delay(3000);
        SDL_Quit(); /*shutdown SDL*/
        fflush(stdout); /*update stdout*/
        return 0;
    }

    /*print dimensions of loaded image*/
    printf("image dimensions: %d x %d\n", image->w, image->h);

    /*convert SDL_Surface into GL texture*/
    glEnable(GL_TEXTURE_2D); /*enable 2D texturing*/
    glGenTextures(1, &texture); /*create a texture, store ID*/
    glBindTexture(GL_TEXTURE_2D, texture); /*set current texture*/
    /*filtering options: try changing between LINEAR and NEAREST*/
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    /*wrapping options: try different combinations of repeat and clamp*/
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    /*defines texture image with SDL_Surface data*/
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->w, image->h, 0,
       GL_BGR, GL_UNSIGNED_BYTE, image->pixels);

    /*set clear color to purple*/
    glClearColor(0.25, 0.0, 0.75, 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;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*set color to purple*/
       glColor3f(1, 0, 1);

       /*start texturing*/
       glEnable(GL_TEXTURE_2D);

       /*textured square*/
       glTexCoord2f(0, 0); /*Texture Coordinate before vertex*/
       glVertex2f(0.5, 0.5);
       glTexCoord2f(0, 2);
       glVertex2f(0.5, 2.5);
       glTexCoord2f(2, 2);
       glVertex2f(2.5, 2.5);
       glTexCoord2f(2, 0);
       glVertex2f(2.5, 0.5);

       /*stop texturing*/
       glDisable(GL_TEXTURE_2D);

       /*untextured trapezoid*/
       glVertex2f(3, 0.5);
       glVertex2f(3, 2.5);
       glVertex2f(3.5, 2);
       glVertex2f(3.5, 1);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*remove texture-related stuff from memory*/
    glDeleteTextures(1, &texture);
    SDL_FreeSurface(image);

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
image dimensions: 256 x 256
User has quit the program

output inside window:
Texture Wrapping on a purple Quad


    Focusing on the left square, we see that the texture image appears twice on the left and some lines make up the right of the square.  The S axis is clamped, which means any coordinates beyond 1 simply become 1, also anything below 0 becomes 0.  The T axis repeats the texture, which is why we see the texture repeat in the Y direction.  The 0 to 2 range for the T coordinate means 2 texture images will appear.

    A few other features of this demonstration are the use of colors and non-textured shapes.  Notice that the quadrilaterals are colored purple and that the texture is affected by this color.  The color of the shape will multiply with the color of the texture, which works mathematically if we use the 0 to 1 range for colors.  Since white (1, 1, 1) is multiplied with purple (1, 0, 1), it becomes purple.

    The last thing to mention is that shapes can be drawn without texture by calling glDisable with the input GL_TEXTURE_2D.  Any shape drawn after this call will have no texture.  Since the program works in loops, we need to also call glEnable with GL_TEXTURE_2D before shapes with texture.

    I am sorry if this post seems rushed, but hopefully it helps clarify GL textures.  For other tutorials, check out http://www.swiftless.com/tutorials/opengl/texture_under_windows.html and http://www.videotutorialsrock.com/opengl_tutorial/textures/text.php.  For documentation about glTexImage2D, see http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml.  For information about glTexParameter, see http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml.

    Thank you for reading and happy new year!  The next posts may revisit SDL and possibly C, otherwise it may discuss OpenGL transformations.

P.S.  I recently discovered that while the image I have for "texture.bmp" is a BMP file, it apparently becomes a JPG when uploaded to a post.  I will try to fix this, but for now readers may want to use a valid BMP or convert the JPG to a BMP.  I apologize for this and will make a note on the SDL BMP image post as well.

Monday, December 24, 2012

OpenGL Colors and Shapes

    OpenGL specializes in drawing primitive shapes, especially triangles.  It can draw triangles of practically any shape and color and add a texture as needed.  OpenGL uses a process called rasterization, explained on the page http://www-users.mat.uni.torun.pl/~wrona/3d_tutor/tri_fillers.html.  Since OpenGL uses features of the graphics card, it does not require us to write code which draws triangles (which may not even run as fast as OpenGL).  This post will discuss some of these features.

Getting ready to draw
    With a working setup and OpenGL functions, we can begin to draw a triangle.  So far, the example from the previous post featured a program which runs and closes after 3 seconds.  In order to have programs which wait for the user to exit, we must make use of the variables SDL_Event input and int loop again.  Here is an example which still uses the new, OpenGL functions:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h> /*new header file*/
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*set clear color to green*/
    glClearColor(0.0, 0.5, 0.0, 1.0);

    while (loop) {
       glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/

       while (SDL_PollEvent(&input)) {
          if (input.type == SDL_QUIT) loop = 0;
       }

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
User has quit the program

output inside window:
A dark green background


    SDL_OPENGL is still the last argument for the SDL_SetVideoMode function to build the OpenGL window.  This example uses a dark green background by inputting a red and blue of 0.0 to glClearColor while setting green to 0.5, or half intensity.  Alpha is still set to 1.0, but I don't believe that changing its value will change the output.  Feel free to change the background color by changing the red, green, and blue arguments; my later examples will still use this dark green.

    Inside the while loop, glClear is called with the input GL_COLOR_BUFFER_BIT.  OpenGL will interpret this as coloring all visible pixels the color specified in glClearColor.  This is similar to drawing a screen-size rectangle in SDL.  The inner-while loop checks if the user clicks the exit button and will stop the outer loop if that occurs.  Finally, the buffer which was drawn onto is made visible and the other is ready to be drawn on by calling SDL_GL_SwapBuffers, and a delay is added for the processor's sake.

    After the loop, the printf function will output a message.  This may be too quick to be observed in some cases, but can be in the stdout file.  This setup will allow us to observe a program for as long as needed and exit.  Another feature which can be added is exiting the program when the escape key is pressed, but this is not required.

Shapes
    To draw a triangle, we need to provide OpenGL the coordinates of the triangle's endpoints.  However, unlike SDL, OpenGL does not have a set coordinate system: the right of the screen could be +1 while the left is -1.  This would require float values to place a point anywhere within the screen boundaries.  OpenGL does provide functions to help us define the coordinate space we want so that once we give it the endpoints of the triangle, it will know where to place them.

    The function gluOrtho2D will define the coordinate space and the function glVertex2i will place endpoints of a triangle.  The following example should draw a white triangle:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h> /*new header file*/
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*set clear color to green*/
    glClearColor(0.0, 0.5, 0.0, 1.0);

    /*tell OpenGL how to interpret coordinates*/
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0, 640, 480, 0);

    while (loop) {
       glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/

       while (SDL_PollEvent(&input)) {
          if (input.type == SDL_QUIT) loop = 0;
       }

       /*tell OpenGL to draw triangles and give it three points*/
       glBegin(GL_TRIANGLES);
       glVertex2i(320, 120);
       glVertex2i(480, 360);
       glVertex2i(160, 360);
       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
User has quit the program

output inside window:
A Triangle


    After setting the clear color, glMatrixMode is called and given the enumeration GL_PROJECTION as input.  OpenGL uses a set of matrices to determine where on-screen a point will be placed.  For this two-dimensional demo, setting the projection matrix should work fine.  The next command is gluOrtho2D which allows us to define what the left, right, bottom, and top of the screen are (in that order).  I chose a coordinate system similar to SDL's in this case.

    Inside the while loop are commands used to draw a triangle.  The function glBegin is given the argument GL_TRIANGLES, which means OpenGL is now ready to draw triangles.  This is followed by glVertex2i which allows us to give OpenGL a point of a triangle.  The 2i suffix means two integers will define this point.  The final command, glEnd, complements the glBegin call earlier and OpenGL stops accepting points to draw triangles.

    The triangle is drawn every frame after the screen is cleared and before the buffers are swapped.  The top vertex is drawn first, followed by the lower right, and finally the lower left.  Only three points are specified for a single triangle, but we may later need to draw several triangles and other shapes.  Here is a demonstration with more shapes:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h> /*new header file*/
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*set clear color to green*/
    glClearColor(0.0, 0.5, 0.0, 1.0);

    /*use a -1 to +1 coordinate system (lower-left origin)*/
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-1, 1, -1, 1);

    while (loop) {
       glClear(GL_COLOR_BUFFER_BIT); /*clear screen*/

       while (SDL_PollEvent(&input)) {
          if (input.type == SDL_QUIT) loop = 0;
       }

       /*draw triangles*/
       glBegin(GL_TRIANGLES);

       /*upper-left triangle*/
       glVertex2f(-0.5, 0.75);
       glVertex2f(-0.25, 0.25);
       glVertex2f(-0.75, 0.25);

       /*upper-right triangle*/
       glVertex2f(0.25, 0.75);
       glVertex2f(0.75, 0.5);
       glVertex2f(0.25, 0.25);
       glEnd();

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*lower-left rectangle*/
       glVertex2f(-0.75, -0.25);
       glVertex2f(-0.75, -0.75);
       glVertex2f(-0.25, -0.75);
       glVertex2f(-0.25, -0.25);
       glEnd();

       /*draw a single polygon*/
       glBegin(GL_POLYGON);

       /*lower-right pentagon*/
       glVertex2f(0.5, -0.3);
       glVertex2f(0.3, -0.5);
       glVertex2f(0.4, -0.7);
       glVertex2f(0.6, -0.7);
       glVertex2f(0.7, -0.5);
       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
User has quit the program

output inside window:
Various Shapes


    The first change made was the coordinate system: now left is -1 and right is +1.  Bottom is -1 and top is +1 also, so compared to SDL's coordinate system, this is flipped vertically.  In order to place points within these boundaries, glVertex2f is used since the 2f suffix allows two float values as input.

    To draw two triangles, we can simply provide six points between glBegin and glEnd.  We could also have two glBegin/glEnd blocks with three points each, but I believe this would be slower.  Aside from triangles, a "quad" or quadrilateral can be drawn by calling glBegin with GL_QUADS as input and providing four points.  Like the triangles, another could be drawn by providing four more points.

    However, glBegin with input GL_POLYGON does not work this way: only one polygon will be drawn since it can have an arbitrary number of points (hexagon, octagon, etc.)  Also, notice that the points must trace around the perimeter of the shape clockwise or counter-clockwise.  Even the rectangle required this, so make sure to give OpenGL points which connect to the next or previous point by edge.

Drawing with Color
    So far, the output has been white-colored shapes (although they may appear another color since OpenGL's default color varies).  To specify which color we want a shape, we must change OpenGL's current color before drawing the shape.  OpenGL saves its current working color and applies it to any shape drawn, but this color can be changed with either the function glColor3f or glColor3ub.

    Note from the previous example that the shapes are stretched horizontally: the triangles should be of equal dimensions and the rectangle is actually a square.  However, the coordinate system I use assigns equal distances for the horizontal and vertical axes despite the pixel width and height not being equal.

    SDL does not encounter this problem since the coordinate system uses the pixel dimensions.  When using OpenGL, we must ensure that our coordinate system has the same width to height ratio as the pixel dimensions of the window.  This is called the aspect ratio and for a 640 by 480 window, the aspect ratio is 1.33333 or 4:3.

    The following example will cover how to calculate the aspect ratio and color shapes:
/*thanks to tohtml.com for syntax highlighting*/

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h> /*new header file*/
#include <stdio.h>

SDL_Surface* screen;
SDL_Event input;
int loop = 1;
double aspect;

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_EVERYTHING); /*initialize SDL*/
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);

    /*calculate and print aspect ratio of window*/
    aspect = (double)screen->w / (double)screen->h;
    printf("aspect ratio = %lg\n", aspect);

    /*set clear color to green*/
    glClearColor(0.0, 0.5, 0.0, 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;
       }

       /*draw quadrilaterals*/
       glBegin(GL_QUADS);

       /*set color to blue*/
       glColor3ub(0, 0, 255);

       /*blue square on left*/
       glVertex2f(1.5, 2);
       glVertex2f(1.5, 1);
       glVertex2f(0.5, 1);
       glVertex2f(0.5, 2);

       /*set color to yellow*/
       glColor3f(1, 1, 0);

       /*trapezoid on right*/
       glVertex2f(2, 2.5);
       glVertex2f(3, 2.5);

       glColor3f(1, 0, 0); /*set color to red*/
       glVertex2f(4, 0.5);

       glColor3f(0, 1, 0); /*set color to green*/
       glVertex2f(1, 0.5);

       glEnd();

       SDL_GL_SwapBuffers();
       SDL_Delay(20); /*wait 20ms*/
    }

    /*print that the program is exitting*/
    printf("User has quit the program\n");

    /*perform final commands then exit*/
    SDL_Quit(); /*close SDL*/
    fflush(stdout); /*update stdout*/
    return 0;
}
prompt/stdout:
aspect ratio = 1.33333
User has quit the program

output inside window:
Colored Quadrilaterals


    To calculate the aspect ratio, simply divide the width by the height.  However, make sure that two doubles or floats are divided since an integer division would result in 1 due to rounding.  The aspect ratio is preserved in the coordinate system by setting the right to 4 and the top to 3 (for a 4:3 ratio).  Now the square appears as a square rather than stretched to a rectangle.

    To set the color to blue, glColor3ub requires three unsigned bytes (or unsigned char values).  This works similar to SDL, where 0 is the lowest and 255 is the highest.  Blue would be 0 red, 0 green, and 255 blue as shown above.  Next, all points of the square are given and a blue square is drawn.

    Color can be changed before or while the shape is drawn.  The trapezoid has two yellow vertices, one red vertex, and a green vertex.  To have points of different colors, use glColor3f (or glColor3ub) to set the color before individual points are given.  Finally, notice that like SDL, when a shape overlaps another the last one drawn is the most visible.  In this case, the trapezoid is drawn over some pixels of the square.

    For more information on OpenGL shapes and colors, see the pages http://www.swiftless.com/tutorials/opengl/square.html and http://www.swiftless.com/tutorials/opengl/color.html.  Also check out http://www.videotutorialsrock.com/opengl_tutorial/color/text.php.  For documentation of glBegin and gluOrtho2D, see http://www.opengl.org/sdk/docs/man2/xhtml/glBegin.xml and http://www.opengl.org/sdk/docs/man2/xhtml/gluOrtho2D.xml.

    Thank you for reading and happy holidays.  I hope to have a post about OpenGL textures up before New Years.