PDA

View Full Version : Square stopped rendering when I added indices



quadcricket
09-29-2014, 07:18 PM
I've been working on this for a couple of days now and I just can't see what I'm doing wrong. I took some code that draws a square from two triangles and tried to simply add an index buffer object and everything just stopped rendering.

main.c


/*
* This example shows an example of setting up the projection matrix to enable control over 3d rendering.
* I also added an index to cut down on the vertices.
*
* This project includes two shader files: vs.glsl and fs.glsl
*
*/

#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <math.h>

#define TRUE 1
#define FALSE 0
#define SCREEN_WIDTH 640.0
#define SCREEN_HEIGHT 480.0

#define IDENTITY_MATRIX4 { 1.0, 0.0, 0.0, 0.0,\
0.0, 1.0, 0.0, 0.0,\
0.0, 0.0, 1.0, 0.0,\
0.0, 0.0, 0.0, 1.0 }
typedef enum {
X_AXIS,
Y_AXIS,
Z_AXIS
} AXIS;

/* our loadShader function */
GLuint loadShader(const char *strShaderFile, GLenum type)
{
char shaderSource[4096];
char inChar;
FILE *shaderFile;
int i = 0;

shaderFile = fopen(strShaderFile, "r");
while(fscanf(shaderFile, "%c", &inChar) > 0)
{
shaderSource[i++] = inChar; /* load the file's characters into an array */
}
shaderSource[i-1] = '\0'; /* set the last character to NULL for future parsing. */

fclose(shaderFile);
puts(shaderSource); /* print the source to test loading */

GLuint shader = glCreateShader(type);
const char *ss = shaderSource;
glShaderSource(shader, 1, &ss, NULL);

glCompileShader(shader);

GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE)
{
printf("Shader Compile error!\n");
}
else
{
printf("Shader compiled successfully!\n");
}

return shader;
}

/* our matrix math functions
*
*/
/* Multiply 4x4 matrix m1 by 4x4 matrix m2 and store the result in m1 */
void multiply4x4(GLfloat *m1, GLfloat *m2)
{
GLfloat temp[16];

int x,y;

for (x=0; x < 4; x++)
{
for(y=0; y < 4; y++)
{
temp[y + (x*4)] = (m1[x*4] * m2[y]) +
(m1[(x*4)+1] * m2[y+4]) +
(m1[(x*4)+2] * m2[y+8]) +
(m1[(x*4)+3] * m2[y+12]);
}
}

memcpy(m1, temp, sizeof(GLfloat) << 4);
}

/* Generate a perspective view matrix using a field of view angle fov,
* window aspect ratio, near and far clipping planes */
void perspective(GLfloat *matrix, GLfloat fov, GLfloat aspect, GLfloat nearz, GLfloat farz)
{
GLfloat range;

range = tan(fov * 0.00872664625) * nearz; /* 0.00872664625 = PI/360 */
memset(matrix, 0, sizeof(GLfloat) * 16);
matrix[0] = (2 * nearz) / ((range * aspect) - (-range * aspect));
matrix[5] = (2 * nearz) / (2 * range);
matrix[10] = -(farz + nearz) / (farz - nearz);
matrix[11] = -1;
matrix[14] = -(2 * farz * nearz) / (farz - nearz);
}

/* Perform translation operations on a matrix */
void translate(GLfloat *matrix, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat newmatrix[16] = IDENTITY_MATRIX4;

newmatrix[12] = x;
newmatrix[13] = y;
newmatrix[14] = z;

multiply4x4(matrix, newmatrix);
}

/* Rotate a matrix by an angle on a X, Y, or Z axis */
void rotate(GLfloat *matrix, GLfloat angle, AXIS axis)
{
const GLfloat d2r = 0.0174532925199; /* PI / 180 */
const int cos1[3] = { 5, 0, 0 };
const int cos2[3] = { 10, 10, 5 };
const int sin1[3] = { 6, 2, 1 };
const int sin2[3] = { 9, 8, 4 };
GLfloat newmatrix[16] = IDENTITY_MATRIX4;

newmatrix[cos1[axis]] = cos(d2r * angle);
newmatrix[sin1[axis]] = -sin(d2r * angle);
newmatrix[sin2[axis]] = -newmatrix[sin1[axis]];
newmatrix[cos2[axis]] = newmatrix[cos1[axis]];

multiply4x4(matrix, newmatrix);
}
/*
* end math functions
*/


int main(int argc, char *argv[])
{
/* first let's initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("Could not initialize SDL!: %s", SDL_GetError());
return 1;
}

/* create an event handler for the keyboard */
SDL_Event(event);

/* Now let's create our window */
SDL_Window *window = SDL_CreateWindow("two triangles with index! - perspective",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);

/* because of OpenGL we will need to create the OpenGL context */
SDL_GLContext glContext = SDL_GL_CreateContext(window);
/* and test */
if(glContext == NULL)
{
printf("Unable to create OpenGL context: %s\n", SDL_GetError());
return 1;
}

/* vsync */
SDL_GL_SetSwapInterval(1);

/* Next make the window current */
SDL_GL_MakeCurrent(window, glContext);

/* and make sure we specify a GL version */
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
printf("OpenGL version: %s\n", glGetString(GL_VERSION));

/* Enable depth testing and which type of test we will do */
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

/* create our matrices for projection, model, and identity */
GLfloat projectionMatrix[16];
GLfloat modelMatrix[16];
const float identityMatrix[16] = IDENTITY_MATRIX4;

/* great, the SDL setup is done so we can start
* setting up the program.
* Let's define some variables.
*/
GLuint vao; /* our vertex array object */
GLuint vbo; /* our vertex buffer object */
GLuint ibo; /* our index buffer object */
GLuint programID; /* the program we will use to render the triangle */

/* lets create some vertices
*
* (-1,1)[0] (1,1)[3]
* *-------*
* | /|
* | / |
* | / |
* | / |
* | / |
* | / |
* |/ |
* *-------*
* (-1,-1)[1] (1,-1)[2]
*/
/* old
GLfloat vertexData[] = {
-1.0f, 1.0f, 0.0f,
-1.0f,-1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f,-1.0f, 0.0f,
1.0f,-1.0f, 0.0f
};
*/
/* new! */
GLfloat vertices[] = {
-1.0, 1.0, 0.0,
1.0,-1.0, 0.0,
1.0,-1.0, 0.0,
1.0, 1.0, 0.0
};

/* our index for the shared vertices */
GLuint indices[] = {
0, 1, 3,
3, 1, 2
};

/* let's load our shaders */
GLuint vertexShader;
GLuint fragmentShader;
GLuint linked; /* for testing if the shaders linked correctly. */

vertexShader = loadShader("vs.glsl", GL_VERTEX_SHADER); /* loadShader has been previously defined. */
fragmentShader = loadShader("fs.glsl", GL_FRAGMENT_SHADER);

/* activate our shader program. */
programID = glCreateProgram();
glAttachShader(programID, vertexShader);
glAttachShader(programID, fragmentShader);
glBindAttribLocation(programID, 0, "in_Position");
glLinkProgram(programID);
glGetProgramiv(programID, GL_LINK_STATUS, &linked);
glUseProgram(programID);

/* generate our objects */
glGenVertexArrays(1, &vao); /* generate array using previously defined "vao" variable as id storage */
glBindVertexArray(vao); /* bind the array for use */

glGenBuffers(1, &vbo); /* generate Vertex Buffer Objext and store id in "vbo" */
glBindBuffer(GL_ARRAY_BUFFER, vbo); /* bind it for use */
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); /* store our vertex data "vertexData" into our vbo */
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertices), NULL); /* specify how to use the vertex data */
if(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
{
printf("Framebuffer ok!\n");
}
else
{
printf("Error creating fremebuffer!\n");
glDeleteVertexArrays(&vao);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
glEnableVertexAttribArray(0); /* connect to the 0 attibute of the vertex shader */

glGenBuffers(1, &ibo); /* generate the index buffer object */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); /* bind it for definition */
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); /* the definition */

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); /* unbind the ibo */
glBindBuffer(GL_ARRAY_BUFFER, 0); /* unbind the vbo */
glBindVertexArray(0); /* unbind the vao */

/* now that the vbo and vao are defined and populated we may proceed to drawing stuff! */
unsigned short int running = TRUE;
glClearColor(0.2, 0.2, 0.2, 1); /* background */
/* our projection matrix with 45 degree FOV, a width to height ratio , and view .1 to 100 in front of us */
perspective(projectionMatrix, 45.0, SCREEN_WIDTH/SCREEN_HEIGHT, 0.1, 100);

/* degree will be used to rotate our object */
unsigned short int degree = 0;
int z = -1; /* used for controlling the z axis */

while(running)
{
/* check for keyboard events */
if(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
running = FALSE;
else if(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE )
running = FALSE;
else if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_UP)
z--;
else if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_DOWN)
z++;
}

/* move the identity matrix into model matrix, move it back 5 */
memcpy(modelMatrix, identityMatrix, sizeof(GLfloat)*16);
rotate(modelMatrix, (GLfloat)degree * 1.0, Y_AXIS);
translate(modelMatrix, 0, 0, z);
if( degree > 360 )
{
degree = 0;
}
else
{
degree++;
}

/* multiply our model matrix and our projection matrix, results are stored in modelMatrix */
multiply4x4(modelMatrix, projectionMatrix);

/* bind our model matrix to mvpMatrix in our shader program */
glUniformMatrix4fv(glGetUniformLocation(programID, "mvpMatrix"), 1, GL_FALSE, modelMatrix);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear the color buffer and depth buffer after each frame with the background color */

/* lets draw the triangle! */
glBindVertexArray(vao); /* bind the vao we want to work with ("vao" in this case) */
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (GLvoid*)0); /* draw a triangle! */
//printf("glError: %i\n", glGetError());
glBindVertexArray(0); /* now unbind it */

/* SDL2 */
SDL_GL_SwapWindow(window); /* this swaps the video memory to the window allowing us to see it */
SDL_Delay(20); /* It's very important to allow some cpu to go to other processes on the computer, SDL_Delay does just that. */

}

/* cleanup */
glUseProgram(0);
glDisableVertexAttribArray(0);
glDetachShader(programID, vertexShader);
glDetachShader(programID, fragmentShader);
glDeleteProgram(programID);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteBuffers(1, &ibo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
SDL_Quit();

return 0; /* end our program */
}


vs.glsl


#version 130
in vec3 in_Position;

//mvp matrix is the result of multiplying the model, view, and projection matrices
uniform mat4 mvpMatrix;

void main()
{
// multiply the mvpMatrix by the vertex to obtain our final vertex position
gl_Position = mvpMatrix * vec4(in_Position, 1.0);
}


fs.glsl


#version 130
out vec4 outputColor;
void main()
{
outputColor = vec4(1.0, 0.7, 0.0, 1.0);
}


It also may be my old laptop since it's running linux.

Any help would be appreciated!

sueyllam
09-29-2014, 08:47 PM
Well I noticed that in your new vertices the vertex -1.0, -1.0, 0.0 is missing, instead you wrote one of the vertices twice....

quadcricket
09-30-2014, 12:01 AM
That's correct, I completely missed that.



/* new! */
GLfloat vertices[] = {
-1.0, 1.0, 0.0,
-1.0,-1.0, 0.0,
1.0, 1.0, 0.0,
1.0,-1.0, 0.0
};


That is what I changed it to. Still nothing on the screen.

I'm wondering if there is part of the shaders I have to change. I'm just learning openGL so it's hard to learn all the parts at once. It seems like there are so many ways to make it fail. I'm not getting any compile errors or glErrors. It certainly is tricky to debug.

Nikki_k
09-30-2014, 12:28 AM
You unbind the GL_ELEMENT_ARRAY_BUFFER while your VAO is still bound. You need to keep this binding in your VAO or glDrawElements does not know what buffer to draw from.
Remember: Unlike GL_ARRAY_BUFFER you never specify anything else about your index buffer (unlike the glVertexAttribPointer calls for GL_ARRAY_BUFFER) so the binding must remain active that the VAO can keep a reference to it.

If you just remove this line:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); /* unbind the ibo */

it should work.

quadcricket
09-30-2014, 09:21 AM
I uncommented that line but there is still nothing rendering. I didn't know you could leave things bound. Thanks for looking through the source!

quadcricket
10-06-2014, 03:58 PM
I got it to work by constantly messing with the code. I'm still not exactly sure why it works but here is the working code as reference to anyone who might be running into the same issue I had:



/*
* This example shows an example of setting up the projection matrix to enable control over 3d rendering.
*
* This project includes two shader files: vs.glsl and fs.glsl
*
*/

#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <math.h>

#define TRUE 1
#define FALSE 0
#define SCREEN_WIDTH 640.0
#define SCREEN_HEIGHT 480.0

#define IDENTITY_MATRIX4 { 1.0, 0.0, 0.0, 0.0,\
0.0, 1.0, 0.0, 0.0,\
0.0, 0.0, 1.0, 0.0,\
0.0, 0.0, 0.0, 1.0 }
typedef enum {
X_AXIS,
Y_AXIS,
Z_AXIS
} AXIS;

/* our loadShader function */
GLuint loadShader(const char *strShaderFile, GLenum type)
{
char shaderSource[4096];
char inChar;
FILE *shaderFile;
int i = 0;

shaderFile = fopen(strShaderFile, "r");
while(fscanf(shaderFile, "%c", &inChar) > 0)
{
shaderSource[i++] = inChar; /* load the file's characters into an array */
}
shaderSource[i-1] = '\0'; /* set the last character to NULL for future parsing. */

fclose(shaderFile);
puts(shaderSource); /* print the source to test loading */

GLuint shader = glCreateShader(type);
const char *ss = shaderSource;
glShaderSource(shader, 1, &ss, NULL);

glCompileShader(shader);

GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE)
{
printf("Shader Compile error!\n");
}
else
{
printf("Shader compiled successfully!\n");
}

return shader;
}

/* our matrix math functions
*
*/
/* Multiply 4x4 matrix m1 by 4x4 matrix m2 and store the result in m1 */
void multiply4x4(GLfloat *m1, GLfloat *m2)
{
GLfloat temp[16];

int x,y;

for (x=0; x < 4; x++)
{
for(y=0; y < 4; y++)
{
temp[y + (x*4)] = (m1[x*4] * m2[y]) +
(m1[(x*4)+1] * m2[y+4]) +
(m1[(x*4)+2] * m2[y+8]) +
(m1[(x*4)+3] * m2[y+12]);
}
}

memcpy(m1, temp, sizeof(GLfloat) << 4);
}

/* Generate a perspective view matrix using a field of view angle fov,
* window aspect ratio, near and far clipping planes */
void perspective(GLfloat *matrix, GLfloat fov, GLfloat aspect, GLfloat nearz, GLfloat farz)
{
GLfloat range;

range = tan(fov * 0.00872664625) * nearz; /* 0.00872664625 = PI/360 */
memset(matrix, 0, sizeof(GLfloat) * 16);
matrix[0] = (2 * nearz) / ((range * aspect) - (-range * aspect));
matrix[5] = (2 * nearz) / (2 * range);
matrix[10] = -(farz + nearz) / (farz - nearz);
matrix[11] = -1;
matrix[14] = -(2 * farz * nearz) / (farz - nearz);
}

/* Perform translation operations on a matrix */
void translate(GLfloat *matrix, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat newmatrix[16] = IDENTITY_MATRIX4;

newmatrix[12] = x;
newmatrix[13] = y;
newmatrix[14] = z;

multiply4x4(matrix, newmatrix);
}

/* Rotate a matrix by an angle on a X, Y, or Z axis */
void rotate(GLfloat *matrix, GLfloat angle, AXIS axis)
{
const GLfloat d2r = 0.0174532925199; /* PI / 180 */
const int cos1[3] = { 5, 0, 0 };
const int cos2[3] = { 10, 10, 5 };
const int sin1[3] = { 6, 2, 1 };
const int sin2[3] = { 9, 8, 4 };
GLfloat newmatrix[16] = IDENTITY_MATRIX4;

newmatrix[cos1[axis]] = cos(d2r * angle);
newmatrix[sin1[axis]] = -sin(d2r * angle);
newmatrix[sin2[axis]] = -newmatrix[sin1[axis]];
newmatrix[cos2[axis]] = newmatrix[cos1[axis]];

multiply4x4(matrix, newmatrix);
}
/*
* end math functions
*/


int main(int argc, char *argv[])
{
/* first let's initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("Could not initialize SDL!: %s", SDL_GetError());
return 1;
}

/* create an event handler for the keyboard */
SDL_Event(event);

/* Now let's create our window */
SDL_Window *window = SDL_CreateWindow("two triangles - perspective",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);

/* because of OpenGL we will need to create the OpenGL context */
SDL_GLContext glContext = SDL_GL_CreateContext(window);
/* and test */
if(glContext == NULL)
{
printf("Unable to create OpenGL context: %s\n", SDL_GetError());
return 1;
}

/* vsync */
SDL_GL_SetSwapInterval(1);

/* Next make the window current */
SDL_GL_MakeCurrent(window, glContext);

/* and make sure we specify a GL version */
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);

/* Enable depth testing and which type of test we will do */
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

/* create our matrices for projection, model, and identity */
GLfloat projectionMatrix[16];
GLfloat modelMatrix[16];
const float identityMatrix[16] = IDENTITY_MATRIX4;

/* great, the SDL setup is done so we can start
* setting up the program.
* Let's define some variables.
*/
GLuint program; /* the program we will use to render the triangle */

/* lets create some vertices
*
* (-1,1)[0] (1,1)[3]
* *-------*
* | /|
* | / |
* | / |
* | / |
* | / |
* | / |
* |/ |
* *-------*
* (-1,-1)[1] (1,-1)[2]
*/
GLfloat vertexData[] = {
/* X Y Z */
-1.0f, 1.0f, 0.0f,
-1.0f,-1.0f, 0.0f,
1.0f,-1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
/* index */
GLushort indices[] = {0,1,3, 3,1,2};

/* activate our shader program. */
GLuint vertexShader;
GLuint fragmentShader;
vertexShader = loadShader("vs.glsl", GL_VERTEX_SHADER);
fragmentShader = loadShader("fs.glsl", GL_FRAGMENT_SHADER);
program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glBindAttribLocation(program, 0, "in_Position");
glLinkProgram(program);
glUseProgram(program);

/* generate the vertex array object that will hold our other objects */
GLuint vao; /* our vertex array object */
glGenVertexArrays(1, &vao);
glBindVertexArray(vao); /* "bind" the array for use */

/* generate Vertex Buffer Objext and store id in "vbo" */
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo); /* bind it for use */
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); /* store our vertex data "vertexData" into our vbo */

/* we will need an index buffer object to store the vertices */
GLuint ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

/* unbind the vbo */
glBindBuffer(GL_ARRAY_BUFFER, 0);


/* now that the vbo and vao are defined and populated we may proceed to drawing stuff! */
unsigned short int running = TRUE;
glClearColor(0.2, 0.2, 0.2, 1); /* background */
/* our projection matrix with 45 degree FOV, a width to height ratio , and view .1 to 100 in front of us */
perspective(projectionMatrix, 45.0, SCREEN_WIDTH/SCREEN_HEIGHT, 0.1, 100);

/* degree will be used to rotate our object */
unsigned short int degree = 0;
int z = -3; /* used for controlling the z axis */

while(running)
{
/* check for keyboard events */
if(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
running = FALSE;
else if(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE )
running = FALSE;
else if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_UP)
z--;
else if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_DOWN)
z++;
}

/* move the identity matrix into model matrix, move it back 5 */
memcpy(modelMatrix, identityMatrix, sizeof(GLfloat)*16);
rotate(modelMatrix, (GLfloat)degree * 1.0, Y_AXIS);
translate(modelMatrix, 0, 0, z);
if( degree > 360 )
{
degree = 0;
}
else
{
degree++;
}

/* multiply our model matrix and our projection matrix, results are stored in modelMatrix */
multiply4x4(modelMatrix, projectionMatrix);

/* bind our model matrix to mvpMatrix in our shader program */
glUniformMatrix4fv(glGetUniformLocation(program, "mvpMatrix"), 1, GL_FALSE, modelMatrix);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear the color buffer and depth buffer after each frame with the background color */

/* lets draw the triangle! */
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);

glDisableVertexAttribArray(0);

/* SDL2 */
SDL_GL_SwapWindow(window); /* this swaps the video memory to the window allowing us to see it */
SDL_Delay(20); /* It's very important to allow some cpu to go to other processes on the computer, SDL_Delay does just that. */

}

/* cleanup */
glUseProgram(0);
glDisableVertexAttribArray(0);
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
glDeleteProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
SDL_Quit();

return 0; /* end our program */
}