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