Created
November 7, 2011 21:42
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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