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