Difference between revisions of "Tutorial2: VAOs, VBOs, Vertex and Fragment Shaders (C / SDL)"

From OpenGL Wiki
Jump to: navigation, search
(New page: == Overview == This tutorial is designed to help explain Vertex Array Objects(VAOs), Vertex Buffer Objects(VBOs), Vertex and Fragment Shaders. This tutorial has the same requirements to r...)
 
m (Make more readable)
 
(22 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 +
{{tutorial c sdl}}
 +
 
== Overview ==
 
== Overview ==
  
This tutorial is designed to help explain Vertex Array Objects(VAOs), Vertex Buffer Objects(VBOs), Vertex and Fragment Shaders. This tutorial has the same requirements to run and compile as tutorial1.<br/>
+
This tutorial is designed to help explain [[Vertex Array Objects]](VAOs), [[Vertex Buffer Objects]](VBOs), Vertex and Fragment Shaders. This tutorial has the same requirements to run and compile as tutorial1.
The OpenGL 3.2 core specification removes the majority of the fixed function pipeline previously used, and replaces it with a completely programmable architecture using shaders. Our tutorial will make use of VAOs, and VBOs to provide our shaders with data.<br/><br/>
+
 
A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.<br/>
+
The OpenGL 3.2 core specification removes the majority of the fixed function pipeline previously used, and replaces it with a completely programmable architecture using shaders. Our tutorial will make use of VAOs and VBOs to provide our shaders with data.
A Vertex Buffer Object (VBO) is a memory buffer in the high speed memory of your video card designed to hold information about vertices. In our example we have two VBOs, one that describes the coordinates of our vertices and another that describes the color associated with each vertex. VBOs can also store information such as normals, texcoords, indicies, etc. <br/>
+
 
A Vertex Shader in OpenGL is a piece of C like code written to the GLSL specification which influences the attributes of a vertex. Vertex shaders can be used to modify properties of the vertex such as position, color, and texture coordinates.<br/>
+
A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.
A Fragment Shader is similar to a Vertex Shader, but is used for calculating individual fragment colors. This is where lighting and bump-mapping effects are performed.<br/>
+
 
There is another shader type called Geometry Shaders which we will using in the next tutorial that are natively available in opengl 3.2. They are used to create addition vertices.<br/>
+
A Vertex Buffer Object (VBO) is a [[Buffer Objects|memory buffer]] in the high speed memory of your video card designed to hold information about vertices. In our example we have two VBOs, one that describes the coordinates of our vertices and another that describes the color associated with each vertex. VBOs can also store information such as normals, texcoords, indicies, etc.  
The shader pipeline behaves as follows: Vertex Shaders -> Geometry Shaders -> (Rasterizing Engine) -> Fragment Shaders.<br/>
+
 
The shaders are compilied and the chained together into a Shader Program.<br/>
+
A [[Vertex Shader]] in OpenGL is a piece of C like code written to the GLSL specification which influences the attributes of a vertex. Vertex shaders can be used to modify properties of the vertex such as position, color, and texture coordinates.
Our shaders receive input data from our VAO through a process of attribute binding, allowing us to perform the needed computations to provides us with the desired results.<br/><br/>
+
 
Our tutorial consists of three separate code pieces: our main program (tutorial2.c), our vertex shader (tutorial2.vert), and our fragment shader (tutorial2.frag). The shader code files are read by our program and sent to OpenGL to be compiled, linked, and run.
+
A [[Fragment Shader]] is similar to a Vertex Shader, but is used for calculating individual fragment colors. This is where lighting and bump-mapping effects are performed.
 +
 
 +
There is another shader type called Geometry Shaders which we will be using in a later tutorial. They are used to create additional vertices.
 +
 
 +
The shader pipeline behaves as follows: Vertex Shaders -> Geometry Shaders -> (Rasterizing Engine) -> Fragment Shaders.
 +
 
 +
The shaders are compilied and then chained together into a Shader Program.
 +
 
 +
The shaders receive input data from our VAO through a process of attribute binding, allowing us to perform the needed computations to provide us with the desired results.
 +
 
 +
The tutorial consists of three separate code pieces: the main program (tutorial2.c), the vertex shader (tutorial2.vert), and the fragment shader (tutorial2.frag). The shader code files are read by our program and sent to OpenGL to be compiled, linked, and run.
  
 
== tutorial2.c ==
 
== tutorial2.c ==
<pre>
+
<source lang="c">
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <stdio.h>
 
#include <stdio.h>
Line 21: Line 33:
 
#include <GL3/gl3.h>
 
#include <GL3/gl3.h>
  
#include <SDL/SDL.h>
+
#include <SDL.h>
  
 
#define PROGRAM_NAME "Tutorial2"
 
#define PROGRAM_NAME "Tutorial2"
Line 32: Line 44:
 
     char *buf;
 
     char *buf;
  
     fptr = fopen(file, "r"); /* Open file for reading */
+
     fptr = fopen(file, "rb"); /* Open file for reading */
 
     if (!fptr) /* Return NULL on failure */
 
     if (!fptr) /* Return NULL on failure */
 
         return NULL;
 
         return NULL;
 
     fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
 
     fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
 
     length = ftell(fptr); /* Find out how many bytes into the file we are */
 
     length = ftell(fptr); /* Find out how many bytes into the file we are */
     buf = (char*)malloc(length); /* Allocate a buffer for the entile length of the file */
+
     buf = (char*)malloc(length+1); /* Allocate a buffer for the entire length of the file and a null terminator */
 
     fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
 
     fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
 
     fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
 
     fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
 
     fclose(fptr); /* Close the file */
 
     fclose(fptr); /* Close the file */
 +
    buf[length] = 0; /* Null terminator */
  
 
     return buf; /* Return the buffer */
 
     return buf; /* Return the buffer */
Line 86: Line 99:
 
     int i; /* Simple iterator */
 
     int i; /* Simple iterator */
 
     GLuint vao, vbo[2]; /* Create handles for our Vertex Array Object and two Vertex Buffer Objects */
 
     GLuint vao, vbo[2]; /* Create handles for our Vertex Array Object and two Vertex Buffer Objects */
 +
    int IsCompiled_VS, IsCompiled_FS;
 +
    int IsLinked;
 +
    int maxLength;
 +
    char *vertexInfoLog;
 +
    char *fragmentInfoLog;
 +
    char *shaderProgramInfoLog;
  
 
     /* We're going to create a simple diamond made from lines */
 
     /* We're going to create a simple diamond made from lines */
Line 126: Line 145:
  
 
     /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
 
     /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
     glVertexAttribPointer((GLuint)0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
  
 
     /* Enable attribute index 0 as being used */
 
     /* Enable attribute index 0 as being used */
Line 139: Line 158:
  
 
     /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
 
     /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
     glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
  
 
     /* Enable attribute index 1 as being used */
 
     /* Enable attribute index 1 as being used */
Line 148: Line 167:
 
     fragmentsource = filetobuf("tutorial2.frag");
 
     fragmentsource = filetobuf("tutorial2.frag");
  
     /* Assign our handles a "name" to new shader objects */
+
     /* Create an empty vertex shader handle */
 
     vertexshader = glCreateShader(GL_VERTEX_SHADER);
 
     vertexshader = glCreateShader(GL_VERTEX_SHADER);
 +
 +
    /* Send the vertex shader source code to GL */
 +
    /* Note that the source code is NULL character terminated. */
 +
    /* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
 +
    glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);
 +
 +
    /* Compile the vertex shader */
 +
    glCompileShader(vertexshader);
 +
 +
    glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &IsCompiled_VS);
 +
    if(IsCompiled_VS == FALSE)
 +
    {
 +
      glGetShaderiv(vertexshader, GL_INFO_LOG_LENGTH, &maxLength);
 +
 +
      /* The maxLength includes the NULL character */
 +
      vertexInfoLog = (char *)malloc(maxLength);
 +
 +
      glGetShaderInfoLog(vertexshader, maxLength, &maxLength, vertexInfoLog);
 +
 +
      /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
 +
      /* In this simple program, we'll just leave */
 +
      free(vertexInfoLog);
 +
      return;
 +
    }
 +
 +
    /* Create an empty fragment shader handle */
 
     fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
 
     fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
  
     /* Associate the source code buffers with each handle */
+
     /* Send the fragment shader source code to GL */
     glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);
+
     /* Note that the source code is NULL character terminated. */
 +
    /* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
 
     glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);
 
     glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);
  
     /* Compile our shader objects */
+
     /* Compile the fragment shader */
    glCompileShader(vertexshader);
 
 
     glCompileShader(fragmentshader);
 
     glCompileShader(fragmentshader);
  
 +
    glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &IsCompiled_FS);
 +
    if(IsCompiled_FS == FALSE)
 +
    {
 +
      glGetShaderiv(fragmentshader, GL_INFO_LOG_LENGTH, &maxLength);
 +
 +
      /* The maxLength includes the NULL character */
 +
      fragmentInfoLog = (char *)malloc(maxLength);
 +
 +
      glGetShaderInfoLog(fragmentshader, maxLength, &maxLength, fragmentInfoLog);
 +
 +
      /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
 +
      /* In this simple program, we'll just leave */
 +
      free(fragmentInfoLog);
 +
      return;
 +
    }
 +
 +
    /* If we reached this point it means the vertex and fragment shaders compiled and are syntax error free. */
 +
    /* We must link them together to make a GL shader program */
 +
    /* GL shader programs are monolithic. It is a single piece made of 1 vertex shader and 1 fragment shader. */
 
     /* Assign our program handle a "name" */
 
     /* Assign our program handle a "name" */
 
     shaderprogram = glCreateProgram();
 
     shaderprogram = glCreateProgram();
Line 168: Line 232:
  
 
     /* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
 
     /* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
 +
    /* Attribute locations must be setup before calling glLinkProgram. */
 
     glBindAttribLocation(shaderprogram, 0, "in_Position");
 
     glBindAttribLocation(shaderprogram, 0, "in_Position");
 
     glBindAttribLocation(shaderprogram, 1, "in_Color");
 
     glBindAttribLocation(shaderprogram, 1, "in_Color");
  
     /* Link our program, and set it as being actively used */
+
     /* Link our program */
 +
    /* At this stage, the vertex and fragment programs are inspected, optimized and a binary code is generated for the shader. */
 +
    /* The binary code is uploaded to the GPU, if there is no error. */
 
     glLinkProgram(shaderprogram);
 
     glLinkProgram(shaderprogram);
 +
 +
    /* Again, we must check and make sure that it linked. If it fails, it would mean either there is a mismatch between the vertex */
 +
    /* and fragment shaders. It might be that you have surpassed your GPU's abilities. Perhaps too many ALU operations or */
 +
    /* too many texel fetch instructions or too many interpolators or dynamic loops. */
 +
 +
    glGetProgramiv(shaderprogram, GL_LINK_STATUS, (int *)&IsLinked);
 +
    if(IsLinked == FALSE)
 +
    {
 +
      /* Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. */
 +
      glGetProgramiv(shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);
 +
 +
      /* The maxLength includes the NULL character */
 +
      shaderProgramInfoLog = (char *)malloc(maxLength);
 +
 +
      /* Notice that glGetProgramInfoLog, not glGetShaderInfoLog. */
 +
      glGetProgramInfoLog(shaderprogram, maxLength, &maxLength, shaderProgramInfoLog);
 +
 +
      /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
 +
      /* In this simple program, we'll just leave */
 +
      free(shaderProgramInfoLog);
 +
      return;
 +
    }
 +
 +
    /* Load the shader into the rendering pipeline */
 
     glUseProgram(shaderprogram);
 
     glUseProgram(shaderprogram);
  
Line 199: Line 290:
 
     glDetachShader(shaderprogram, vertexshader);
 
     glDetachShader(shaderprogram, vertexshader);
 
     glDetachShader(shaderprogram, fragmentshader);
 
     glDetachShader(shaderprogram, fragmentshader);
 +
    glDeleteProgram(shaderprogram);
 
     glDeleteShader(vertexshader);
 
     glDeleteShader(vertexshader);
 
     glDeleteShader(fragmentshader);
 
     glDeleteShader(fragmentshader);
Line 231: Line 323:
 
     return 0;
 
     return 0;
 
}
 
}
</pre>
+
</source>
  
 
== tutorial2.vert ==
 
== tutorial2.vert ==
<pre>
+
<source lang="c">
 
#version 150
 
#version 150
 
// in_Position was bound to attribute index 0 and in_Color was bound to attribute index 1
 
// in_Position was bound to attribute index 0 and in_Color was bound to attribute index 1
Line 249: Line 341:
  
 
     // GLSL allows shorthand use of vectors too, the following is also valid:
 
     // GLSL allows shorthand use of vectors too, the following is also valid:
     // gl_Position = vec3(in_Position, 0.0);
+
     // gl_Position = vec4(in_Position, 0.0, 1.0);
 +
 
 
     // We're simply passing the color through unmodified
 
     // We're simply passing the color through unmodified
 
 
     ex_Color = in_Color;
 
     ex_Color = in_Color;
 
}
 
}
</pre>
+
</source>
  
 
== tutorial2.frag ==
 
== tutorial2.frag ==
<pre>
+
<source lang="c">
 
#version 150
 
#version 150
 
// It was expressed that some drivers required this next line to function properly
 
// It was expressed that some drivers required this next line to function properly
Line 263: Line 355:
  
 
in  vec3 ex_Color;
 
in  vec3 ex_Color;
out vec4 out_Color;
+
out vec4 gl_FragColor;
  
 
void main(void) {
 
void main(void) {
 
     // Pass through our original color with full opacity.
 
     // Pass through our original color with full opacity.
     out_Color = vec4(ex_Color,1.0);
+
     gl_FragColor = vec4(ex_Color,1.0);
 
}
 
}
</pre>
+
</source>
 +
 
 
== Compilation ==
 
== Compilation ==
On linux:<br/>
+
On linux:
gcc tutorial2.c -o tutorial2 -lGL -lSDL<br/>
+
 
If you installed libsdl-1.3 to /usr/local then do:<br/>
+
gcc tutorial2.c -o tutorial2 -lGL $(sdl-config --cflags --libs)
gcc -I/usr/local/include -L/usr/local/lib tutorial2.c -o tutorial2 -lGL -lSDL
+
 
 +
If you have libsdl-1.2 and libsdl-1.3 both installed, make sure to run the appropriate version of sdl-config. For example if you installed sdl-1.3 to /usr/local:
 +
 
 +
gcc tutorial2.c -o tutorial2 -lGL $(/usr/local/bin/sdl-config --cflags --libs)
  
 
== Execution ==
 
== Execution ==
./tutorial2<br/>
+
./tutorial2
 
The result should be a 512x512 window centered on your display first drawing a gradient line, triangle, and then diamond.
 
The result should be a 512x512 window centered on your display first drawing a gradient line, triangle, and then diamond.
 +
 +
[[Category:Tutorials]]

Latest revision as of 12:08, 31 October 2018

Overview

This tutorial is designed to help explain Vertex Array Objects(VAOs), Vertex Buffer Objects(VBOs), Vertex and Fragment Shaders. This tutorial has the same requirements to run and compile as tutorial1.

The OpenGL 3.2 core specification removes the majority of the fixed function pipeline previously used, and replaces it with a completely programmable architecture using shaders. Our tutorial will make use of VAOs and VBOs to provide our shaders with data.

A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.

A Vertex Buffer Object (VBO) is a memory buffer in the high speed memory of your video card designed to hold information about vertices. In our example we have two VBOs, one that describes the coordinates of our vertices and another that describes the color associated with each vertex. VBOs can also store information such as normals, texcoords, indicies, etc.

A Vertex Shader in OpenGL is a piece of C like code written to the GLSL specification which influences the attributes of a vertex. Vertex shaders can be used to modify properties of the vertex such as position, color, and texture coordinates.

A Fragment Shader is similar to a Vertex Shader, but is used for calculating individual fragment colors. This is where lighting and bump-mapping effects are performed.

There is another shader type called Geometry Shaders which we will be using in a later tutorial. They are used to create additional vertices.

The shader pipeline behaves as follows: Vertex Shaders -> Geometry Shaders -> (Rasterizing Engine) -> Fragment Shaders.

The shaders are compilied and then chained together into a Shader Program.

The shaders receive input data from our VAO through a process of attribute binding, allowing us to perform the needed computations to provide us with the desired results.

The tutorial consists of three separate code pieces: the main program (tutorial2.c), the vertex shader (tutorial2.vert), and the fragment shader (tutorial2.frag). The shader code files are read by our program and sent to OpenGL to be compiled, linked, and run.

tutorial2.c

#include <stdlib.h>
#include <stdio.h>
/* Ensure we are using opengl's core profile only */
#define GL3_PROTOTYPES 1
#include <GL3/gl3.h>

#include <SDL.h>

#define PROGRAM_NAME "Tutorial2"

/* A simple function that will read a file into an allocated char pointer buffer */
char* filetobuf(char *file)
{
    FILE *fptr;
    long length;
    char *buf;

    fptr = fopen(file, "rb"); /* Open file for reading */
    if (!fptr) /* Return NULL on failure */
        return NULL;
    fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
    length = ftell(fptr); /* Find out how many bytes into the file we are */
    buf = (char*)malloc(length+1); /* Allocate a buffer for the entire length of the file and a null terminator */
    fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
    fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
    fclose(fptr); /* Close the file */
    buf[length] = 0; /* Null terminator */

    return buf; /* Return the buffer */
}

/* A simple function that prints a message, the error code returned by SDL, and quits the application */
void sdldie(char *msg)
{
    printf("%s: %s\n", msg, SDL_GetError());
    SDL_Quit();
    exit(1);
}

void setupwindow(SDL_WindowID *window, SDL_GLContext *context)
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0) /* Initialize SDL's Video subsystem */
        sdldie("Unable to initialize SDL"); /* Or die on error */

    /* Request an opengl 3.2 context.
     * SDL doesn't have the ability to choose which profile at this time of writing,
     * but it should default to the core profile */
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);

    /* Turn on double buffering with a 24bit Z buffer.
     * You may need to change this to 16 or 32 for your system */
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    /* Create our window centered at 512x512 resolution */
    *window = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
    if (!*window) /* Die if creation failed */
        sdldie("Unable to create window");

    /* Create our opengl context and attach it to our window */
    *context = SDL_GL_CreateContext(*window);

    /* This makes our buffer swap syncronized with the monitor's vertical refresh */
    SDL_GL_SetSwapInterval(1);
}

void drawscene(SDL_WindowID window)
{
    int i; /* Simple iterator */
    GLuint vao, vbo[2]; /* Create handles for our Vertex Array Object and two Vertex Buffer Objects */
    int IsCompiled_VS, IsCompiled_FS;
    int IsLinked;
    int maxLength;
    char *vertexInfoLog;
    char *fragmentInfoLog;
    char *shaderProgramInfoLog;

    /* We're going to create a simple diamond made from lines */
    const GLfloat diamond[4][2] = {
    {  0.0,  1.0  }, /* Top point */
    {  1.0,  0.0  }, /* Right point */
    {  0.0, -1.0  }, /* Bottom point */
    { -1.0,  0.0  } }; /* Left point */

    const GLfloat colors[4][3] = {
    {  1.0,  0.0,  0.0  }, /* Red */
    {  0.0,  1.0,  0.0  }, /* Green */
    {  0.0,  0.0,  1.0  }, /* Blue */
    {  1.0,  1.0,  1.0  } }; /* White */

    /* These pointers will receive the contents of our shader source code files */
    GLchar *vertexsource, *fragmentsource;

    /* These are handles used to reference the shaders */
    GLuint vertexshader, fragmentshader;

    /* This is a handle to the shader program */
    GLuint shaderprogram;

    /* Allocate and assign a Vertex Array Object to our handle */
    glGenVertexArrays(1, &vao);

    /* Bind our Vertex Array Object as the current used object */
    glBindVertexArray(vao);

    /* Allocate and assign two Vertex Buffer Objects to our handle */
    glGenBuffers(2, vbo);

    /* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);

    /* Copy the vertex data from diamond to our buffer */
    /* 8 * sizeof(GLfloat) is the size of the diamond array, since it contains 8 GLfloat values */
    glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), diamond, GL_STATIC_DRAW);

    /* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

    /* Enable attribute index 0 as being used */
    glEnableVertexAttribArray(0);

    /* Bind our second VBO as being the active buffer and storing vertex attributes (colors) */
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);

    /* Copy the color data from colors to our buffer */
    /* 12 * sizeof(GLfloat) is the size of the colors array, since it contains 12 GLfloat values */
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), colors, GL_STATIC_DRAW);

    /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

    /* Enable attribute index 1 as being used */
    glEnableVertexAttribArray(1);

    /* Read our shaders into the appropriate buffers */
    vertexsource = filetobuf("tutorial2.vert");
    fragmentsource = filetobuf("tutorial2.frag");

    /* Create an empty vertex shader handle */
    vertexshader = glCreateShader(GL_VERTEX_SHADER);

    /* Send the vertex shader source code to GL */
    /* Note that the source code is NULL character terminated. */
    /* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
    glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);

    /* Compile the vertex shader */
    glCompileShader(vertexshader);

    glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &IsCompiled_VS);
    if(IsCompiled_VS == FALSE)
    {
       glGetShaderiv(vertexshader, GL_INFO_LOG_LENGTH, &maxLength);

       /* The maxLength includes the NULL character */
       vertexInfoLog = (char *)malloc(maxLength);

       glGetShaderInfoLog(vertexshader, maxLength, &maxLength, vertexInfoLog);

       /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
       /* In this simple program, we'll just leave */
       free(vertexInfoLog);
       return;
    }

    /* Create an empty fragment shader handle */
    fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

    /* Send the fragment shader source code to GL */
    /* Note that the source code is NULL character terminated. */
    /* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
    glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);

    /* Compile the fragment shader */
    glCompileShader(fragmentshader);

    glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &IsCompiled_FS);
    if(IsCompiled_FS == FALSE)
    {
       glGetShaderiv(fragmentshader, GL_INFO_LOG_LENGTH, &maxLength);

       /* The maxLength includes the NULL character */
       fragmentInfoLog = (char *)malloc(maxLength);

       glGetShaderInfoLog(fragmentshader, maxLength, &maxLength, fragmentInfoLog);

       /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
       /* In this simple program, we'll just leave */
       free(fragmentInfoLog);
       return;
    }

    /* If we reached this point it means the vertex and fragment shaders compiled and are syntax error free. */
    /* We must link them together to make a GL shader program */
    /* GL shader programs are monolithic. It is a single piece made of 1 vertex shader and 1 fragment shader. */
    /* Assign our program handle a "name" */
    shaderprogram = glCreateProgram();

    /* Attach our shaders to our program */
    glAttachShader(shaderprogram, vertexshader);
    glAttachShader(shaderprogram, fragmentshader);

    /* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
    /* Attribute locations must be setup before calling glLinkProgram. */
    glBindAttribLocation(shaderprogram, 0, "in_Position");
    glBindAttribLocation(shaderprogram, 1, "in_Color");

    /* Link our program */
    /* At this stage, the vertex and fragment programs are inspected, optimized and a binary code is generated for the shader. */
    /* The binary code is uploaded to the GPU, if there is no error. */
    glLinkProgram(shaderprogram);

    /* Again, we must check and make sure that it linked. If it fails, it would mean either there is a mismatch between the vertex */
    /* and fragment shaders. It might be that you have surpassed your GPU's abilities. Perhaps too many ALU operations or */
    /* too many texel fetch instructions or too many interpolators or dynamic loops. */

    glGetProgramiv(shaderprogram, GL_LINK_STATUS, (int *)&IsLinked);
    if(IsLinked == FALSE)
    {
       /* Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. */
       glGetProgramiv(shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);

       /* The maxLength includes the NULL character */
       shaderProgramInfoLog = (char *)malloc(maxLength);

       /* Notice that glGetProgramInfoLog, not glGetShaderInfoLog. */
       glGetProgramInfoLog(shaderprogram, maxLength, &maxLength, shaderProgramInfoLog);

       /* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
       /* In this simple program, we'll just leave */
       free(shaderProgramInfoLog);
       return;
    }

    /* Load the shader into the rendering pipeline */
    glUseProgram(shaderprogram);

    /* Loop our display increasing the number of shown vertexes each time.
     * Start with 2 vertexes (a line) and increase to 3 (a triangle) and 4 (a diamond) */
    for (i=2; i <= 4; i++)
    {
        /* Make our background black */
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        /* Invoke glDrawArrays telling that our data is a line loop and we want to draw 2-4 vertexes */
        glDrawArrays(GL_LINE_LOOP, 0, i);

        /* Swap our buffers to make our changes visible */
        SDL_GL_SwapWindow(window);

        /* Sleep for 2 seconds */
        SDL_Delay(2000);
    }

    /* Cleanup all the things we bound and allocated */
    glUseProgram(0);
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDetachShader(shaderprogram, vertexshader);
    glDetachShader(shaderprogram, fragmentshader);
    glDeleteProgram(shaderprogram);
    glDeleteShader(vertexshader);
    glDeleteShader(fragmentshader);
    glDeleteBuffers(2, vbo);
    glDeleteVertexArrays(1, &vao);
    free(vertexsource);
    free(fragmentsource);
}

void destroywindow(SDL_WindowID window, SDL_GLContext context)
{
    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

/* Our program's entry point */
int main(int argc, char *argv[])
{
    SDL_WindowID mainwindow; /* Our window handle */
    SDL_GLContext maincontext; /* Our opengl context handle */

    /* Create our window, opengl context, etc... */
    setupwindow(&mainwindow, &maincontext);

    /* Call our function that performs opengl operations */
    drawscene(mainwindow);

    /* Delete our opengl context, destroy our window, and shutdown SDL */
    destroywindow(mainwindow, maincontext);

    return 0;
}

tutorial2.vert

#version 150
// in_Position was bound to attribute index 0 and in_Color was bound to attribute index 1
in  vec2 in_Position;
in  vec3 in_Color;

// We output the ex_Color variable to the next shader in the chain
out vec3 ex_Color;
void main(void) {
    // Since we are using flat lines, our input only had two points: x and y.
    // Set the Z coordinate to 0 and W coordinate to 1

    gl_Position = vec4(in_Position.x, in_Position.y, 0.0, 1.0);

    // GLSL allows shorthand use of vectors too, the following is also valid:
    // gl_Position = vec4(in_Position, 0.0, 1.0);

    // We're simply passing the color through unmodified
    ex_Color = in_Color;
}

tutorial2.frag

#version 150
// It was expressed that some drivers required this next line to function properly
precision highp float;

in  vec3 ex_Color;
out vec4 gl_FragColor;

void main(void) {
    // Pass through our original color with full opacity.
    gl_FragColor = vec4(ex_Color,1.0);
}

Compilation

On linux:

gcc tutorial2.c -o tutorial2 -lGL $(sdl-config --cflags --libs)

If you have libsdl-1.2 and libsdl-1.3 both installed, make sure to run the appropriate version of sdl-config. For example if you installed sdl-1.3 to /usr/local:

gcc tutorial2.c -o tutorial2 -lGL $(/usr/local/bin/sdl-config --cflags --libs)

Execution

./tutorial2 The result should be a 512x512 window centered on your display first drawing a gradient line, triangle, and then diamond.