|
|
- /*
- Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely.
- */
- /* 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
- {
- GLhandleARB program;
- GLhandleARB vert_shader;
- GLhandleARB 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(GLhandleARB shader, const char *source)
- {
- GLint status = 0;
-
- glShaderSourceARB(shader, 1, &source, NULL);
- glCompileShaderARB(shader);
- glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
- if (status == 0) {
- GLint length = 0;
- char *info;
-
- glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
- info = (char *)SDL_malloc((size_t)length + 1);
- if (info == NULL) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
- } else {
- glGetInfoLogARB(shader, length, NULL, info);
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile shader:\n%s\n%s", source, info);
- SDL_free(info);
- }
- return SDL_FALSE;
- } else {
- return SDL_TRUE;
- }
- }
-
- static SDL_bool LinkProgram(ShaderData *data)
- {
- GLint status = 0;
-
- glAttachObjectARB(data->program, data->vert_shader);
- glAttachObjectARB(data->program, data->frag_shader);
- glLinkProgramARB(data->program);
-
- glGetObjectParameterivARB(data->program, GL_OBJECT_LINK_STATUS_ARB, &status);
- if (status == 0) {
- GLint length = 0;
- char *info;
-
- glGetObjectParameterivARB(data->program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
- info = (char *)SDL_malloc((size_t)length + 1);
- if (info == NULL) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
- } else {
- glGetInfoLogARB(data->program, length, NULL, info);
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to link program:\n%s", info);
- SDL_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 */
- if (!LinkProgram(data)) {
- return SDL_FALSE;
- }
-
- /* Set up some uniform variables */
- glUseProgramObjectARB(data->program);
- for (i = 0; i < num_tmus_bound; ++i) {
- char tex_name[5];
- (void)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) ? SDL_TRUE : SDL_FALSE;
- }
-
- 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])) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "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;
- SDL_BlendMode saved_mode;
-
- /* 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 */
- SDL_GetSurfaceBlendMode(surface, &saved_mode);
- SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
-
- /* 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 */
- SDL_SetSurfaceBlendMode(surface, saved_mode);
-
- /* 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(SDL_Window *window, 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_SwapWindow(window);
- }
-
- int main(int argc, char **argv)
- {
- int done;
- SDL_Window *window;
- SDL_Surface *surface;
- GLuint texture;
- GLfloat texcoords[4];
-
- /* Enable standard application logging */
- SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
-
- /* Initialize SDL for video output */
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to initialize SDL: %s\n", SDL_GetError());
- exit(1);
- }
-
- /* Create a 640x480 OpenGL screen */
- window = SDL_CreateWindow("Shader Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL);
- if (window == NULL) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL window: %s\n", SDL_GetError());
- SDL_Quit();
- exit(2);
- }
-
- if (!SDL_GL_CreateContext(window)) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL context: %s\n", SDL_GetError());
- SDL_Quit();
- exit(2);
- }
-
- surface = SDL_LoadBMP("icon.bmp");
- if (surface == NULL) {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "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()) {
- SDL_Log("Shaders supported, press SPACE to cycle them.\n");
- } else {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shaders not supported!\n");
- }
- done = 0;
- while (!done) {
- DrawGLScene(window, 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[])
- {
- SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No OpenGL support on this system\n");
- return 1;
- }
-
- #endif /* HAVE_OPENGL */
-
- /* vi: set ts=4 sw=4 expandtab: */
|