1 /* 2 Copyright (C) 1997-2014 Sam Lantinga <slouken (at) libsdl.org> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11 */ 12 /* This is a simple example of using GLSL shaders with SDL */ 13 14 #include "SDL.h" 15 16 #ifdef HAVE_OPENGL 17 18 #include "SDL_opengl.h" 19 20 21 static SDL_bool shaders_supported; 22 static int current_shader = 0; 23 24 enum { 25 SHADER_COLOR, 26 SHADER_TEXTURE, 27 SHADER_TEXCOORDS, 28 NUM_SHADERS 29 }; 30 31 typedef struct { 32 GLhandleARB program; 33 GLhandleARB vert_shader; 34 GLhandleARB frag_shader; 35 const char *vert_source; 36 const char *frag_source; 37 } ShaderData; 38 39 static ShaderData shaders[NUM_SHADERS] = { 40 41 /* SHADER_COLOR */ 42 { 0, 0, 0, 43 /* vertex shader */ 44 "varying vec4 v_color;\n" 45 "\n" 46 "void main()\n" 47 "{\n" 48 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" 49 " v_color = gl_Color;\n" 50 "}", 51 /* fragment shader */ 52 "varying vec4 v_color;\n" 53 "\n" 54 "void main()\n" 55 "{\n" 56 " gl_FragColor = v_color;\n" 57 "}" 58 }, 59 60 /* SHADER_TEXTURE */ 61 { 0, 0, 0, 62 /* vertex shader */ 63 "varying vec4 v_color;\n" 64 "varying vec2 v_texCoord;\n" 65 "\n" 66 "void main()\n" 67 "{\n" 68 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" 69 " v_color = gl_Color;\n" 70 " v_texCoord = vec2(gl_MultiTexCoord0);\n" 71 "}", 72 /* fragment shader */ 73 "varying vec4 v_color;\n" 74 "varying vec2 v_texCoord;\n" 75 "uniform sampler2D tex0;\n" 76 "\n" 77 "void main()\n" 78 "{\n" 79 " gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n" 80 "}" 81 }, 82 83 /* SHADER_TEXCOORDS */ 84 { 0, 0, 0, 85 /* vertex shader */ 86 "varying vec2 v_texCoord;\n" 87 "\n" 88 "void main()\n" 89 "{\n" 90 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" 91 " v_texCoord = vec2(gl_MultiTexCoord0);\n" 92 "}", 93 /* fragment shader */ 94 "varying vec2 v_texCoord;\n" 95 "\n" 96 "void main()\n" 97 "{\n" 98 " vec4 color;\n" 99 " vec2 delta;\n" 100 " float dist;\n" 101 "\n" 102 " delta = vec2(0.5, 0.5) - v_texCoord;\n" 103 " dist = dot(delta, delta);\n" 104 "\n" 105 " color.r = v_texCoord.x;\n" 106 " color.g = v_texCoord.x * v_texCoord.y;\n" 107 " color.b = v_texCoord.y;\n" 108 " color.a = 1.0 - (dist * 4.0);\n" 109 " gl_FragColor = color;\n" 110 "}" 111 }, 112 }; 113 114 static PFNGLATTACHOBJECTARBPROC glAttachObjectARB; 115 static PFNGLCOMPILESHADERARBPROC glCompileShaderARB; 116 static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; 117 static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; 118 static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; 119 static PFNGLGETINFOLOGARBPROC glGetInfoLogARB; 120 static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; 121 static PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; 122 static PFNGLLINKPROGRAMARBPROC glLinkProgramARB; 123 static PFNGLSHADERSOURCEARBPROC glShaderSourceARB; 124 static PFNGLUNIFORM1IARBPROC glUniform1iARB; 125 static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; 126 127 static SDL_bool CompileShader(GLhandleARB shader, const char *source) 128 { 129 GLint status; 130 131 glShaderSourceARB(shader, 1, &source, NULL); 132 glCompileShaderARB(shader); 133 glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); 134 if (status == 0) { 135 GLint length; 136 char *info; 137 138 glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); 139 info = SDL_stack_alloc(char, length+1); 140 glGetInfoLogARB(shader, length, NULL, info); 141 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile shader:\n%s\n%s", source, info); 142 SDL_stack_free(info); 143 144 return SDL_FALSE; 145 } else { 146 return SDL_TRUE; 147 } 148 } 149 150 static SDL_bool CompileShaderProgram(ShaderData *data) 151 { 152 const int num_tmus_bound = 4; 153 int i; 154 GLint location; 155 156 glGetError(); 157 158 /* Create one program object to rule them all */ 159 data->program = glCreateProgramObjectARB(); 160 161 /* Create the vertex shader */ 162 data->vert_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 163 if (!CompileShader(data->vert_shader, data->vert_source)) { 164 return SDL_FALSE; 165 } 166 167 /* Create the fragment shader */ 168 data->frag_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 169 if (!CompileShader(data->frag_shader, data->frag_source)) { 170 return SDL_FALSE; 171 } 172 173 /* ... and in the darkness bind them */ 174 glAttachObjectARB(data->program, data->vert_shader); 175 glAttachObjectARB(data->program, data->frag_shader); 176 glLinkProgramARB(data->program); 177 178 /* Set up some uniform variables */ 179 glUseProgramObjectARB(data->program); 180 for (i = 0; i < num_tmus_bound; ++i) { 181 char tex_name[5]; 182 SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i); 183 location = glGetUniformLocationARB(data->program, tex_name); 184 if (location >= 0) { 185 glUniform1iARB(location, i); 186 } 187 } 188 glUseProgramObjectARB(0); 189 190 return (glGetError() == GL_NO_ERROR) ? SDL_TRUE : SDL_FALSE; 191 } 192 193 static void DestroyShaderProgram(ShaderData *data) 194 { 195 if (shaders_supported) { 196 glDeleteObjectARB(data->vert_shader); 197 glDeleteObjectARB(data->frag_shader); 198 glDeleteObjectARB(data->program); 199 } 200 } 201 202 static SDL_bool InitShaders() 203 { 204 int i; 205 206 /* Check for shader support */ 207 shaders_supported = SDL_FALSE; 208 if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") && 209 SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") && 210 SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") && 211 SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) { 212 glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); 213 glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); 214 glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); 215 glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); 216 glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB"); 217 glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); 218 glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); 219 glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); 220 glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); 221 glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); 222 glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB"); 223 glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); 224 if (glAttachObjectARB && 225 glCompileShaderARB && 226 glCreateProgramObjectARB && 227 glCreateShaderObjectARB && 228 glDeleteObjectARB && 229 glGetInfoLogARB && 230 glGetObjectParameterivARB && 231 glGetUniformLocationARB && 232 glLinkProgramARB && 233 glShaderSourceARB && 234 glUniform1iARB && 235 glUseProgramObjectARB) { 236 shaders_supported = SDL_TRUE; 237 } 238 } 239 240 if (!shaders_supported) { 241 return SDL_FALSE; 242 } 243 244 /* Compile all the shaders */ 245 for (i = 0; i < NUM_SHADERS; ++i) { 246 if (!CompileShaderProgram(&shaders[i])) { 247 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to compile shader!\n"); 248 return SDL_FALSE; 249 } 250 } 251 252 /* We're done! */ 253 return SDL_TRUE; 254 } 255 256 static void QuitShaders() 257 { 258 int i; 259 260 for (i = 0; i < NUM_SHADERS; ++i) { 261 DestroyShaderProgram(&shaders[i]); 262 } 263 } 264 265 /* Quick utility function for texture creation */ 266 static int 267 power_of_two(int input) 268 { 269 int value = 1; 270 271 while (value < input) { 272 value <<= 1; 273 } 274 return value; 275 } 276 277 GLuint 278 SDL_GL_LoadTexture(SDL_Surface * surface, GLfloat * texcoord) 279 { 280 GLuint texture; 281 int w, h; 282 SDL_Surface *image; 283 SDL_Rect area; 284 SDL_BlendMode saved_mode; 285 286 /* Use the surface width and height expanded to powers of 2 */ 287 w = power_of_two(surface->w); 288 h = power_of_two(surface->h); 289 texcoord[0] = 0.0f; /* Min X */ 290 texcoord[1] = 0.0f; /* Min Y */ 291 texcoord[2] = (GLfloat) surface->w / w; /* Max X */ 292 texcoord[3] = (GLfloat) surface->h / h; /* Max Y */ 293 294 image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, 295 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ 296 0x000000FF, 297 0x0000FF00, 0x00FF0000, 0xFF000000 298 #else 299 0xFF000000, 300 0x00FF0000, 0x0000FF00, 0x000000FF 301 #endif 302 ); 303 if (image == NULL) { 304 return 0; 305 } 306 307 /* Save the alpha blending attributes */ 308 SDL_GetSurfaceBlendMode(surface, &saved_mode); 309 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); 310 311 /* Copy the surface into the GL texture image */ 312 area.x = 0; 313 area.y = 0; 314 area.w = surface->w; 315 area.h = surface->h; 316 SDL_BlitSurface(surface, &area, image, &area); 317 318 /* Restore the alpha blending attributes */ 319 SDL_SetSurfaceBlendMode(surface, saved_mode); 320 321 /* Create an OpenGL texture for the image */ 322 glGenTextures(1, &texture); 323 glBindTexture(GL_TEXTURE_2D, texture); 324 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 326 glTexImage2D(GL_TEXTURE_2D, 327 0, 328 GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); 329 SDL_FreeSurface(image); /* No longer needed */ 330 331 return texture; 332 } 333 334 /* A general OpenGL initialization function. Sets all of the initial parameters. */ 335 void InitGL(int Width, int Height) /* We call this right after our OpenGL window is created. */ 336 { 337 GLdouble aspect; 338 339 glViewport(0, 0, Width, Height); 340 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* This Will Clear The Background Color To Black */ 341 glClearDepth(1.0); /* Enables Clearing Of The Depth Buffer */ 342 glDepthFunc(GL_LESS); /* The Type Of Depth Test To Do */ 343 glEnable(GL_DEPTH_TEST); /* Enables Depth Testing */ 344 glShadeModel(GL_SMOOTH); /* Enables Smooth Color Shading */ 345 346 glMatrixMode(GL_PROJECTION); 347 glLoadIdentity(); /* Reset The Projection Matrix */ 348 349 aspect = (GLdouble)Width / Height; 350 glOrtho(-3.0, 3.0, -3.0 / aspect, 3.0 / aspect, 0.0, 1.0); 351 352 glMatrixMode(GL_MODELVIEW); 353 } 354 355 /* The main drawing function. */ 356 void DrawGLScene(SDL_Window *window, GLuint texture, GLfloat * texcoord) 357 { 358 /* Texture coordinate lookup, to make it simple */ 359 enum { 360 MINX, 361 MINY, 362 MAXX, 363 MAXY 364 }; 365 366 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Clear The Screen And The Depth Buffer */ 367 glLoadIdentity(); /* Reset The View */ 368 369 glTranslatef(-1.5f,0.0f,0.0f); /* Move Left 1.5 Units */ 370 371 /* draw a triangle (in smooth coloring mode) */ 372 glBegin(GL_POLYGON); /* start drawing a polygon */ 373 glColor3f(1.0f,0.0f,0.0f); /* Set The Color To Red */ 374 glVertex3f( 0.0f, 1.0f, 0.0f); /* Top */ 375 glColor3f(0.0f,1.0f,0.0f); /* Set The Color To Green */ 376 glVertex3f( 1.0f,-1.0f, 0.0f); /* Bottom Right */ 377 glColor3f(0.0f,0.0f,1.0f); /* Set The Color To Blue */ 378 glVertex3f(-1.0f,-1.0f, 0.0f); /* Bottom Left */ 379 glEnd(); /* we're done with the polygon (smooth color interpolation) */ 380 381 glTranslatef(3.0f,0.0f,0.0f); /* Move Right 3 Units */ 382 383 /* Enable blending */ 384 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 385 glEnable(GL_BLEND); 386 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 387 388 /* draw a textured square (quadrilateral) */ 389 glEnable(GL_TEXTURE_2D); 390 glBindTexture(GL_TEXTURE_2D, texture); 391 glColor3f(1.0f,1.0f,1.0f); 392 if (shaders_supported) { 393 glUseProgramObjectARB(shaders[current_shader].program); 394 } 395 396 glBegin(GL_QUADS); /* start drawing a polygon (4 sided) */ 397 glTexCoord2f(texcoord[MINX], texcoord[MINY]); 398 glVertex3f(-1.0f, 1.0f, 0.0f); /* Top Left */ 399 glTexCoord2f(texcoord[MAXX], texcoord[MINY]); 400 glVertex3f( 1.0f, 1.0f, 0.0f); /* Top Right */ 401 glTexCoord2f(texcoord[MAXX], texcoord[MAXY]); 402 glVertex3f( 1.0f,-1.0f, 0.0f); /* Bottom Right */ 403 glTexCoord2f(texcoord[MINX], texcoord[MAXY]); 404 glVertex3f(-1.0f,-1.0f, 0.0f); /* Bottom Left */ 405 glEnd(); /* done with the polygon */ 406 407 if (shaders_supported) { 408 glUseProgramObjectARB(0); 409 } 410 glDisable(GL_TEXTURE_2D); 411 412 /* swap buffers to display, since we're double buffered. */ 413 SDL_GL_SwapWindow(window); 414 } 415 416 int main(int argc, char **argv) 417 { 418 int done; 419 SDL_Window *window; 420 SDL_Surface *surface; 421 GLuint texture; 422 GLfloat texcoords[4]; 423 424 /* Enable standard application logging */ 425 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); 426 427 /* Initialize SDL for video output */ 428 if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { 429 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to initialize SDL: %s\n", SDL_GetError()); 430 exit(1); 431 } 432 433 /* Create a 640x480 OpenGL screen */ 434 window = SDL_CreateWindow( "Shader Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL ); 435 if ( !window ) { 436 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL window: %s\n", SDL_GetError()); 437 SDL_Quit(); 438 exit(2); 439 } 440 441 if ( !SDL_GL_CreateContext(window)) { 442 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL context: %s\n", SDL_GetError()); 443 SDL_Quit(); 444 exit(2); 445 } 446 447 surface = SDL_LoadBMP("icon.bmp"); 448 if ( ! surface ) { 449 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load icon.bmp: %s\n", SDL_GetError()); 450 SDL_Quit(); 451 exit(3); 452 } 453 texture = SDL_GL_LoadTexture(surface, texcoords); 454 SDL_FreeSurface(surface); 455 456 /* Loop, drawing and checking events */ 457 InitGL(640, 480); 458 if (InitShaders()) { 459 SDL_Log("Shaders supported, press SPACE to cycle them.\n"); 460 } else { 461 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shaders not supported!\n"); 462 } 463 done = 0; 464 while ( ! done ) { 465 DrawGLScene(window, texture, texcoords); 466 467 /* This could go in a separate function */ 468 { SDL_Event event; 469 while ( SDL_PollEvent(&event) ) { 470 if ( event.type == SDL_QUIT ) { 471 done = 1; 472 } 473 if ( event.type == SDL_KEYDOWN ) { 474 if ( event.key.keysym.sym == SDLK_SPACE ) { 475 current_shader = (current_shader + 1) % NUM_SHADERS; 476 } 477 if ( event.key.keysym.sym == SDLK_ESCAPE ) { 478 done = 1; 479 } 480 } 481 } 482 } 483 } 484 QuitShaders(); 485 SDL_Quit(); 486 return 1; 487 } 488 489 #else /* HAVE_OPENGL */ 490 491 int 492 main(int argc, char *argv[]) 493 { 494 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No OpenGL support on this system\n"); 495 return 1; 496 } 497 498 #endif /* HAVE_OPENGL */ 499 500 /* vi: set ts=4 sw=4 expandtab: */ 501