From 76e3d99945540ad4080b9bdd219decb618b8c918 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 8 Feb 2011 22:11:16 -0800 Subject: [PATCH] Added a simple GLSL example using SDL --- test/Makefile.in | 54 +++++- test/testshader.c | 483 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 test/testshader.c diff --git a/test/Makefile.in b/test/Makefile.in index f1969286a..a85d4c2b9 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -7,7 +7,56 @@ EXE = @EXE@ CFLAGS = @CFLAGS@ LIBS = @LIBS@ -TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testalpha$(EXE) testatomic$(EXE) testaudioinfo$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcursor$(EXE) testdraw2$(EXE) testdyngles$(EXE) testdyngl$(EXE) testerror$(EXE) testfile$(EXE) testfill$(EXE) testgamma$(EXE) testgl2$(EXE) testgles$(EXE) testgl$(EXE) testhaptic$(EXE) testhread$(EXE) testiconv$(EXE) testime$(EXE) testintersections$(EXE) testjoystick$(EXE) testkeys$(EXE) testloadso$(EXE) testlock$(EXE) testmultiaudio$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testplatform$(EXE) testpower$(EXE) testresample$(EXE) testsem$(EXE) testshape$(EXE) testsprite2$(EXE) testsprite$(EXE) testspriteminimal$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm2$(EXE) testwm$(EXE) threadwin$(EXE) torturethread$(EXE) testgesture$(EXE) +TARGETS = \ + checkkeys$(EXE) \ + graywin$(EXE) \ + loopwave$(EXE) \ + testalpha$(EXE) \ + testatomic$(EXE) \ + testaudioinfo$(EXE) \ + testbitmap$(EXE) \ + testblitspeed$(EXE) \ + testcursor$(EXE) \ + testdraw2$(EXE) \ + testdyngl$(EXE) \ + testdyngles$(EXE) \ + testerror$(EXE) \ + testfile$(EXE) \ + testfill$(EXE) \ + testgamma$(EXE) \ + testgesture$(EXE) \ + testgl$(EXE) \ + testgl2$(EXE) \ + testgles$(EXE) \ + testhaptic$(EXE) \ + testhread$(EXE) \ + testiconv$(EXE) \ + testime$(EXE) \ + testintersections$(EXE) \ + testjoystick$(EXE) \ + testkeys$(EXE) \ + testloadso$(EXE) \ + testlock$(EXE) \ + testmultiaudio$(EXE) \ + testoverlay$(EXE) \ + testoverlay2$(EXE) \ + testplatform$(EXE) \ + testpower$(EXE) \ + testresample$(EXE) \ + testsem$(EXE) \ + testshader$(EXE) \ + testshape$(EXE) \ + testsprite$(EXE) \ + testsprite2$(EXE) \ + testspriteminimal$(EXE) \ + testtimer$(EXE) \ + testver$(EXE) \ + testvidinfo$(EXE) \ + testwin$(EXE) \ + testwm$(EXE) \ + testwm2$(EXE) \ + threadwin$(EXE) \ + torturethread$(EXE) \ all: Makefile $(TARGETS) @@ -107,6 +156,9 @@ testplatform$(EXE): $(srcdir)/testplatform.c testsem$(EXE): $(srcdir)/testsem.c $(CC) -o $@ $? $(CFLAGS) $(LIBS) +testshader$(EXE): $(srcdir)/testshader.c + $(CC) -o $@ $(srcdir)/testshader.c $(CFLAGS) $(LIBS) @GLLIB@ @MATHLIB@ + testshape$(EXE): $(srcdir)/testshape.c $(CC) -o $@ $? -std=c99 $(CFLAGS) $(LIBS) diff --git a/test/testshader.c b/test/testshader.c new file mode 100644 index 000000000..cb1149183 --- /dev/null +++ b/test/testshader.c @@ -0,0 +1,483 @@ +/* This is a simple example of using GLSL shaders with SDL */ + +#include "SDL.h" + +#ifdef HAVE_OPENGL + +#include "SDL_opengl.h" + + +static SDL_bool shaders_supported; +static int current_shader = 0; + +enum { + SHADER_COLOR, + SHADER_TEXTURE, + SHADER_TEXCOORDS, + NUM_SHADERS +}; + +typedef struct { + GLuint program; + GLuint vert_shader; + GLuint frag_shader; + const char *vert_source; + const char *frag_source; +} ShaderData; + +static ShaderData shaders[NUM_SHADERS] = { + + /* SHADER_COLOR */ + { 0, 0, 0, + /* vertex shader */ +"varying vec4 v_color;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +" v_color = gl_Color;\n" +"}", + /* fragment shader */ +"varying vec4 v_color;\n" +"\n" +"void main()\n" +"{\n" +" gl_FragColor = v_color;\n" +"}" + }, + + /* SHADER_TEXTURE */ + { 0, 0, 0, + /* vertex shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +" v_color = gl_Color;\n" +" v_texCoord = vec2(gl_MultiTexCoord0);\n" +"}", + /* fragment shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"uniform sampler2D tex0;\n" +"\n" +"void main()\n" +"{\n" +" gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n" +"}" + }, + + /* SHADER_TEXCOORDS */ + { 0, 0, 0, + /* vertex shader */ +"varying vec2 v_texCoord;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +" v_texCoord = vec2(gl_MultiTexCoord0);\n" +"}", + /* fragment shader */ +"varying vec2 v_texCoord;\n" +"\n" +"void main()\n" +"{\n" +" vec4 color;\n" +" vec2 delta;\n" +" float dist;\n" +"\n" +" delta = vec2(0.5, 0.5) - v_texCoord;\n" +" dist = dot(delta, delta);\n" +"\n" +" color.r = v_texCoord.x;\n" +" color.g = v_texCoord.x * v_texCoord.y;\n" +" color.b = v_texCoord.y;\n" +" color.a = 1.0 - (dist * 4.0);\n" +" gl_FragColor = color;\n" +"}" + }, +}; + +static PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +static PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +static PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +static PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +static PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +static PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +static PFNGLUNIFORM1IARBPROC glUniform1iARB; +static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; + +static SDL_bool CompileShader(GLenum shader, const char *source) +{ + GLint status; + + glShaderSourceARB(shader, 1, &source, NULL); + glCompileShaderARB(shader); + glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); + if (status == 0) { + GLint length; + char *info; + + glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); + info = SDL_stack_alloc(char, length+1); + glGetInfoLogARB(shader, length, NULL, info); + fprintf(stderr, "Failed to compile shader:\n%s\n%s", source, info); + SDL_stack_free(info); + + return SDL_FALSE; + } else { + return SDL_TRUE; + } +} + +static SDL_bool CompileShaderProgram(ShaderData *data) +{ + const int num_tmus_bound = 4; + int i; + GLint location; + + glGetError(); + + /* Create one program object to rule them all */ + data->program = glCreateProgramObjectARB(); + + /* Create the vertex shader */ + data->vert_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + if (!CompileShader(data->vert_shader, data->vert_source)) { + return SDL_FALSE; + } + + /* Create the fragment shader */ + data->frag_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + if (!CompileShader(data->frag_shader, data->frag_source)) { + return SDL_FALSE; + } + + /* ... and in the darkness bind them */ + glAttachObjectARB(data->program, data->vert_shader); + glAttachObjectARB(data->program, data->frag_shader); + glLinkProgramARB(data->program); + + /* Set up some uniform variables */ + glUseProgramObjectARB(data->program); + for (i = 0; i < num_tmus_bound; ++i) { + char tex_name[5]; + SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i); + location = glGetUniformLocationARB(data->program, tex_name); + if (location >= 0) { + glUniform1iARB(location, i); + } + } + glUseProgramObjectARB(0); + + return (glGetError() == GL_NO_ERROR); +} + +static void DestroyShaderProgram(ShaderData *data) +{ + glDeleteObjectARB(data->vert_shader); + glDeleteObjectARB(data->frag_shader); + glDeleteObjectARB(data->program); +} + +static SDL_bool InitShaders() +{ + int i; + + /* Check for shader support */ + shaders_supported = SDL_FALSE; + if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") && + SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") && + SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") && + SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) { + glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); + glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); + glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); + glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); + glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB"); + glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); + glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); + glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); + glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); + glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); + glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB"); + glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); + if (glAttachObjectARB && + glCompileShaderARB && + glCreateProgramObjectARB && + glCreateShaderObjectARB && + glDeleteObjectARB && + glGetInfoLogARB && + glGetObjectParameterivARB && + glGetUniformLocationARB && + glLinkProgramARB && + glShaderSourceARB && + glUniform1iARB && + glUseProgramObjectARB) { + shaders_supported = SDL_TRUE; + } + } + + if (!shaders_supported) { + return SDL_FALSE; + } + + /* Compile all the shaders */ + for (i = 0; i < NUM_SHADERS; ++i) { + if (!CompileShaderProgram(&shaders[i])) { + fprintf(stderr, "Unable to compile shader!\n"); + return SDL_FALSE; + } + } + + /* We're done! */ + return SDL_TRUE; +} + +static void QuitShaders() +{ + int i; + + for (i = 0; i < NUM_SHADERS; ++i) { + DestroyShaderProgram(&shaders[i]); + } +} + +/* Quick utility function for texture creation */ +static int +power_of_two(int input) +{ + int value = 1; + + while (value < input) { + value <<= 1; + } + return value; +} + +GLuint +SDL_GL_LoadTexture(SDL_Surface * surface, GLfloat * texcoord) +{ + GLuint texture; + int w, h; + SDL_Surface *image; + SDL_Rect area; + Uint32 saved_flags; + Uint8 saved_alpha; + + /* Use the surface width and height expanded to powers of 2 */ + w = power_of_two(surface->w); + h = power_of_two(surface->h); + texcoord[0] = 0.0f; /* Min X */ + texcoord[1] = 0.0f; /* Min Y */ + texcoord[2] = (GLfloat) surface->w / w; /* Max X */ + texcoord[3] = (GLfloat) surface->h / h; /* Max Y */ + + image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ + 0x000000FF, + 0x0000FF00, 0x00FF0000, 0xFF000000 +#else + 0xFF000000, + 0x00FF0000, 0x0000FF00, 0x000000FF +#endif + ); + if (image == NULL) { + return 0; + } + + /* Save the alpha blending attributes */ + saved_flags = surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK); + SDL_GetSurfaceAlphaMod(surface, &saved_alpha); + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) { + SDL_SetAlpha(surface, 0, 0); + } + + /* Copy the surface into the GL texture image */ + area.x = 0; + area.y = 0; + area.w = surface->w; + area.h = surface->h; + SDL_BlitSurface(surface, &area, image, &area); + + /* Restore the alpha blending attributes */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) { + SDL_SetAlpha(surface, saved_flags, saved_alpha); + } + + /* Create an OpenGL texture for the image */ + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); + SDL_FreeSurface(image); /* No longer needed */ + + return texture; +} + +/* A general OpenGL initialization function. Sets all of the initial parameters. */ +void InitGL(int Width, int Height) // We call this right after our OpenGL window is created. +{ + GLdouble aspect; + + glViewport(0, 0, Width, Height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black + glClearDepth(1.0); // Enables Clearing Of The Depth Buffer + glDepthFunc(GL_LESS); // The Type Of Depth Test To Do + glEnable(GL_DEPTH_TEST); // Enables Depth Testing + glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // Reset The Projection Matrix + + aspect = (GLdouble)Width / Height; + glOrtho(-3.0, 3.0, -3.0 / aspect, 3.0 / aspect, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); +} + +/* The main drawing function. */ +void DrawGLScene(GLuint texture, GLfloat * texcoord) +{ + /* Texture coordinate lookup, to make it simple */ + enum { + MINX, + MINY, + MAXX, + MAXY + }; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer + glLoadIdentity(); // Reset The View + + glTranslatef(-1.5f,0.0f,0.0f); // Move Left 1.5 Units + + // draw a triangle (in smooth coloring mode) + glBegin(GL_POLYGON); // start drawing a polygon + glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red + glVertex3f( 0.0f, 1.0f, 0.0f); // Top + glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green + glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right + glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue + glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left + glEnd(); // we're done with the polygon (smooth color interpolation) + + glTranslatef(3.0f,0.0f,0.0f); // Move Right 3 Units + + // Enable blending + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // draw a textured square (quadrilateral) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + glColor3f(1.0f,1.0f,1.0f); + if (shaders_supported) { + glUseProgramObjectARB(shaders[current_shader].program); + } + + glBegin(GL_QUADS); // start drawing a polygon (4 sided) + glTexCoord2f(texcoord[MINX], texcoord[MINY]); + glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left + glTexCoord2f(texcoord[MAXX], texcoord[MINY]); + glVertex3f( 1.0f, 1.0f, 0.0f); // Top Right + glTexCoord2f(texcoord[MAXX], texcoord[MAXY]); + glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right + glTexCoord2f(texcoord[MINX], texcoord[MAXY]); + glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left + glEnd(); // done with the polygon + + if (shaders_supported) { + glUseProgramObjectARB(0); + } + glDisable(GL_TEXTURE_2D); + + // swap buffers to display, since we're double buffered. + SDL_GL_SwapBuffers(); +} + +int main(int argc, char **argv) +{ + int done; + SDL_Surface *surface; + GLuint texture; + GLfloat texcoords[4]; + + /* Initialize SDL for video output */ + if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + /* Create a 640x480 OpenGL screen */ + if ( SDL_SetVideoMode(640, 480, 0, SDL_OPENGL) == NULL ) { + fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError()); + SDL_Quit(); + exit(2); + } + + /* Set the title bar in environments that support it */ + SDL_WM_SetCaption("Shader Demo", NULL); + + surface = SDL_LoadBMP("icon.bmp"); + if ( ! surface ) { + fprintf(stderr, "Unable to load icon.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + exit(3); + } + texture = SDL_GL_LoadTexture(surface, texcoords); + SDL_FreeSurface(surface); + + /* Loop, drawing and checking events */ + InitGL(640, 480); + if (InitShaders()) { + printf("Shaders supported, press SPACE to cycle them.\n"); + } else { + printf("Shaders not supported!\n"); + } + done = 0; + while ( ! done ) { + DrawGLScene(texture, texcoords); + + /* This could go in a separate function */ + { SDL_Event event; + while ( SDL_PollEvent(&event) ) { + if ( event.type == SDL_QUIT ) { + done = 1; + } + if ( event.type == SDL_KEYDOWN ) { + if ( event.key.keysym.sym == SDLK_SPACE ) { + current_shader = (current_shader + 1) % NUM_SHADERS; + } + if ( event.key.keysym.sym == SDLK_ESCAPE ) { + done = 1; + } + } + } + } + } + QuitShaders(); + SDL_Quit(); + return 1; +} + +#else /* HAVE_OPENGL */ + +int +main(int argc, char *argv[]) +{ + printf("No OpenGL support on this system\n"); + return 1; +} + +#endif /* HAVE_OPENGL */