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