Lesson 16: Waving flag with OpenGL and GLSL
With the newer versions of OpenGL we are able to manipulate the OpenGL Rendering Pipeline. This gives a lot of power to the developers to do graphical manipulations and visual effects. So far we have used the classical method to display 3D graphics.This (simplified) OpenGL pipeline is:
Essentially what we will do is upload small programs into the graphics card chip (GPU). These small programs are called ‘shaders’. For example, we will have a ‘vertex shader’ to manipulate vertexes and a ‘fragment shader’ to manipulate the image data. Both will directly manipulate the OpenGL pipeline. These programs are written in the language GLSL (abrev. OpenGL Shading Language). In this tutorial we’ll draw a waving flag. First we will display a set of quads which have a fixed x and y position for each vertex.
/* Draw here a plain surface */ float TS = 1.0 / 40; //0.025; glBegin(GL_QUADS); for (i = -20; iThe variables TS, startX and startY are required to fit the texture. Texture coordinates are mapped between (0.0 and 1.0). The total size of all quads will be 40 (e.g. 20 in negative and 20 in positive direction). We will then create a vertex shader (program in the GPU to manipulate vertex coordinates)
We then create a custom vertex shader, where we manipulate the Z coordinate of each vertex according to the cosine or sine function, which are somewhat similar to the position of a waving flag:
We define our vertex shader as:
/* Vertex shader */ uniform float waveTime; uniform float waveWidth; uniform float waveHeight; void main(void) { vec4 v = vec4(gl_Vertex); v.z = cos(waveWidth * v.x + waveTime) * waveHeight; gl_Position = gl_ModelViewProjectionMatrix * v; gl_TexCoord[0] = gl_MultiTexCoord0; }And fragment shader as:
/* Fragment shader */ uniform sampler2D color_texture; void main() { // Set the output color of our current pixel gl_FragColor = texture2D(color_texture, gl_TexCoord[0].st); }We should then have this result (in wireframe mode):
Finally the program code is:
/* * OpenGL Water Effect. * It uses GLSL to create a wavey effect. * * https://talkera.org/opengl/ * http://openglsamples.sf.net/ * */ #include#include #include #include void printLog(GLuint obj) { int infologLength = 0; char infoLog[1024]; if (glIsShader(obj)) glGetShaderInfoLog(obj, 1024, &infologLength, infoLog); else glGetProgramInfoLog(obj, 1024, &infologLength, infoLog); if (infologLength > 0) printf("%s\n", infoLog); } char *file2string(const char *path) { FILE *fd; long len, r; char *str; if (!(fd = fopen(path, "r"))) { fprintf(stderr, "Can't open file '%s' for reading\n", path); return NULL; } fseek(fd, 0, SEEK_END); len = ftell(fd); printf("File '%s' is %ld long\n", path, len); fseek(fd, 0, SEEK_SET); if (!(str = malloc(len * sizeof(char)))) { fprintf(stderr, "Can't malloc space for '%s'\n", path); return NULL; } r = fread(str, sizeof(char), len, fd); str[r - 1] = '\0'; /* Shader sources have to term with null */ fclose(fd); return str; } /* Storage For Textures */ GLuint texture[3]; /* Loads in a bitmap as a GL texture */ int LoadGLTextures( ) { int Status = 0; /* Create storage space for the texture */ SDL_Surface *TextureImage[1]; /* Load The Bitmap into Memory */ if ((TextureImage[0] = IMG_Load("flag.bmp"))) { Status = 1; glGenTextures( 1, &texture[0] ); glBindTexture( GL_TEXTURE_2D, texture[0] ); glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w, TextureImage[0]->h, 0, GL_BGR, GL_UNSIGNED_BYTE, TextureImage[0]->pixels ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } /* Free up some memory */ if ( TextureImage[0] ) SDL_FreeSurface( TextureImage[0] ); return Status; } int main(int argc, char **argv) { SDL_Event sdlEv; Uint32 sdlVideoFlags = SDL_OPENGL; Uint8 quit; char *extensions; int i, j; /* Initialize */ if (SDL_Init(SDL_INIT_VIDEO) = nextUpdate) { fprintf(stderr, "FPS: %d\n", frameCount); frameCount = 0; nextUpdate = SDL_GetTicks() + 1000; } } glDeleteShader(vs); glDeleteShader(fs); glDeleteProgram(sp); return EXIT_SUCCESS; } Running it should result in an animation as: