Simple Directmedia Layer
fork

Configure Feed

Select the types of activity you want to include in your feed.

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