Skip to content

Instantly share code, notes, and snippets.

@dlivingstone
Created November 7, 2011 21:42
Show Gist options
  • Save dlivingstone/1346284 to your computer and use it in GitHub Desktop.
Save dlivingstone/1346284 to your computer and use it in GitHub Desktop.
OpenGL with GLM & SDL: A simple program to demonstrate modern OpenGL programming (compatible with OpenGL 3.0 and later) CC-BY-SA Daniel Livingstone
#include <iostream>
#include <fstream>
// Windows specific: Uncomment the following line to open a console window for debug output
#if _DEBUG
#pragma comment(linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")
#endif
// Simple OpenGL GLM/SDL demo
// Renders a rotating pyramid, showing 3 of 4 sides (allowing back faces to be seen)
// Using the gl3.h header (from http://www.opengl.org/registry/) can limit code to the core profile only
// GLEW is required on Windows but prob not on Linux or OSX Lion. On those platforms you should
// uncomment the next two lines and then comment out the following line.
//#define GL3_PROTOTYPES 1
//#include <GL3/gl3.h>
#include <GL/glew.h>
#include <SDL.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std;
// global variables - normally would avoid globals, using in this demo
GLuint shaderprogram; // handle for shader program
GLuint vao, vbo[2]; // handles for our VAO and two VBOs
float r = 0;
// loadFile - loads text file into char* fname
// allocates memory - so need to delete after use
const char* loadFile(char *fname)
{
int size;
char * memblock;
// file read based on example in cplusplus.com tutorial
// ios::ate opens file at the end
ifstream file (fname, ios::in|ios::binary|ios::ate);
if (file.is_open())
{
size = (int) file.tellg(); // get location of file pointer i.e. file size
memblock = new char [size+1]; // create buffer with space for null char
file.seekg (0, ios::beg);
file.read (memblock, size);
file.close();
memblock[size] = 0;
cout << "file " << fname << " loaded" << endl;
}
else
{
cout << "Unable to open file " << fname << endl;
// should ideally set a flag or use exception handling
// so that calling function can decide what to do now
}
return memblock;
}
// Something went wrong - print SDL error message and quit
void exitFatalError(char *message)
{
cout << message << " ";
cout << SDL_GetError();
SDL_Quit();
exit(1);
}
// Set up rendering context
void setupRC(SDL_WindowID &window, SDL_GLContext &context)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) // Initialize video
exitFatalError("Unable to initialize SDL");
// Request an OpenGL 3.0 context.
// Not able to use SDL to choose profile (yet), should default to core profile on 3.2 or later
// If you request a context not supported by your drivers, no OpenGL context will be created
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); // double buffering on
// Turn on x4 multisampling anti-aliasing (MSAA)
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
// Create 800x600 window
window = SDL_CreateWindow("SDL/GLM/OpenGL Demo",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
if (!window) // Check window was created OK
exitFatalError("Unable to create window");
context = SDL_GL_CreateContext(window); // Create opengl context and attach to window
SDL_GL_SetSwapInterval(1); // set swap buffers to sync with monitor's vertical refresh rate
}
// printShaderError
// Display (hopefully) useful error messages if shader fails to compile or link
void printShaderError(GLint shader)
{
int maxLength = 0;
int logLength = 0;
GLchar *logMessage;
// Find out how long the error message is
if (glIsShader(shader))
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
else
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
if (maxLength > 0) // If message has length > 0
{
logMessage = new GLchar[maxLength];
if (glIsShader(shader))
glGetProgramInfoLog(shader, maxLength, &logLength, logMessage);
else
glGetShaderInfoLog(shader,maxLength, &logLength, logMessage);
cout << "Shader Info Log:" << endl << logMessage << endl;
delete [] logMessage;
}
}
GLuint initShaders(char *vertFile, char *fragFile)
{
GLuint p, f, v; // Handles for shader program & vertex and fragment shaders
v = glCreateShader(GL_VERTEX_SHADER); // Create vertex shader handle
f = glCreateShader(GL_FRAGMENT_SHADER); // " fragment shader handle
const char *vertSource = loadFile(vertFile); // load vertex shader source
const char *fragSource = loadFile(fragFile); // load frag shader source
// Send the shader source to the GPU
// Strings here are null terminated - a non-zero final parameter can be
// used to indicate the length of the shader source instead
glShaderSource(v, 1, &vertSource,0);
glShaderSource(f, 1, &fragSource,0);
GLint compiled, linked; // return values for checking for compile & link errors
// compile the vertex shader and test for errors
glCompileShader(v);
glGetShaderiv(v, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
cout << "Vertex shader not compiled." << endl;
printShaderError(v);
}
// compile the fragment shader and test for errors
glCompileShader(f);
glGetShaderiv(f, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
cout << "Fragment shader not compiled." << endl;
printShaderError(f);
}
p = glCreateProgram(); // create the handle for the shader program
glAttachShader(p,v); // attach vertex shader to program
glAttachShader(p,f); // attach fragment shader to program
glBindAttribLocation(p,0,"in_Position"); // bind position attribute to location 0
glBindAttribLocation(p,1,"in_Color"); // bind color attribute to location 0
glLinkProgram(p); // link the shader program and test for errors
glGetProgramiv(p, GL_LINK_STATUS, &linked);
if(!linked)
{
cout << "Program not linked." << endl;
printShaderError(p);
}
glUseProgram(p); // Make the shader program the current active program
delete [] vertSource; // Don't forget to free allocated memory
delete [] fragSource; // We allocated this in the loadFile function...
return p; // Return the shader program handle
}
void init(void)
{
const GLfloat pyramid[15] = { // a simple pyramid
0.0, 0.5, 0.0, // top
-1.0, -0.5, 1.0, // front bottom left
1.0, -0.5, 1.0, // front bottom right
1.0, -0.5, -1.0, // back bottom right
-1.0, -0.5, -1.0 }; // back bottom left
const GLfloat colors[15] = {
0.0, 0.0, 0.0, // black
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, 0.0 }; // yellow
shaderprogram = initShaders("simple.vert","simple.frag"); // Create and start shader program
glGenVertexArrays(1, &vao); // allocate & assign a Vertex Array Object (VAO)
glBindVertexArray(vao); // bind VAO as current object
glGenBuffers(2, vbo); // allocate two Vertex Buffer Objects (VBO)
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // bind first VBO as active buffer object
// Copy the vertex data from diamond to our VBO
// 15 * sizeof(GLfloat) is the size of the pyramid array, since it contains 15 GLfloat values
glBufferData(GL_ARRAY_BUFFER, 15 * sizeof(GLfloat), pyramid, GL_STATIC_DRAW);
// Specify that position data is going into attribute index 0, and contains three floats per vertex
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0); // Enable attribute index 0 (position)
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); // bind second VBO as active buffer object
// Copy the color data from colors to our buffer
// 15 * sizeof(GLfloat) is the size of the colors array, since it contains 15 GLfloat values
glBufferData(GL_ARRAY_BUFFER, 15 * 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);
glEnableVertexAttribArray(1); // Enable attribute index 1 (color)
// OpenGL is state based - we normally need to keep references to shader, VAO, VBO, etc., to allow
// swapping between different ones. In this program there is only one shader program and one VAO
glEnable(GL_DEPTH_TEST); // enable depth testing
//glEnable(GL_CULL_FACE); // enable back face culling - try this and see what happens!
}
void draw(const SDL_WindowID &window)
{
glClearColor(1.0, 1.0, 1.0, 1.0); // set background colour
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear window
// Create perspective projection matrix
glm::mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 1.0f, 100.f);
// Apply model view transformations
glm::mat4 identity(1.0);
glm::mat4 translation = glm::translate( identity, glm::vec3(0.0f, 0.0f, -4.0f));
glm::mat4 rotation = glm::rotate( translation, r, glm::vec3(0.0f,1.0f,0.0f));
glm::mat4 MVP = projection * rotation; // Calculate final MVP matrix
// pass MVP as uniform into shader
int mvpIndex = glGetUniformLocation(shaderprogram, "MVP");
glUniformMatrix4fv(mvpIndex, 1, GL_FALSE, glm::value_ptr(MVP));
glDrawArrays(GL_TRIANGLE_FAN, 0, 5); // draw the pyramid
r+=0.5;
SDL_GL_SwapWindow(window); // swap buffers
}
void cleanup(void)
{
glUseProgram(0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
// could also detach shaders
glDeleteProgram(shaderprogram);
glDeleteBuffers(2, vbo);
glDeleteVertexArrays(1, &vao);
}
// Program entry point - SDL manages the actual WinMain entry point for us
int main(int argc, char *argv[])
{
SDL_WindowID hWindow; // window handle
SDL_GLContext glContext; // OpenGL context handle
setupRC(hWindow, glContext); // Create window and render context
GLenum err = glewInit();
// Required on Windows... init GLEW to access OpenGL beyond 1.1
// remove on other platforms
if (GLEW_OK != err)
{ // glewInit failed, something is seriously wrong.
cout << "glewInit failed, aborting." << endl;
exit (1);
}
init();
bool running = true; // set running to true
SDL_Event sdlEvent; // variable to detect SDL events
while (running) // the event loop
{
while (SDL_PollEvent(&sdlEvent))
{
if (sdlEvent.type == SDL_QUIT)
running = false;
}
//update();
draw(hWindow); // call the draw function
}
cleanup();
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(hWindow);
SDL_Quit();
return 0;
}
// simple fragment shader simple.frag
#version 130
// OpenGL 3.2 replace above with #version 150
// Some drivers require the following
precision highp float;
in vec3 ex_Color;
// GLSL versions after 1.3 remove the built in type gl_FragColor
// If using a shader lang version greater than #version 130
// you *may* need to uncomment the following line:
// out vec4 gl_FragColor
void main(void) {
// Pass through original colour
gl_FragColor = vec4(ex_Color,1.0);
}
// simple vert shader - simple.vert
#version 130
// OpenGL3.0
uniform mat4x4 MVP;
in vec3 in_Position;
in vec3 in_Color;
out vec3 ex_Color;
// simple shader program
// multiply each vertex position by the MVP matrix
void main(void){
gl_Position = MVP * vec4(in_Position,1.0);
ex_Color = in_Color;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment