Using OpenGL With SDL

OpenGL is a 3D graphics library which is available for all major platforms. However, each platform requires a lot of different code to get going with OpenGL so it is not an ideal solution for cross-platform 3D programs. SDL tackles this so that you can write an OpenGL-enabled program with SDL that will compile for many different platforms.

SDL has the ability to create and use OpenGL contexts on several platforms (Linux/X11, Win32, BeOS, MacOS Classic/Toolbox, Mac OS X, FreeBSD/X11 and Solaris/X11). This allows you to use SDL's audio, event handling, threads and time subsystems in your OpenGL applications. The event handling and window management are often performed by the library GLUT.

Initializing SDL with OpenGL

Initializing SDL to use OpenGL is not very different to initializing SDL normally. There are three differences: you must pass SDL_OPENGL to SDL_SetVideoMode, you must specify several GL attributes (depth buffer size, framebuffer sizes) using SDL_GL_SetAttribute and finally, if you wish to use double buffering you must specify it as a GL attribute, not by passing the SDL_DOUBLEBUF flag to SDL_SetVideoMode.

   1 /* Information about the current video settings. */
   2 const SDL_VideoInfo* info = NULL;
   3 
   4 /* Dimensions of our window. */
   5 int width = 0;
   6 int height = 0;
   7 
   8 /* Color depth in bits of our window. */
   9 int bpp = 0;
  10 
  11 /* Flags we will pass into SDL_SetVideoMode. */
  12 int flags = 0;
  13 
  14 /* First, initialize SDL's video subsystem. */
  15 if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
  16     /* Failed, exit. */
  17     fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError( ) );
  18     quit_tutorial( 1 );
  19 }
  20 
  21 /* Let's get some video information. */
  22 info = SDL_GetVideoInfo( );
  23 
  24 if( !info ) {
  25     /* This should probably never happen. */
  26     fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
  27     quit_tutorial( 1 );
  28 }
  29 
  30 /*
  31  * Set our width/height to 640/480 (you would
  32  * of course let the user decide this in a normal
  33  * app). We get the bpp we will request from
  34  * the display. On X11, VidMode can't change
  35  * bpp, so this is probably being overly
  36  * safe. Under Win32, ChangeDisplaySettings
  37  * can change the bpp.
  38  */
  39 width = 640;
  40 height = 480;
  41 bpp = info->vfmt->BitsPerPixel;
  42 
  43 /*
  44  * Now, we want to setup our requested
  45  * window attributes for our OpenGL window.
  46  * We want *at least* 5 bits of red, green
  47  * and blue. We also want at least a 16-bit
  48  * depth buffer.
  49  *
  50  * The last thing we do is request a double
  51  * buffered window. '1' turns on double
  52  * buffering, '0' turns it off.
  53  *
  54  * Note that we do not use SDL_DOUBLEBUF in
  55  * the flags to SDL_SetVideoMode. That does
  56  * not affect the GL attribute state, only
  57  * the standard 2D blitting setup.
  58  */
  59 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
  60 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
  61 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
  62 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
  63 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
  64 
  65 /*
  66  * We want to request that SDL provide us
  67  * with an OpenGL window, in a fullscreen
  68  * video mode.
  69  *
  70  * EXERCISE:
  71  * Make starting windowed an option, and
  72  * handle the resize events properly with
  73  * glViewport.
  74  */
  75 flags = SDL_OPENGL | SDL_FULLSCREEN;
  76 
  77 /*
  78  * Set the video mode
  79  */
  80 if ( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
  81     /* 
  82      * This could happen for a variety of reasons,
  83      * including DISPLAY not being set, the specified
  84      * resolution not being available, etc.
  85      */
  86     fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
  87     quit_tutorial( 1 );
  88 }

SDL and OpenGL

Apart from initialization, using OpenGL within SDL is the same as using OpenGL with any other API, e.g. GLUT. You still use all the same function calls and data types. However if you are using a double-buffered display, then you must use SDL_GL_SwapBuffers to swap the buffers and update the display. To request double-buffering with OpenGL, use SDL_GL_SetAttribute with SDL_GL_DOUBLEBUFFER, and use SDL_GL_GetAttribute to see if you actually got it.

   1 /*
   2  * SDL OpenGL Tutorial.
   3  * (c) Michael Vance, 2000
   4  * briareos@lokigames.com
   5  *
   6  * Distributed under terms of the LGPL. 
   7  */
   8 
   9 #include <SDL/SDL.h>
  10 #include <GL/gl.h>
  11 #include <GL/glu.h>
  12 
  13 #include <stdio.h>
  14 #include <stdlib.h>
  15 
  16 static GLboolean should_rotate = GL_TRUE;
  17 
  18 static void quit_tutorial( int code )
  19 {
  20     /*
  21      * Quit SDL so we can release the fullscreen
  22      * mode and restore the previous video settings, etc.
  23      */
  24     SDL_Quit( );
  25 
  26     /* Exit program. */
  27     exit( code );
  28 }
  29 
  30 
  31 static void handle_key_down( SDL_keysym* keysym )
  32 {
  33 
  34     /* 
  35      * We're only interested if 'Esc' has been presssed.
  36      *
  37      * EXERCISE: 
  38      * Handle the arrow keys and have that change the
  39      * viewing position/angle.
  40      */
  41     switch( keysym->sym ) {
  42     case SDLK_ESCAPE:
  43         quit_tutorial( 0 );
  44         break;
  45     case SDLK_SPACE:
  46         should_rotate = !should_rotate;
  47         break;
  48     default:
  49         break;
  50     }
  51 
  52 }
  53 
  54 
  55 static void process_events( void )
  56 {
  57     /* Our SDL event placeholder. */
  58     SDL_Event event;
  59 
  60     /* Grab all the events off the queue. */
  61     while( SDL_PollEvent( &event ) ) {
  62         switch( event.type ) {
  63         case SDL_KEYDOWN:
  64             /* Handle key presses. */
  65             handle_key_down( &event.key.keysym );
  66             break;
  67 
  68         case SDL_QUIT:
  69             /* Handle quit requests (like Ctrl-c). */
  70             quit_tutorial( 0 );
  71             break;
  72 
  73         default:
  74             break;
  75         }
  76     } /* while */
  77 }
  78 
  79 
  80 static void draw_screen( void )
  81 {
  82     /* Our angle of rotation. */
  83     static float angle = 0.0f;
  84 
  85     /*
  86      * EXERCISE:
  87      * Replace this awful mess with vertex
  88      * arrays and a call to glDrawElements.
  89      *
  90      * EXERCISE:
  91      * After completing the above, change
  92      * it to use compiled vertex arrays.
  93      *
  94      * EXERCISE:
  95      * Verify my windings are correct here ;).
  96      */
  97     static GLfloat v0[] = { -1.0f, -1.0f,  1.0f };
  98     static GLfloat v1[] = {  1.0f, -1.0f,  1.0f };
  99     static GLfloat v2[] = {  1.0f,  1.0f,  1.0f };
 100     static GLfloat v3[] = { -1.0f,  1.0f,  1.0f };
 101     static GLfloat v4[] = { -1.0f, -1.0f, -1.0f };
 102     static GLfloat v5[] = {  1.0f, -1.0f, -1.0f };
 103     static GLfloat v6[] = {  1.0f,  1.0f, -1.0f };
 104     static GLfloat v7[] = { -1.0f,  1.0f, -1.0f };
 105     static GLubyte red[]    = { 255,   0,   0, 255 };
 106     static GLubyte green[]  = {   0, 255,   0, 255 };
 107     static GLubyte blue[]   = {   0,   0, 255, 255 };
 108     static GLubyte white[]  = { 255, 255, 255, 255 };
 109     static GLubyte yellow[] = {   0, 255, 255, 255 };
 110     static GLubyte black[]  = {   0,   0,   0, 255 };
 111     static GLubyte orange[] = { 255, 255,   0, 255 };
 112     static GLubyte purple[] = { 255,   0, 255,   0 };
 113 
 114     /* Clear the color and depth buffers. */
 115     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 116 
 117     /* We don't want to modify the projection matrix. */
 118     glMatrixMode( GL_MODELVIEW );
 119     glLoadIdentity( );
 120 
 121     /* Move down the z-axis. */
 122     glTranslatef( 0.0, 0.0, -5.0 );
 123 
 124     /* Rotate. */
 125     glRotatef( angle, 0.0, 1.0, 0.0 );
 126 
 127     if ( should_rotate ) {
 128         if ( ++angle > 360.0f ) {
 129             angle = 0.0f;
 130         }
 131     }
 132 
 133     /* Send our triangle data to the pipeline. */
 134     glBegin( GL_TRIANGLES );
 135 
 136     glColor4ubv( red );
 137     glVertex3fv( v0 );
 138     glColor4ubv( green );
 139     glVertex3fv( v1 );
 140     glColor4ubv( blue );
 141     glVertex3fv( v2 );
 142 
 143     glColor4ubv( red );
 144     glVertex3fv( v0 );
 145     glColor4ubv( blue );
 146     glVertex3fv( v2 );
 147     glColor4ubv( white );
 148     glVertex3fv( v3 );
 149 
 150     glColor4ubv( green );
 151     glVertex3fv( v1 );
 152     glColor4ubv( black );
 153     glVertex3fv( v5 );
 154     glColor4ubv( orange );
 155     glVertex3fv( v6 );
 156 
 157     glColor4ubv( green );
 158     glVertex3fv( v1 );
 159     glColor4ubv( orange );
 160     glVertex3fv( v6 );
 161     glColor4ubv( blue );
 162     glVertex3fv( v2 );
 163 
 164     glColor4ubv( black );
 165     glVertex3fv( v5 );
 166     glColor4ubv( yellow );
 167     glVertex3fv( v4 );
 168     glColor4ubv( purple );
 169     glVertex3fv( v7 );
 170 
 171     glColor4ubv( black );
 172     glVertex3fv( v5 );
 173     glColor4ubv( purple );
 174     glVertex3fv( v7 );
 175     glColor4ubv( orange );
 176     glVertex3fv( v6 );
 177 
 178     glColor4ubv( yellow );
 179     glVertex3fv( v4 );
 180     glColor4ubv( red );
 181     glVertex3fv( v0 );
 182     glColor4ubv( white );
 183     glVertex3fv( v3 );
 184 
 185     glColor4ubv( yellow );
 186     glVertex3fv( v4 );
 187     glColor4ubv( white );
 188     glVertex3fv( v3 );
 189     glColor4ubv( purple );
 190     glVertex3fv( v7 );
 191 
 192     glColor4ubv( white );
 193     glVertex3fv( v3 );
 194     glColor4ubv( blue );
 195     glVertex3fv( v2 );
 196     glColor4ubv( orange );
 197     glVertex3fv( v6 );
 198 
 199     glColor4ubv( white );
 200     glVertex3fv( v3 );
 201     glColor4ubv( orange );
 202     glVertex3fv( v6 );
 203     glColor4ubv( purple );
 204     glVertex3fv( v7 );
 205 
 206     glColor4ubv( green );
 207     glVertex3fv( v1 );
 208     glColor4ubv( red );
 209     glVertex3fv( v0 );
 210     glColor4ubv( yellow );
 211     glVertex3fv( v4 );
 212 
 213     glColor4ubv( green );
 214     glVertex3fv( v1 );
 215     glColor4ubv( yellow );
 216     glVertex3fv( v4 );
 217     glColor4ubv( black );
 218     glVertex3fv( v5 );
 219 
 220     glEnd( );
 221 
 222     /*
 223      * EXERCISE:
 224      * Draw text telling the user that 'Spc' pauses the rotation and 'Esc' quits.
 225      * Do it using vetors and textured quads.
 226      */
 227 
 228     /*
 229      * Swap the buffers. This this tells the driver to
 230      * render the next frame from the contents of the
 231      * back-buffer, and to set all rendering operations
 232      * to occur on what was the front-buffer.
 233      *
 234      * Double buffering prevents nasty visual tearing
 235      * from the application drawing on areas of the
 236      * screen that are being updated at the same time.
 237      */
 238     SDL_GL_SwapBuffers( );
 239 }
 240 
 241 
 242 static void setup_opengl( int width, int height )
 243 {
 244     float ratio = (float) width / (float) height;
 245 
 246     /* Our shading model--Gouraud (smooth). */
 247     glShadeModel( GL_SMOOTH );
 248 
 249     /* Culling. */
 250     glCullFace( GL_BACK );
 251     glFrontFace( GL_CCW );
 252     glEnable( GL_CULL_FACE );
 253 
 254     /* Set the clear color. */
 255     glClearColor( 0, 0, 0, 0 );
 256 
 257     /* Setup our viewport. */
 258     glViewport( 0, 0, width, height );
 259 
 260     /*
 261      * Change to the projection matrix and set
 262      * our viewing volume.
 263      */
 264     glMatrixMode( GL_PROJECTION );
 265     glLoadIdentity( );
 266 
 267     /*
 268      * EXERCISE:
 269      * Replace this with a call to glFrustum.
 270      */
 271     gluPerspective( 60.0, ratio, 1.0, 1024.0 );
 272 }
 273 
 274 
 275 int main( int argc, char* argv[] )
 276 {
 277     /* Information about the current video settings. */
 278     const SDL_VideoInfo* info = NULL;
 279 
 280     /* Dimensions of our window. */
 281     int width = 0;
 282     int height = 0;
 283 
 284     /* Color depth in bits of our window. */
 285     int bpp = 0;
 286 
 287     /* Flags we will pass into SDL_SetVideoMode. */
 288     int flags = 0;
 289 
 290     /* First, initialize SDL's video subsystem. */
 291     if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
 292         /* Failed, exit. */
 293         fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError( ) );
 294         quit_tutorial( 1 );
 295     }
 296 
 297     /* Let's get some video information. */
 298     info = SDL_GetVideoInfo( );
 299 
 300     if ( !info ) {
 301         /* This should probably never happen. */
 302         fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
 303         quit_tutorial( 1 );
 304     }
 305 
 306     /*
 307      * Set our width/height to 640/480 (you would of course let the user
 308      * decide this in a normal app). We get the bpp we will request from
 309      * the display. On X11, VidMode can't change resolution, so this is
 310      * probably being overly safe. Under Win32, ChangeDisplaySettings
 311      * can change the bpp.
 312      */
 313     width = 640;
 314     height = 480;
 315     bpp = info->vfmt->BitsPerPixel;
 316 
 317     /*
 318      * Now, we want to setup our requested
 319      * window attributes for our OpenGL window.
 320      * We want *at least* 5 bits of red, green
 321      * and blue. We also want at least a 16-bit
 322      * depth buffer.
 323      *
 324      * The last thing we do is request a double
 325      * buffered window. '1' turns on double
 326      * buffering, '0' turns it off.
 327      *
 328      * Note that we do not use SDL_DOUBLEBUF in
 329      * the flags to SDL_SetVideoMode. That does
 330      * not affect the GL attribute state, only
 331      * the standard 2D blitting setup.
 332      */
 333     SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
 334     SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
 335     SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
 336     SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
 337     SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
 338 
 339     /*
 340      * We want to request that SDL provide us
 341      * with an OpenGL window, in a fullscreen
 342      * video mode.
 343      *
 344      * EXERCISE:
 345      * Make starting windowed an option, and
 346      * handle the resize events properly with
 347      * glViewport.
 348      */
 349     flags = SDL_OPENGL | SDL_FULLSCREEN;
 350 
 351     /*
 352      * Set the video mode
 353      */
 354     if ( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
 355         /* 
 356          * This could happen for a variety of reasons,
 357          * including DISPLAY not being set, the specified
 358          * resolution not being available, etc.
 359          */
 360         fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
 361         quit_tutorial( 1 );
 362     }
 363 
 364     /*
 365      * At this point, we should have a properly setup
 366      * double-buffered window for use with OpenGL.
 367      */
 368     setup_opengl( width, height );
 369 
 370     /*
 371      * Now we want to begin our normal app process--
 372      * an event loop with a lot of redrawing.
 373      */
 374     while (1) {
 375         /* Process incoming events. */
 376         process_events( );
 377 
 378         /* Draw the screen. */
 379         draw_screen( );
 380 
 381         /* Avoid to eat all the CPU power */
 382         SDL_Delay( 50 );
 383     }
 384 
 385     /*
 386      * EXERCISE:
 387      * Record timings using SDL_GetTicks() and
 388      * and print out frames per second at program
 389      * end.
 390      */
 391 
 392     /* Never reached. */
 393     return 0;
 394 }

See also

Introduction_to_SDL_Video


CategoryGuide

Using_OpenGL_With_SDL (last edited 2009-08-12 21:18:38 by p5088FE21)