Simple Directmedia Layer
fork
Configure Feed
Select the types of activity you want to include in your feed.
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 */