Atlas - testshader.c
Home / ext / SDL / test Lines: 49 | Size: 17438 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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:"); 149 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", source); 150 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", info); 151 SDL_free(info); 152 } 153 return false; 154 } else { 155 return true; 156 } 157} 158 159static bool LinkProgram(ShaderData *data) 160{ 161 GLint status = 0; 162 163 pglAttachObjectARB(data->program, data->vert_shader); 164 pglAttachObjectARB(data->program, data->frag_shader); 165 pglLinkProgramARB(data->program); 166 167 pglGetObjectParameterivARB(data->program, GL_OBJECT_LINK_STATUS_ARB, &status); 168 if (status == 0) { 169 GLint length = 0; 170 char *info; 171 172 pglGetObjectParameterivARB(data->program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); 173 info = (char *)SDL_malloc((size_t)length + 1); 174 if (!info) { 175 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!"); 176 } else { 177 pglGetInfoLogARB(data->program, length, NULL, info); 178 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to link program:"); 179 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", info); 180 SDL_free(info); 181 } 182 return false; 183 } else { 184 return true; 185 } 186} 187 188static bool CompileShaderProgram(ShaderData *data) 189{ 190 const int num_tmus_bound = 4; 191 int i; 192 GLint location; 193 194 glGetError(); 195 196 /* Create one program object to rule them all */ 197 data->program = pglCreateProgramObjectARB(); 198 199 /* Create the vertex shader */ 200 data->vert_shader = pglCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 201 if (!CompileShader(data->vert_shader, data->vert_source)) { 202 return false; 203 } 204 205 /* Create the fragment shader */ 206 data->frag_shader = pglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 207 if (!CompileShader(data->frag_shader, data->frag_source)) { 208 return false; 209 } 210 211 /* ... and in the darkness bind them */ 212 if (!LinkProgram(data)) { 213 return false; 214 } 215 216 /* Set up some uniform variables */ 217 pglUseProgramObjectARB(data->program); 218 for (i = 0; i < num_tmus_bound; ++i) { 219 char tex_name[5]; 220 (void)SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i); 221 location = pglGetUniformLocationARB(data->program, tex_name); 222 if (location >= 0) { 223 pglUniform1iARB(location, i); 224 } 225 } 226 pglUseProgramObjectARB(0); 227 228 return (glGetError() == GL_NO_ERROR); 229} 230 231static void DestroyShaderProgram(ShaderData *data) 232{ 233 if (shaders_supported) { 234 pglDeleteObjectARB(data->vert_shader); 235 pglDeleteObjectARB(data->frag_shader); 236 pglDeleteObjectARB(data->program); 237 } 238} 239 240static bool InitShaders(void) 241{ 242 int i; 243 244 /* Check for shader support */ 245 shaders_supported = false; 246 if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") && 247 SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") && 248 SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") && 249 SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) { 250 pglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)SDL_GL_GetProcAddress("glAttachObjectARB"); 251 pglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)SDL_GL_GetProcAddress("glCompileShaderARB"); 252 pglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glCreateProgramObjectARB"); 253 pglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)SDL_GL_GetProcAddress("glCreateShaderObjectARB"); 254 pglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)SDL_GL_GetProcAddress("glDeleteObjectARB"); 255 pglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)SDL_GL_GetProcAddress("glGetInfoLogARB"); 256 pglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)SDL_GL_GetProcAddress("glGetObjectParameterivARB"); 257 pglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)SDL_GL_GetProcAddress("glGetUniformLocationARB"); 258 pglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)SDL_GL_GetProcAddress("glLinkProgramARB"); 259 pglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)SDL_GL_GetProcAddress("glShaderSourceARB"); 260 pglUniform1iARB = (PFNGLUNIFORM1IARBPROC)SDL_GL_GetProcAddress("glUniform1iARB"); 261 pglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glUseProgramObjectARB"); 262 if (pglAttachObjectARB && 263 pglCompileShaderARB && 264 pglCreateProgramObjectARB && 265 pglCreateShaderObjectARB && 266 pglDeleteObjectARB && 267 pglGetInfoLogARB && 268 pglGetObjectParameterivARB && 269 pglGetUniformLocationARB && 270 pglLinkProgramARB && 271 pglShaderSourceARB && 272 pglUniform1iARB && 273 pglUseProgramObjectARB) { 274 shaders_supported = true; 275 } 276 } 277 278 if (!shaders_supported) { 279 return false; 280 } 281 282 /* Compile all the shaders */ 283 for (i = 0; i < NUM_SHADERS; ++i) { 284 if (!CompileShaderProgram(&shaders[i])) { 285 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to compile shader!"); 286 return false; 287 } 288 } 289 290 /* We're done! */ 291 return true; 292} 293 294static void QuitShaders(void) 295{ 296 int i; 297 298 for (i = 0; i < NUM_SHADERS; ++i) { 299 DestroyShaderProgram(&shaders[i]); 300 } 301} 302 303/* Quick utility function for texture creation */ 304static int 305power_of_two(int input) 306{ 307 int value = 1; 308 309 while (value < input) { 310 value <<= 1; 311 } 312 return value; 313} 314 315static GLuint 316SDL_GL_LoadTexture(SDL_Surface *surface, GLfloat *texcoord) 317{ 318 GLuint texture; 319 int w, h; 320 SDL_Surface *image; 321 SDL_Rect area; 322 SDL_BlendMode saved_mode; 323 324 /* Use the surface width and height expanded to powers of 2 */ 325 w = power_of_two(surface->w); 326 h = power_of_two(surface->h); 327 texcoord[0] = 0.0f; /* Min X */ 328 texcoord[1] = 0.0f; /* Min Y */ 329 texcoord[2] = (GLfloat)surface->w / w; /* Max X */ 330 texcoord[3] = (GLfloat)surface->h / h; /* Max Y */ 331 332 image = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_RGBA32); 333 if (!image) { 334 return 0; 335 } 336 337 /* Save the alpha blending attributes */ 338 SDL_GetSurfaceBlendMode(surface, &saved_mode); 339 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); 340 341 /* Copy the surface into the GL texture image */ 342 area.x = 0; 343 area.y = 0; 344 area.w = surface->w; 345 area.h = surface->h; 346 SDL_BlitSurface(surface, &area, image, &area); 347 348 /* Restore the alpha blending attributes */ 349 SDL_SetSurfaceBlendMode(surface, saved_mode); 350 351 /* Create an OpenGL texture for the image */ 352 glGenTextures(1, &texture); 353 glBindTexture(GL_TEXTURE_2D, texture); 354 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 355 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 356 glTexImage2D(GL_TEXTURE_2D, 357 0, 358 GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); 359 SDL_DestroySurface(image); /* No longer needed */ 360 361 return texture; 362} 363 364/* A general OpenGL initialization function. Sets all of the initial parameters. */ 365static void InitGL(int Width, int Height) /* We call this right after our OpenGL window is created. */ 366{ 367 GLdouble aspect; 368 369 glViewport(0, 0, Width, Height); 370 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* This Will Clear The Background Color To Black */ 371 glClearDepth(1.0); /* Enables Clearing Of The Depth Buffer */ 372 glDepthFunc(GL_LESS); /* The Type Of Depth Test To Do */ 373 glEnable(GL_DEPTH_TEST); /* Enables Depth Testing */ 374 glShadeModel(GL_SMOOTH); /* Enables Smooth Color Shading */ 375 376 glMatrixMode(GL_PROJECTION); 377 glLoadIdentity(); /* Reset The Projection Matrix */ 378 379 aspect = (GLdouble)Width / Height; 380 glOrtho(-3.0, 3.0, -3.0 / aspect, 3.0 / aspect, 0.0, 1.0); 381 382 glMatrixMode(GL_MODELVIEW); 383} 384 385/* The main drawing function. */ 386static void DrawGLScene(SDL_Window *window, GLuint texture, GLfloat *texcoord) 387{ 388 /* Texture coordinate lookup, to make it simple */ 389 enum 390 { 391 MINX, 392 MINY, 393 MAXX, 394 MAXY 395 }; 396 397 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Clear The Screen And The Depth Buffer */ 398 glLoadIdentity(); /* Reset The View */ 399 400 glTranslatef(-1.5f, 0.0f, 0.0f); /* Move Left 1.5 Units */ 401 402 /* draw a triangle (in smooth coloring mode) */ 403 glBegin(GL_POLYGON); /* start drawing a polygon */ 404 glColor3f(1.0f, 0.0f, 0.0f); /* Set The Color To Red */ 405 glVertex3f(0.0f, 1.0f, 0.0f); /* Top */ 406 glColor3f(0.0f, 1.0f, 0.0f); /* Set The Color To Green */ 407 glVertex3f(1.0f, -1.0f, 0.0f); /* Bottom Right */ 408 glColor3f(0.0f, 0.0f, 1.0f); /* Set The Color To Blue */ 409 glVertex3f(-1.0f, -1.0f, 0.0f); /* Bottom Left */ 410 glEnd(); /* we're done with the polygon (smooth color interpolation) */ 411 412 glTranslatef(3.0f, 0.0f, 0.0f); /* Move Right 3 Units */ 413 414 /* Enable blending */ 415 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 416 glEnable(GL_BLEND); 417 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 418 419 /* draw a textured square (quadrilateral) */ 420 glEnable(GL_TEXTURE_2D); 421 glBindTexture(GL_TEXTURE_2D, texture); 422 glColor3f(1.0f, 1.0f, 1.0f); 423 if (shaders_supported) { 424 pglUseProgramObjectARB(shaders[current_shader].program); 425 } 426 427 glBegin(GL_QUADS); /* start drawing a polygon (4 sided) */ 428 glTexCoord2f(texcoord[MINX], texcoord[MINY]); 429 glVertex3f(-1.0f, 1.0f, 0.0f); /* Top Left */ 430 glTexCoord2f(texcoord[MAXX], texcoord[MINY]); 431 glVertex3f(1.0f, 1.0f, 0.0f); /* Top Right */ 432 glTexCoord2f(texcoord[MAXX], texcoord[MAXY]); 433 glVertex3f(1.0f, -1.0f, 0.0f); /* Bottom Right */ 434 glTexCoord2f(texcoord[MINX], texcoord[MAXY]); 435 glVertex3f(-1.0f, -1.0f, 0.0f); /* Bottom Left */ 436 glEnd(); /* done with the polygon */ 437 438 if (shaders_supported) { 439 pglUseProgramObjectARB(0); 440 } 441 glDisable(GL_TEXTURE_2D); 442 443 /* swap buffers to display, since we're double buffered. */ 444 SDL_GL_SwapWindow(window); 445} 446 447int main(int argc, char **argv) 448{ 449 int i; 450 int done; 451 SDL_Window *window; 452 char *filename = NULL; 453 SDL_Surface *surface; 454 GLuint texture; 455 GLfloat texcoords[4]; 456 SDLTest_CommonState *state; 457 458 /* Initialize test framework */ 459 state = SDLTest_CommonCreateState(argv, 0); 460 if (!state) { 461 return 1; 462 } 463 464 /* Parse commandline */ 465 for (i = 1; i < argc;) { 466 int consumed; 467 468 consumed = SDLTest_CommonArg(state, i); 469 if (!consumed) { 470 if (!filename) { 471 filename = argv[i]; 472 consumed = 1; 473 } 474 } 475 if (consumed <= 0) { 476 static const char *options[] = { "[icon.png]", NULL }; 477 SDLTest_CommonLogUsage(state, argv[0], options); 478 exit(1); 479 } 480 481 i += consumed; 482 } 483 484 /* Initialize SDL for video output */ 485 if (!SDL_Init(SDL_INIT_VIDEO)) { 486 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to initialize SDL: %s", SDL_GetError()); 487 exit(1); 488 } 489 490 /* Create a 640x480 OpenGL screen */ 491 window = SDL_CreateWindow("Shader Demo", 640, 480, SDL_WINDOW_OPENGL); 492 if (!window) { 493 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL window: %s", SDL_GetError()); 494 SDL_Quit(); 495 exit(2); 496 } 497 498 if (!SDL_GL_CreateContext(window)) { 499 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL context: %s", SDL_GetError()); 500 SDL_Quit(); 501 exit(2); 502 } 503 504 filename = GetResourceFilename(NULL, "icon.png"); 505 surface = SDL_LoadPNG(filename); 506 SDL_free(filename); 507 508 if (!surface) { 509 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load icon.png: %s", SDL_GetError()); 510 SDL_Quit(); 511 exit(3); 512 } 513 texture = SDL_GL_LoadTexture(surface, texcoords); 514 SDL_DestroySurface(surface); 515 516 /* Loop, drawing and checking events */ 517 InitGL(640, 480); 518 if (InitShaders()) { 519 SDL_Log("Shaders supported, press SPACE to cycle them."); 520 } else { 521 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shaders not supported!"); 522 } 523 done = 0; 524 while (!done) { 525 DrawGLScene(window, texture, texcoords); 526 527 /* This could go in a separate function */ 528 { 529 SDL_Event event; 530 while (SDL_PollEvent(&event)) { 531 if (event.type == SDL_EVENT_QUIT) { 532 done = 1; 533 } 534 if (event.type == SDL_EVENT_KEY_DOWN) { 535 if (event.key.key == SDLK_SPACE) { 536 current_shader = (current_shader + 1) % NUM_SHADERS; 537 } 538 if (event.key.key == SDLK_ESCAPE) { 539 done = 1; 540 } 541 } 542 } 543 } 544 } 545 QuitShaders(); 546 SDL_Quit(); 547 SDLTest_CommonDestroyState(state); 548 return 1; 549} 550 551#else /* HAVE_OPENGL */ 552 553int main(int argc, char *argv[]) 554{ 555 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No OpenGL support on this system"); 556 return 1; 557} 558 559#endif /* HAVE_OPENGL */ 560[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.