In this new tutorial series, we will be looking at using SDL along with OpenGL. SDL as it stands is nice for basic 2D graphics/composition, but when it comes to more intensive gaming applications or 3D graphics, we need to leverage the use of other libraries. SDL works so well with OpenGL because we can use OpenGL to manage only graphical rendering, and leave SDL for everything else (Events, Window management, etc).

So, lets get started. We'll be basing our code off of the "SDL Tutorial Basics" tutorial - so head on over there first if you haven't had a chance to look at it. Primarily what we'll be doing here is making any necessary changes to the code for use with OpenGL. What's nice about SDL is that it provides an easy way to setup OpenGL. On the technical side of things, whenever a window is created on an operating system, an OpenGL context must also be created (with certain properties) and linked to that window. SDL makes this rather easy for us.

Before we get started with modifying our code, open up your project file (the one from SDL Tutorial Basics). Because we are using another library now, we are going to need to link to it. So, open up your Project Properties, and add "opengl32", "glu32" to your linker settings (this is the same area that has mingw32, SDLmain, SDL). If you are another operating system besides Windows, your library names will most likely be "opengl", "glu." Most people already have the OpenGL library installed (as it usually comes with CodeBlocks). If you don't, make sure you downloaded the CodeBlocks with mingw. If you are on some Linux flavor, try installing Mesa. Also, as a last note, put the new libraries after the SDL libraries.

That's it for project settings, lets move on to modifying our code. Open up CApp.h and include our OpenGL header file.
#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>
#include <gl/gl.h>
#include <gl/glu.h>

// ... other code below
If you are wondering what the difference between gl and glu, gl contains the primary functions from the OpenGL specification, and glu contains several helper/utility functions for creating complex shapes, moving the camera, etc. So now that we have that, lets actually get OpenGL up and running. Open up CApp_OnInit.cpp. Right now this is what we have:
#include "CApp.h"

bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }

    if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
        return false;
    }

    return true;
}
The first thing we need to do is tell SDL that we are going to be using OpenGL. Thankfully, SDL provides an easy way to do this. Add the flag SDL_OPENGL just after SDL_DOUBLEBUF, and change SDL_DOUBLEBUF to SDL_GL_DOUBLEBUFFER:
    if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL)) == NULL) {
        return false;
    }
Now, there are several settings you can set for OpenGL. By default, most are fine just as is, but I want to make you aware of them:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,        8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,      8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,       8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,      8);

SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,      16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,        32);

SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,    8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,    8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,    8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,    8);

SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,  1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,  2);
These are all basic memory sizes for how much data OpenGL will store (except SDL_GL_MULTISAMPLEBUFFERS/SDL_GL_MULTISAMPLESAMPLES, those are for something else). By increasing these, you will effectively increase how much memory OpenGL will use for color information, and the buffer. As I mentioned before, the default on all of these is usually just fine. If you wish to set these, the values I have up above are pretty good. Be sure to call SDL_GL_SetAttribute before calling SDL_SetVideoMode. One thing I want to mention is on SDL_GL_MULTISAMPLEBUFFERS. This is basically your anti-aliasing flag. By turning this on, you are turning on anti-aliasing. SDL_GL_MULTISAMPLESAMPLES is how much to anti-alias. A value of 2 is pretty standard if turned on, 4 is also commonly used. Just keep in mind that this can be resource intensive for some systems. Also, not every computer is able to perform anti-aliasing. And one last note! Make sure your SDL_GL_DEPTH_SIZE is a power of two (16, 32 works), otherwise anti-aliasing may not work for you.

Moving on. Now we basically have OpenGL setup with our window, but the problem is that OpenGL requires additional information about the rendering area. What I mean by this is that because OpenGL is a 3D rendering library, it needs to know some basic things like where the camera is, what our 3D world looks like, what color is our rendering area, etc.

So, lets add a few more lines of code after SDL_SetVideoMode.
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);

glViewport(0, 0, 640, 480);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glOrtho(0, 640, 480, 0, 1, -1);

glMatrixMode(GL_MODELVIEW);

glEnable(GL_TEXTURE_2D);

glLoadIdentity();
Okay, this is where we actually get to start using OpenGL. First, note that OpenGL is a C library (like SDL), and that all function calls begin with gl* (much like all SDL function calls begin with SDL_*). So what are we doing here exactly? The first function call, glClearColor, tells OpenGL what color to render the view when the color buffer is cleared. The color buffer is the buffer OpenGL uses to render pixels to the screen. glClearDepth does something similar. It tells OpenGL what value to reset the depth buffer when it is cleared. The depth buffer is used to determine the z-axis of a pixel. There are other buffers as well, but only worry about these two for now. The 4 arguments are basically Red, Green, Blue, Alpha. With 1.0f being full color, and 0.0f being no color. If you want to define values as regular RGB 255 values, do this:
glClearColor((128.0f / 255.0f), 1.0f, 1.0f, 1.0f); //Cyan color

Next, we setup the viewport for opengl. This, in essence, is the window area OpenGL will use. If you set it smaller to the SDL window size you'll have black borders, and if you set it larger then the SDL window, you'll have your view clipped. Usually, this will always be the same as the regular window size, but there are certain cases you would want it smaller (you can actually create multiple viewports, and have them all display different camera angles of a single scene). Arguments are X, Y, Width, Height.

After that, we tell OpenGL to change to GL_PROJECTION mode. OpenGL contains certain matrix modes that affect certain things. I don't want to confuse you with technical details, but you'll only ever want to use GL_PROJECTION when you are setting up the "projection" of the scene (which is what the glOrtho call does). Other possible projection functions are glLoadIdentity / gluPerspective / glFrustum / gluOrtho2D. Otherwise, you'll almost always want to be in GL_MODELVIEW mode.

So, after we are in projection mode, we call glLoadIdentity (which is basically a "reset matrix" call). Then, we call glOrtho. Now, to take a quick aside for a moment, we have been working in 2D thus far in all the tutorials on this website. This tutorial is no different, so we are going to use glOrtho to setup a "2D Perspective." An ortho, or orthogonal, perspective is one that flattens a 3D environment into a 2D one. This more or less elmiminates any 3D look, and makes it render like a regular 2D game. Please note though, you can still using the z-axis for layering. On the flip side, if you wanted a 3D environment, you would use gluPerspective or similar to setup a "3D Perspective." So, in simple basic terms, for 2D us glOrtho, and for 3D use gluPerspective. Regarding the arguments of the function, they are Left, Right, Bottom, Top, Near, Far. So, by setting Left to 0, and Right to 640, I am defining the perspective area. If you set these values to something different than the window size, it'd basically stretch / shrink everything.

Moving on, we switch to GL_MODELVIEW mode, reset it, and also enable GL_TEXTURE_2D (By default, textures are not enabled). What is a texture? A texture is very much like an SDL_Surface. It's an image stored in memory that can be rendered to the display, but unlike SDL_Surface's, Textures cannot be rendered to the screen on their own. They must be attached to some sort of primitive. Meaning, if you want to render a basic texture like an SDL_Surface, you have to "Bind" the texture (make it active), render a square, and then attach the texture to the four corners of that square. It sounds more complex then it really is, and we'll take a look at doing that in future tutorials.

This is how our OnInit methods ends up looking like:
bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE,            8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,          8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,           8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,          8);

    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,          16);
    SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,            32);

    SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,        8);
    SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,    8);
    SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,        8);
    SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,    8);

    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,  1);

    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,  2);

    if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL)) == NULL) {
        return false;
    }

    glClearColor(0, 0, 0, 0);

    glViewport(0, 0, 640, 480);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(0, 640, 480, 0, 1, -1);

    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_TEXTURE_2D);

    glLoadIdentity();

    return true;
}

Okay, that's it for our OpenGL setup. Please note that there are other setup options I haven't mentioned, but I am sure we will get to them in future tutorials.

Jump on over to OnRender and lets get something to show up on the screen.
void CApp::OnRender() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glBegin(GL_QUADS);
        glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
        glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
        glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
        glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
    glEnd();

    SDL_GL_SwapBuffers();
}

We should be able to get through this fairly quickly. The first line clears our color and depth buffer. For all simplistic purposes, this does a type of reset, and makes our screen blank again. This is similar to calling SDL_FillRect on SDL's primary display surface. Keep in mind though it does more than that. Then, we reset our GL_MODELVIEW matrix. Why? Whenever you move around in the 2D / 3D world in OpenGL to render things you are modifying the model view matrix. This matrix is what OpenGL uses to determine where and how to render something to the screen. So, if you are moving around rendering things, you need to reset it on every loop.

Next is where the magic starts to happen. glBegin is used to signal the start of the rendering of a primitive. The parameter is used to simply define what primitive you are going to render. In this case, we are going to render a Rectangle (called a Quad - 4 sides). glEnd is used to signal the end you rendering that primitive.

Now, when we go to render our primitive what we have to do is define each corner of that primitive. So, we start with the first corner (or vertex), specify the coordinates, and then move to the next, and so on. At the same time, I am defining the colors of each corner (each corner will have a different color). What OpenGL will do for us is color the entire primitive blending those four corner's colors together. It ends up looking like this:



Lastly, we call SDL_GL_SwapBuffers which basically takes the buffer and moves it to the primary display - much like SDL_Flip.

And that's it! I hope you see how easy it is to setup OpenGL. There's quiet a bit to memorize at first, but once you have it down, it's not that bad at all.

If you want more information about specific functions, check out the docs here:
http://www.opengl.org/sdk/docs/man/