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