1. Loading Images (with SOIL)

Drop an image file onto the window to load it.

Main.cpp

#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <SOIL.h>

#include <glm/glm.hpp>

#include <iostream>

#include <vector>

#include <string>

#include "Shader.h"

using namespace std;

using namespace glm;

/* callbacks */

/******************************************************************************************************/

void error_callback(int error, const char* description);

void drop_callback(GLFWwindow* window, int pathcount, const char** paths);

void framebuffer_size_callback(GLFWwindow* window, int width, int height);

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

/******************************************************************************************************/

/* main functions */

/******************************************************************************************************/

#define CheckForGLErrors CheckForGLErrors__(__FILE__, __LINE__);

bool Initialize();

void Render();

void CleanUp();

void CheckForGLErrors__(const char* file, unsigned int line);

/* debug callback: gets called by OpenGL automatically */

void APIENTRY OpenGLDebugCallback(

GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam);

/******************************************************************************************************/

/* global variables */

/******************************************************************************************************/

enum Buffers {

Buffer_Vertices,

MAX_Buffers,

};

enum Queries {

/* none */

MAX_Queries,

};

enum Renderbuffers {

/* none */

MAX_Renderbuffers,

};

enum Samplers {

/* none */

MAX_Samplers,

};

enum Textures {

Texture_Vertices,

MAX_Textures,

};

enum Programs {

Program_Vertices,

MAX_Programs,

};

enum Framebuffers {

/* none */

MAX_Framebuffers,

};

enum TransformFeedbacks {

/* none */

MAX_TransformFeedbacks,

};

enum VertexArrays {

VertexArray_Vertices,

MAX_VertexArrays,

};

/* objects sharable between contexts */

GLuint buffers [MAX_Buffers + 1];

GLuint queries [MAX_Queries + 1];

GLuint renderbuffers [MAX_Renderbuffers + 1];

GLuint samplers [MAX_Samplers + 1];

GLuint textures [MAX_Textures + 1];

GLuint programs [MAX_Programs + 1];

/* container objects */

GLuint framebuffers [MAX_Framebuffers + 1];

GLuint transformfeedbacks [MAX_TransformFeedbacks + 1];

GLuint vertexarrays [MAX_VertexArrays + 1];

/* NOTE: +1 necessary to avoid compile errors if MAX_... = 0 */

struct DebugUserParams {

bool ShowNotifications = false;

} debuguserparams;

/******************************************************************************************************/

/******************************************************************************************************/

int main(int argc, char* argv[])

{

/* Initialize GLFW */

if (!glfwInit())

return 1;

/* Create a window and its OpenGL context */

bool fullscreen = false;

bool debugcontext = true;

bool compatibilityprofilecontext = false;

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);

glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, debugcontext ? GLFW_TRUE : GLFW_FALSE);

glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, compatibilityprofilecontext ? GLFW_TRUE : GLFW_FALSE);

glfwWindowHint(GLFW_OPENGL_PROFILE, compatibilityprofilecontext ? GLFW_OPENGL_COMPAT_PROFILE : GLFW_OPENGL_CORE_PROFILE);

GLFWmonitor* monitor = fullscreen ? glfwGetPrimaryMonitor() : NULL;

GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", monitor, NULL);

if (!window)

{

glfwTerminate();

return 1;

}

/* Make the window's context current */

glfwMakeContextCurrent(window);

/* Set V-sync */

bool vsync = true;

glfwSwapInterval(vsync ? 1 : 0);

/* Set callback functions */

glfwSetErrorCallback(error_callback);

glfwSetDropCallback(window, drop_callback);

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

glfwSetKeyCallback(window, key_callback);

glfwSetCursorPosCallback(window, cursor_position_callback);

glfwSetMouseButtonCallback(window, mouse_button_callback);

glfwSetScrollCallback(window, scroll_callback);

/* Initialize GLEW */

if (glewInit() != GLEW_OK)

{

glfwTerminate();

return 2;

}

/* Register debug callback if debug context is available */

GLint contextflags = 0;

glGetIntegerv(GL_CONTEXT_FLAGS, &contextflags);

if (contextflags & GL_CONTEXT_FLAG_DEBUG_BIT)

{

glEnable(GL_DEBUG_OUTPUT);

glDebugMessageCallback(OpenGLDebugCallback, &debuguserparams);

}

/* Initialize graphics */

if (!Initialize())

{

glfwTerminate();

return 3;

}

/* Loop until the user closes the window */

while (!glfwWindowShouldClose(window))

{

/* Render here */

Render();

/* Swap front and back buffers */

glfwSwapBuffers(window);

/* Poll for and process events */

glfwPollEvents();

}

/* Clean up graphics */

CleanUp();

glfwTerminate();

return 0;

}

/******************************************************************************************************/

/* input */

/******************************************************************************************************/

void error_callback(int error, const char* description)

{

cout << "GLFW Error: " << description << endl;

cin.get();

}

void drop_callback(GLFWwindow* window, int pathcount, const char** paths)

{

for (unsigned int i = 0; i < pathcount; i++)

{

int width = 0, height = 0, channels = 0;

unsigned char* imagedata = SOIL_load_image(paths[i], &width, &height, &channels, 4);

if (imagedata)

{

glBindTexture(GL_TEXTURE_2D, textures[Texture_Vertices]);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imagedata);

glBindTexture(GL_TEXTURE_2D, 0);

glfwSetWindowSize(window, 800, (800 * height) / width);

cout << "loading image: \t"<< paths[i] << ": \t" << width << " x " << height << endl;

break;

}

else

cout << "cant load image: \t"<< paths[i] << endl;

SOIL_free_image_data(imagedata);

}

}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)

{

glViewport(0, 0, width, height);

}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)

{

if (action == GLFW_PRESS)

{

if (key == GLFW_KEY_ESCAPE)

glfwSetWindowShouldClose(window, GLFW_TRUE);

}

}

void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)

{}

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)

{

if (action == GLFW_PRESS)

{

if (button == GLFW_MOUSE_BUTTON_RIGHT)

{}

}

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)

{}

/******************************************************************************************************/

/* main functions */

/******************************************************************************************************/

bool Initialize()

{

/* create all objects */

glGenBuffers(MAX_Buffers, buffers);

glGenQueries(MAX_Queries, queries);

glGenRenderbuffers(MAX_Renderbuffers, renderbuffers);

glGenSamplers(MAX_Samplers, samplers);

glGenTextures(MAX_Textures, textures);

for (unsigned int i = 0; i < MAX_Programs; i++)

programs[i] = glCreateProgram();

glGenFramebuffers(MAX_Framebuffers, framebuffers);

glGenTransformFeedbacks(MAX_TransformFeedbacks, transformfeedbacks);

glGenVertexArrays(MAX_VertexArrays, vertexarrays);

/* setup program */

string vertexshader_source = {

"#version 450 core\n"

"layout (location = 0) in vec3 in_position;"

"layout (location = 1) in vec2 in_texcoord;"

"smooth out vec2 interpolated_texcoord;"

"void main() {"

"gl_Position = vec4(in_position, 1);"

"interpolated_texcoord = in_texcoord;"

"}"

};

string fragmentshader_source = {

"#version 450 core\n"

"layout (binding = 1) uniform sampler2D tex1;"

"smooth in vec2 interpolated_texcoord;"

"layout (location = 0) out vec4 out_color;"

"void main() {"

"out_color = texture(tex1, interpolated_texcoord);"

"}"

};

bool success = true;

GLuint vertexshader = glCreateShader(GL_VERTEX_SHADER);

GLuint fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

if (!CompileShader(vertexshader, vertexshader_source))

success = false;

if (!CompileShader(fragmentshader, fragmentshader_source))

success = false;

if (!LinkProgram(programs[Program_Vertices], { vertexshader, fragmentshader }))

success = false;

glDeleteShader(vertexshader);

glDeleteShader(fragmentshader);

if (!success)

return false;

/* setup vertex array */

struct Vertex {

vec3 Position;

vec2 TexCoord;

};

glBindVertexArray(vertexarrays[VertexArray_Vertices]);

glBindBuffer(GL_ARRAY_BUFFER, buffers[Buffer_Vertices]);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, Position)));

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, TexCoord)));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glEnableVertexAttribArray(0);

glEnableVertexAttribArray(1);

glBindVertexArray(0);

/* setup vertex buffer */

vector<Vertex> vertices = {

{ { -1, -1, 0 }, { 0, 1 } },

{ { +1, -1, 0 }, { 1, 1 } },

{ { +1, +1, 0 }, { 1, 0 } },

{ { -1, -1, 0 }, { 0, 1 } },

{ { +1, +1, 0 }, { 1, 0 } },

{ { -1, +1, 0 }, { 0, 0 } },

};

glBindBuffer(GL_ARRAY_BUFFER, buffers[Buffer_Vertices]);

glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);

/* setup texture */

unsigned char texturedata[] = {

0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,

0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,

};

glBindTexture(GL_TEXTURE_2D, textures[Texture_Vertices]);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texturedata);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glBindTexture(GL_TEXTURE_2D, 0);

glBindTextureUnit(1, textures[Texture_Vertices]);

/* successfully initialized */

return true;

}

void Render()

{

/* clear frame buffer */

glClearColor(0, 0, 0.5f, 0);

glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

/* draw triangle */

glUseProgram(programs[Program_Vertices]);

glBindVertexArray(vertexarrays[VertexArray_Vertices]);

glDrawArrays(GL_TRIANGLES, 0, 6);

glBindVertexArray(0);

glUseProgram(0);

CheckForGLErrors

}

void CleanUp()

{

/* destroy all objects */

glDeleteBuffers(MAX_Buffers, buffers);

glDeleteQueries(MAX_Queries, queries);

glDeleteRenderbuffers(MAX_Renderbuffers, renderbuffers);

glDeleteSamplers(MAX_Samplers, samplers);

glDeleteTextures(MAX_Textures, textures);

for (unsigned int i = 0; i < MAX_Programs; i++)

glDeleteProgram(programs[i]);

glDeleteFramebuffers(MAX_Framebuffers, framebuffers);

glDeleteTransformFeedbacks(MAX_TransformFeedbacks, transformfeedbacks);

glDeleteVertexArrays(MAX_VertexArrays, vertexarrays);

}

void CheckForGLErrors__(const char* file, unsigned int line)

{

string errorstring;

for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)

{

if (error == GL_INVALID_ENUM)

errorstring += " GL_INVALID_ENUM";

if (error == GL_INVALID_VALUE)

errorstring += " GL_INVALID_VALUE";

if (error == GL_INVALID_OPERATION)

errorstring += " GL_INVALID_OPERATION";

if (error == GL_STACK_OVERFLOW)

errorstring += " GL_STACK_OVERFLOW";

if (error == GL_STACK_UNDERFLOW)

errorstring += " GL_STACK_UNDERFLOW";

if (error == GL_OUT_OF_MEMORY)

errorstring += " GL_OUT_OF_MEMORY";

if (error == GL_INVALID_FRAMEBUFFER_OPERATION)

errorstring += " GL_INVALID_FRAMEBUFFER_OPERATION";

if (error == GL_CONTEXT_LOST)

errorstring += " GL_CONTEXT_LOST";

}

if (!errorstring.empty())

{

cout

<< (char)7

<< "OpenGL Error: " << endl

<< "\tLine: \t" << line << endl

<< "\tFile: \t" << file << endl

<< "\tErrors: \t" << errorstring << endl << endl;

cin.get();

}

}

/* debug */

void APIENTRY OpenGLDebugCallback(

GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam)

{

/* debug user params */

DebugUserParams* params = (DebugUserParams*)userparam;

/* filter out unnecessary warnings */

if (!params->ShowNotifications)

if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)

return;

/* source */

string str_source;

if (source == GL_DEBUG_SOURCE_API) str_source = "API";

if (source == GL_DEBUG_SOURCE_WINDOW_SYSTEM) str_source = "Window System";

if (source == GL_DEBUG_SOURCE_SHADER_COMPILER) str_source = "Shader Compiler";

if (source == GL_DEBUG_SOURCE_THIRD_PARTY) str_source = "Third Party";

if (source == GL_DEBUG_SOURCE_APPLICATION) str_source = "Application";

if (source == GL_DEBUG_SOURCE_OTHER) str_source = "Other";

/* type */

string str_type;

if (type == GL_DEBUG_TYPE_ERROR) str_type = "Error";

if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) str_type = "Deprecated Behavior";

if (type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR) str_type = "Undefined Behavior";

if (type == GL_DEBUG_TYPE_PORTABILITY) str_type = "Portability";

if (type == GL_DEBUG_TYPE_PERFORMANCE) str_type = "Performance";

if (type == GL_DEBUG_TYPE_MARKER) str_type = "Marker";

if (type == GL_DEBUG_TYPE_PUSH_GROUP) str_type = "Push Group";

if (type == GL_DEBUG_TYPE_POP_GROUP) str_type = "Pop Group";

if (type == GL_DEBUG_TYPE_OTHER) str_type = "Other";

/* severity */

string str_severity;

if (severity == GL_DEBUG_SEVERITY_HIGH) str_severity = "High";

if (severity == GL_DEBUG_SEVERITY_MEDIUM) str_severity = "Medium";

if (severity == GL_DEBUG_SEVERITY_LOW) str_severity = "Low";

if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) str_severity = "Notification";

/* print message */

cout << "OpenGL Debug Message:" << endl;

cout << "----------------------------------" << endl;

cout << "ID: \t\t" << id << endl;

cout << "Source: \t" << str_source << endl;

cout << "Type: \t\t" << str_type << endl;

cout << "Severity: \t" << str_severity << endl;

cout << "Message: \t" << message << endl;

cout << "----------------------------------" << endl << endl;

}

/******************************************************************************************************/

Shader.h

#ifndef SHADER_H

#define SHADER_H

#include <GL/glew.h>

#include <string>

#include <list>

std::string LoadTextFile(const std::string& filepath);

std::string ShaderTypeName(GLuint shader);

bool CompileShader(GLuint shader, const std::string& sourcecode);

std::string ShaderInfoLog(GLuint shader);

bool LinkProgram(GLuint program, const std::list<GLuint>& shaderlist);

std::string ProgramInfoLog(GLuint program);

#endif // SHADER_H

Shader.cpp

#include "Shader.h"

#include <iostream>

#include <fstream>

std::string LoadTextFile(const std::string & filepath)

{

std::string result(""), line;

std::fstream f(filepath.c_str(), std::ios::in);

while (f.good())

{

std::getline(f, line);

result += line + '\n';

}

return result;

}

std::string ShaderTypeName(GLuint shader)

{

if (glIsShader(shader))

{

GLint type = 0;

glGetShaderiv(shader, GL_SHADER_TYPE, &type);

if (type == GL_VERTEX_SHADER)

return "Vertex Shader";

if (type == GL_TESS_CONTROL_SHADER)

return "Tessellation Control Shader";

if (type == GL_TESS_EVALUATION_SHADER)

return "Tessellation Evaluation Shader";

if (type == GL_GEOMETRY_SHADER)

return "Geometry Shader";

if (type == GL_FRAGMENT_SHADER)

return "Fragment Shader";

if (type == GL_COMPUTE_SHADER)

return "Compute Shader";

}

return "invalid shader";

}

bool CompileShader(GLuint shader, const std::string& sourcecode)

{

if (!glIsShader(shader))

{

std::cout << "ERROR: shader compilation failed, no valid shader specified" << std::endl;

return false;

}

if (sourcecode.empty())

{

std::cout << "ERROR: shader compilation failed, no source code specified (" << ShaderTypeName(shader) << ")" << std::endl;

return false;

}

const char* sourcearray[] = { sourcecode.c_str() };

glShaderSource(shader, 1, sourcearray, NULL);

glCompileShader(shader);

// check compile status

GLint status = 0;

glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

// successfully compiled shader

if (status == GL_TRUE)

return true;

// show compile errors

std::cout << "ERROR: shader compilation failed (" << ShaderTypeName(shader) << ")" << std::endl << ShaderInfoLog(shader) << std::endl;

return false;

}

std::string ShaderInfoLog(GLuint shader)

{

if (glIsShader(shader))

{

GLint logsize = 0;

GLchar infolog[1024] = { 0 };

glGetShaderInfoLog(shader, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid shader";

}

bool LinkProgram(GLuint program, const std::list<GLuint>& shaderlist)

{

if (!glIsProgram(program))

{

std::cout << "ERROR: shader linking failed, no valid program specified" << std::endl;

return false;

}

// attach all shaders to the program

for (std::list<GLuint>::const_iterator it = shaderlist.begin(); it != shaderlist.end(); it++)

{

if (glIsShader(*it))

glAttachShader(program, *it);

}

// link program

glLinkProgram(program);

// detach all shaders again

for (std::list<GLuint>::const_iterator it = shaderlist.begin(); it != shaderlist.end(); it++)

{

if (glIsShader(*it))

glDetachShader(program, *it);

}

GLint status = 0;

glGetProgramiv(program, GL_LINK_STATUS, &status);

// successfully linked program

if (status == GL_TRUE)

return true;

// show link errors

std::cout << "ERROR: shader linking failed" << std::endl << ProgramInfoLog(program) << std::endl;

return false;

}

std::string ProgramInfoLog(GLuint program)

{

if (glIsProgram(program))

{

GLint logsize = 0;

GLchar infolog[1024] = { 0 };

glGetProgramInfoLog(program, 1024, &logsize, infolog);

return std::string(infolog);

}

return "invalid program";

}