/* 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) { if (shaders_supported) { 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 */ /* vi: set ts=4 sw=4 expandtab: */