Using OpenGL with SDL for Game Programming

In the world of gaming, SDL provides the entire necessary infrastructure. This would have become clear from previous articles in this series. Infrastructure is to a game what a skeleton is to a human body. But without muscles, no locomotion is possible. So working with the body analogy, SDL provides the skeletal structure to build the game whereas the flesh, blood and skin are provided by 2D and 3D graphics libraries.

In the current plethora of 3D libraries, OpenGL stands out for various reasons. The most significant of these is its compatibility with almost all platforms and graphics cards. This is reflected even in the architecture of SDL as SDL can create and use OpenGL contexts on several platforms. Such architecture helps the game programmer to use all the sub-systems of SDL seamlessly in conjunction with OpenGL to provide the most effective games and gaming environments.

In this article I will discuss how to use SDL and OpenGL together, with the gaming infrastructure provided by SDL, and animation as well as rendering being handled by OpenGL. The first section will detail the whys and wherefores of OpenGL. The second section will discuss the steps required to integrate OpenGL with SDL. The third section will utilize the pointers provided in the second section to create an application having some basic animation using OpenGL. That is the agenda for the current discussion.

If you wish to refresh your memory with previous articles in the series, they can be found at the following links:

Part 1: Game Programming using SDL: Getting Started

Part 2: Game Programming using SDL: Raw Graphics and Event Handling

Part 3: Learning Sound for Game Programming using SDL

{mospagebreak title=OpenGL: What is it?}

If this question is asked, then the most common answer one would get is that OpenGL is a graphics library in C. However, this is a misconception. In fact, OpenGL is a low-level graphics library specification. Just like J2EE, OpenGL is nothing but a set of platform neutral, language independent and vendor neutral APIs. These APIs are procedural in nature. In simple terms, this means a programmer does not describe the object and appearances; instead he/she details the steps through which an effect or an appearance can be achieved.

These steps comprise many OpenGL commands; that includes commands to draw graphic primitives such as point, line, polygon etc. in three dimensions. OpenGL also provides commands and procedures to work with lighting, textures, animations and so forth. One important aspect to keep in mind is that OpenGL is meant for rendering. Hence it does not provide any APIs for working with I/O management, window management, etc.

That’s where SDL comes into the picture. To understand how OpenGL renders, it is important to understand how it interfaces between graphics application and graphics card. So here we go.

The interfacing works at three levels. They are:

  1. Generic Implementation
  2. Hardware Implementation
  3. OpenGL pipeline

While the generic implementation deals with providing a rendering layer that sits on top of the OS specific rendering system, the hardware implementation provides direct hardware interfacing and the OpenGL pipeline works at taking the command and giving it to hardware after processing. Let’s look at the details.

The other phrase for generic implementation is software rendering. If the system can display generated graphics, then technically speaking a generic implementation can run anywhere. The place occupied by the generic implementation is between the program and the software rasterizer. Pictorially it would be:

It is clear from the diagram that the generic implementation takes the help of OS specific APIs to draw the generated graphics. For example on Windows it is GDI whereas on *nix systems it is XLib. The generic implementation on Windows is known as WOGL and on Linux it is MESA 3D.

Now let’s move on to hardware implementation. The problem with generic implementation is that it depends on the OS for rendering; hence the rendering speed and quality differs from OS to OS. This is where hardware implementation comes in. In this case, the calls to the OpenGL APIs are passed directly to the device driver (typically the AGP card’s driver). The driver directly interfaces with the graphics device instead of routing it through OS specific graphics system. Diagrammatically:

The functioning of a hardware implementation is totally different from that of a generic implementation, which is evident from the diagram. Interfacing with the device driver directly enhances both the quality and speed of the rendered graphics.

Finally, we will talk about an OpenGL pipeline. In essence, the term "pipeline" is a process that is concerned with the finer steps of a conversion or transformation. In other words, a process such as conversion can be broken down into finer steps. These steps together form the pipeline.

In a graphics pipeline, each stage or step refines the scene. In the case of OpenGL it is vertex data. Whenever an application makes an API call, it is placed at the command buffer along with commands, texture, vertex data and so forth. Upon flushing this buffer (either programmatically or by driver), the contained data is passed on to the next step where calculation-intensive lighting and transformations are applied. Once this is completed the next step creates colored images from the geometric, color and texture data. The created image is placed in the frame buffer which is the memory of the graphic device that is the screen. Pictorially this would be:

Though this a simplified version of the actual process, the above detailed process provides an insight into the working of OpenGL. This concludes this part of the discussion. However one question still remains: what are the basic steps for using OpenGL with SDL? That is what next section is about.

{mospagebreak title=Initialization: Bringing OpenGL into the Picture}

In SDL all the sub-systems are initialized via SDL_Init(). OpenGL, being a part of the graphics subsystem, is not directly initialized in this manner. For initializing OpenGL, these three steps should be followed: 

  1. Set OpenGL Attributes
  2. Specify use of Double Buffering
  3. Set the Video Mode

Of these, the second one is optional as it is used only when double buffering is a requirement. Let’s have a detailed look at all of them.

Setting the OpenGL Attributes

Before initializing the video, it is better to set up the OpenGL attributes. These attributes are passed to OpenGL via SDL calling the SDL_GL_SetAttribute() function. The parameters are the OpenGL attribute and their values. The most common attributes passed to this function are:

1. SDL_GL_RED_SIZE:

It sets the size of the red component of the frame buffer. The value is in bits. The most commonly used value is 5. Similar parameters exist for blue and green components which are SDL_GL_BLUE_SIZE and SDL_GL_GREEN_SIZE respectively. To set green component to a bit value of 4 the code would be:

SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 );

2. SDL_GL_BUFFER_SIZE:

To set the size of the frame buffer this attribute is passed with the required Buffer size in bits. It is greater than or equal to the combined value i.e. sum of the red, green, blue and alpha components. If the requirement is 24 bit color depth and an alpha channel of 32 bits then each color component must be given the size value of 8 and the frame buffer must be given a size of 32. In code it would be:

SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

3. SDL_GL_DEPTH_SIZE:

This attribute controls the size of the depth buffer or Z buffer. Normally graphics cards provide 16-bit or 24-bit depth. If the value is set higher than what is available, the operation will fail. To make it more clear, check out the following code:

SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);

4. SDL_GL_ACCUM_RED_SIZE:

To set the size of the red component’s accumulation buffer, this attribute is used.

The value is specified in bits. SDL_GL_ACCUM_BLUE_SIZE, SDL_GL_ACCUM_GREEN_SIZE controls the size of blue and green components’ accumulation buffer. In code it would be:

SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,5);

You are probably wondering right now whether these attributes could be set after initializing the video mode. The answer is no. The reason is that these settings have to be initialized before invoking and configuring the video mode. Next we have to set up the video mode.

Setting up double buffering

This aspect must also to be covered before setting up the video mode as this attribute goes as a flag parameter to the SDL_GL_SetAttribute. The attribute is SDL_GL_DOUBLEBUFFER and the value is either 1 or 0. The point to be kept in mind is that when working in conjunction with OpenGL, the flag specifying the double buffer must be passed as an attribute to the SDL_GL_SetAttribute function and not to the SDL_Set_VideoMode(). In code this would be:

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);

would set the double buffering to one state.

Setting the Video Mode

Once the OpenGL attributes are set, setting the video mode is similar to the setting of video mode as described in previous tutorials. For more details have a look at the second article in this series. The only difference comes in flags being sent to the SDL_Set_VideoMode(). Apart from other required flags the SDL_OPENGL would also be set i.e.

int flags=0;
flags= SDL_OPENGL | SDL_FULLSCREEN;

Setting the SDL_OPENGL flag is a must.

That covers all the required steps, Now let’s see OpenGL at play.

{mospagebreak title=OpenGL with SDL in Action}

The theory is over. It’s now time to see some real action. The example application will render a rotating triangle. The includes will contain one more header file.

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

gl.h contains function declarations necessary for working with OpenGL.

Next comes the main() and OpenGL attributes.

int main(int argc, char *argv[])
{
  
SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
  
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   :
  
:
}

Next we initialize the video and video mode

int main(int argc, char *argv[])
{
  
SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
  
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   SDL_Init(SDL_INIT_VIDEO);
  
SDL_SetVideoMode(600, 300, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_NOFRAME);

   :
  
:
}

The video is initialized to 600×300 resolutions. And the hardware rendering mode is being used. This is done by the SDL_HWSURFACE flag. Hence OpenGL would write on the graphic card’s memory instead of mapping it to software memory. After this step, we move into the territory of OpenGL.

int main(int argc, char *argv[])
{
   SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
  
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   SDL_Init(SDL_INIT_VIDEO);
  
SDL_SetVideoMode(600, 300, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_NOFRAME);

   glViewport(0, 0, 600, 300);
  
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  
glClearDepth(1.0);
  
glDepthFunc(GL_LESS);
  
glEnable(GL_DEPTH_TEST);
  
glShadeModel(GL_SMOOTH);
  
glMatrixMode(GL_PROJECTION);
  
glMatrixMode(GL_MODELVIEW);
  
:
  
:
}

To start working with OpenGL, the view port is initialized. Then the screen is cleared or rendered with the specified background color. Since the triangle would be rotating in 3D space, the depth has to be set and depth testing has to be enabled. If smooth shading is not used, then the edges would seem jagged. Hence the smooth shading model is used.

This completes the setting up of OpenGL parameters after SDL video initialization. Drawing and rotation is taken care of by the following code in bold:

int main(int argc, char *argv[])
{
  
SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
  
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   SDL_Init(SDL_INIT_VIDEO);
  
SDL_SetVideoMode(600, 300, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_NOFRAME);

   glViewport(0, 0, 600, 300);
  
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  
glClearDepth(1.0);
  
glDepthFunc(GL_LESS);
  
glEnable(GL_DEPTH_TEST);
  
glShadeModel(GL_SMOOTH);
  
glMatrixMode(GL_PROJECTION);
  
glMatrixMode(GL_MODELVIEW);

   int done;
  
for(done = 0; !done;)
  
{
     
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

      glLoadIdentity();
     
glTranslatef(0.0f,0.0f,0.0f);
     
glRotatef(theta, 0.0f, 0.0f, 1.0f);
     
:
     
:
   }
}

The focus is brought to the point of origin by translating it. Then rotation function is provided with the theta value through which the triangle has to be rotated.

int main(int argc, char *argv[])
{
  
SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
   SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   SDL_Init(SDL_INIT_VIDEO);
  
SDL_SetVideoMode(600, 300, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_NOFRAME);

   glViewport(0, 0, 600, 300);
  
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  
glClearDepth(1.0);
  
glDepthFunc(GL_LESS);
  
glEnable(GL_DEPTH_TEST);
  
glShadeModel(GL_SMOOTH);
  
glMatrixMode(GL_PROJECTION);
  
glMatrixMode(GL_MODELVIEW);

   int done;
  
for(done = 0; !done;)
  
{
     
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

     glLoadIdentity();
    
glTranslatef(0.0f,0.0f,0.0f);
    
glRotatef(theta, 0.0f, 0.0f, 1.0f);
    
glBegin(GL_TRIANGLES);
    
glColor3f(1.0f, 0.0f, 0.0f);
    
glVertex2f(0.0f, 1.0f);
    
glColor3f(0.0f, 1.0f, 0.0f);
    
glVertex2f(0.87f, -0.5f);
    
glColor3f(0.0f, 0.0f, 1.0f);
    
glVertex2f(-0.87f, -0.5f);
    
glEnd();

     theta += .5f;
    
SDL_GL_SwapBuffers();
    
:
    
:
  
}
}

The triangle is drawn by specifying the vertices. Then the theta value is increased. Next the event handling part comes into play.

int main(int argc, char *argv[])
{
  
SDL_Event event;
  
float theta = 0.0f;
  
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
  
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,8 );
  
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );

   SDL_Init(SDL_INIT_VIDEO);
  
SDL_SetVideoMode(600, 300, 0, SDL_OPENGL | SDL_HWSURFACE | SDL_NOFRAME);

   glViewport(0, 0, 600, 300);
  
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  
glClearDepth(1.0);
  
glDepthFunc(GL_LESS);
  
glEnable(GL_DEPTH_TEST);
  
glShadeModel(GL_SMOOTH);
  
glMatrixMode(GL_PROJECTION);
  
glMatrixMode(GL_MODELVIEW);

   int done;
  
for(done = 0; !done;)
  
{
    
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

     glLoadIdentity();
    
glTranslatef(0.0f,0.0f,0.0f);
    
glRotatef(theta, 0.0f, 0.0f, 1.0f);
    
glBegin(GL_TRIANGLES);
    
glColor3f(1.0f, 0.0f, 0.0f);
    
glVertex2f(0.0f, 1.0f);
    
glColor3f(0.0f, 1.0f, 0.0f);
    
glVertex2f(0.87f, -0.5f);
    
glColor3f(0.0f, 0.0f, 1.0f);
    
glVertex2f(-0.87f, -0.5f);
    
glEnd();

     theta += .5f;
    
SDL_GL_SwapBuffers();
    
SDL_PollEvent(&event);
    
if(event.key.keysym.sym == SDLK_ESCAPE)
      
done = 1;
   }
}

That’s it. This is how SDL and OpenGL work together. Though this section covers the technical details of OpenGL, the complete steps for using OpenGL are not discussed. That will be the topic of the next discussion.

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort