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 }
