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";
}