SDL  2.0
testshader.c
Go to the documentation of this file.
1 /*
2  Copyright (C) 1997-2018 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 "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,
29 };
30 
31 typedef struct {
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;
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);
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);
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 
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 
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 */
387 
388  /* draw a textured square (quadrilateral) */
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  }
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;
421  GLuint texture;
422  GLfloat texcoords[4];
423 
424  /* Enable standard application logging */
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 */
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: */