test/testshader.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 08 Feb 2011 22:11:16 -0800
changeset 5232 74bc160186a8
child 5236 c7be6ca3a0b8
permissions -rw-r--r--
Added a simple GLSL example using SDL
     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 */