Complete Source Code:
Main.cpp
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Scene.h"
#include "Graphics.h"
using namespace std;
Scene scene;
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
Graphics::Resize(width, height);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_E && action == GLFW_PRESS)
{ }
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS)
{ }
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
}
void joystick_callback(int joy, int event)
{
if (event == GLFW_CONNECTED)
{ }
else if (event == GLFW_DISCONNECTED)
{ }
}
int main(int argc, char* argv[])
{
/* Initialize GLFW */
if (!glfwInit())
return -1;
/* Create a window and its OpenGL context */
bool fullscreen = false;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_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 callback functions */
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetJoystickCallback(joystick_callback);
/* Initialize GLEW */
if (glewInit() != GLEW_OK)
{
glfwTerminate();
return -2;
}
/* Show OpenGL context information */
GLContextInfo infos = Graphics::GetContextInfos();
cout << "OpenGL version: " << infos.Version.Major << "." << infos.Version.Minor << endl;
cout << "Driver version: " << infos.Version.Driver << endl;
cout << "GLSL version: " << infos.Version.ShadingLanguage << endl;
cout << "Vendor: " << infos.Vendor << endl;
cout << "Renderer: " << infos.Renderer << endl;
/*cout << "Supported GLSL Versions: " << endl;
for (std::list<std::string>::iterator it = infos.SupportedGLSLVersions.begin(); it != infos.SupportedGLSLVersions.end(); it++)
cout << "GLSL " << *it << endl;
cout << "Supported Extensions: " << endl;
for (std::list<std::string>::iterator it = infos.SupportedExtensions.begin(); it != infos.SupportedExtensions.end(); it++)
cout << "Extension: " << *it << endl;*/
/* Initialize graphics subsystem */
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
if (!Graphics::Initialize(width, height))
{
glfwTerminate();
return -3;
}
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
Graphics::Render(scene);
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
Graphics::CleanUp();
glfwTerminate();
return 0;
}
Orientation.h
#ifndef ORIENTATION_H
#define ORIENTATION_H
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class Orientation
{
public:
explicit Orientation(const glm::vec3& position = glm::vec3(0, 0, 0));
virtual ~Orientation();
glm::vec3 Position() const;
glm::vec3 Forward() const;
glm::vec3 Back() const;
glm::vec3 Left() const;
glm::vec3 Right() const;
glm::vec3 Up() const;
glm::vec3 Down() const;
glm::mat4 Transformation(const glm::vec3& scalefactor = glm::vec3(1, 1, 1)) const;
void SetPosition(const glm::vec3& position);
void Move(const glm::vec3& direction);
void Pitch(float angle);
void Yaw(float angle);
void Roll(float angle);
protected:
glm::vec3 m_position;
glm::quat m_rotation;
};
#endif // ORIENTATION_H
Orientation.cpp
#include "Orientation.h"
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#define DEFAULT_DIRECTION_FORWARD (glm::vec3(0, 0, 1))
#define DEFAULT_DIRECTION_UP (glm::vec3(0, 1, 0))
#define DEFAULT_DIRECTION_RIGHT (glm::vec3(1, 0, 0))
Orientation::Orientation(const glm::vec3& position)
: m_position(position), m_rotation(glm::quat(1, 0, 0, 0))
{}
Orientation::~Orientation()
{}
glm::vec3 Orientation::Position() const
{
return m_position;
}
glm::vec3 Orientation::Forward() const
{
return glm::rotate(m_rotation, DEFAULT_DIRECTION_FORWARD);
}
glm::vec3 Orientation::Back() const
{
return -Forward();
}
glm::vec3 Orientation::Left() const
{
return -Right();
}
glm::vec3 Orientation::Right() const
{
return glm::rotate(m_rotation, DEFAULT_DIRECTION_RIGHT);
}
glm::vec3 Orientation::Up() const
{
return glm::rotate(m_rotation, DEFAULT_DIRECTION_UP);
}
glm::vec3 Orientation::Down() const
{
return -Up();
}
glm::mat4 Orientation::Transformation(const glm::vec3& scalefactor) const
{
return glm::translate(m_position) * glm::toMat4(m_rotation) * glm::scale(scalefactor);
}
void Orientation::SetPosition(const glm::vec3& position)
{
m_position = position;
}
void Orientation::Move(const glm::vec3& direction)
{
m_position += direction;
}
void Orientation::Pitch(float angle)
{
m_rotation = glm::rotate(m_rotation, angle, DEFAULT_DIRECTION_RIGHT);
}
void Orientation::Yaw(float angle)
{
m_rotation = glm::rotate(m_rotation, angle, DEFAULT_DIRECTION_UP);
}
void Orientation::Roll(float angle)
{
m_rotation = glm::rotate(m_rotation, angle, DEFAULT_DIRECTION_FORWARD);
}
Camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include "Orientation.h"
class Camera : public Orientation
{
public:
explicit Camera(const glm::vec3& position = glm::vec3(0, 0, 0));
virtual ~Camera();
float FieldOfView() const;
float ZNear() const;
float ZFar() const;
glm::mat4 View() const;
glm::mat4 Projection(float aspectratio) const;
void ZoomIn(float angle);
void ZoomOut(float angle);
void SetClippingPlanes(float znear, float zfar);
protected:
float m_fieldofview;
float m_znear;
float m_zfar;
};
#endif // CAMERA_H
Camera.cpp
#include "Camera.h"
#include <glm/gtc/matrix_transform.hpp>
Camera::Camera(const glm::vec3& position)
: Orientation(position), m_fieldofview(glm::radians(45.0f)), m_znear(0.1f), m_zfar(100.0f)
{}
Camera::~Camera()
{}
float Camera::FieldOfView() const
{
return m_fieldofview;
}
float Camera::ZNear() const
{
return m_znear;
}
float Camera::ZFar() const
{
return m_zfar;
}
glm::mat4 Camera::View() const
{
return glm::lookAt(m_position, m_position + Forward(), Up());
}
glm::mat4 Camera::Projection(float aspectratio) const
{
return glm::perspective(m_fieldofview, aspectratio, m_znear, m_zfar);
}
void Camera::ZoomIn(float angle)
{
m_fieldofview -= angle;
if (m_fieldofview < glm::radians(1.0f))
m_fieldofview = glm::radians(1.0f);
if (m_fieldofview > glm::radians(90.0f))
m_fieldofview = glm::radians(90.0f);
}
void Camera::ZoomOut(float angle)
{
ZoomIn(-angle);
}
void Camera::SetClippingPlanes(float znear, float zfar)
{
if (znear > 0 && zfar > znear)
{
m_znear = znear;
m_zfar = zfar;
}
}
Scene.h
#ifndef SCENE_H
#define SCENE_H
#include "Camera.h"
#include "Orientation.h"
struct Scene
{
Scene();
Camera camera;
Orientation triangle;
};
#endif // SCENE_H
Scene.cpp
#include "Scene.h"
Scene::Scene()
{
/* put the camera at the positive z-axis */
camera.SetPosition(glm::vec3(0, 0, 5));
/* turn the camera back to the origin */
camera.Yaw(glm::radians(180.0f));
}
Shader.h
#ifndef SHADER_H
#define SHADER_H
#define GLEW_STATIC
#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";
}
Graphics.h
#ifndef GRAPHICS_H
#define GRAPHICS_H
#define GLEW_STATIC
#include <GL/glew.h>
#include <string>
#include <list>
#include "Scene.h"
struct GLContextInfo
{
struct {
GLint Major, Minor;
std::string Driver;
std::string ShadingLanguage;
} Version;
std::string Vendor;
std::string Renderer;
std::list<std::string> SupportedExtensions;
std::list<std::string> SupportedGLSLVersions;
};
namespace Graphics
{
bool Initialize(unsigned int width, unsigned int height);
void CleanUp();
void Render(const Scene& scene);
void Resize(unsigned int width, unsigned int height);
GLContextInfo GetContextInfos();
void CheckForGLError();
};
#endif // GRAPHICS_H
Graphics.cpp
#include "Graphics.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <iostream>
#include <vector>
#include "Shader.h"
using namespace std;
using namespace glm;
struct Vertex {
vec3 Position;
vec4 Color;
Vertex(const vec3& position, const vec4& color)
: Position(position), Color(color)
{}
};
uvec2 framebuffersize(0, 0);
GLuint program = 0;
GLuint vertexshader = 0;
GLuint fragmentshader = 0;
GLuint vertexarray = 0;
GLuint vertexbuffer = 0;
GLint uniformlocation_Model = -1;
GLint uniformlocation_View = -1;
GLint uniformlocation_Projection = -1;
bool Graphics::Initialize(unsigned int width, unsigned int height)
{
/* set view port */
Resize(width, height);
/* general settings */
glClearColor(0.3f, 0.3f, 0.3f, 0.0f); /* background gray */
/* create all objects */
program = glCreateProgram();
vertexshader = glCreateShader(GL_VERTEX_SHADER);
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glGenVertexArrays(1, &vertexarray);
glGenBuffers(1, &vertexbuffer);
/* shader source */
string vertexshader_source(
"#version 450 core\n"
"in layout (location = 0) vec3 in_position;"
"in layout (location = 1) vec4 in_color;"
"uniform mat4 Model = mat4(1);"
"uniform mat4 View = mat4(1);"
"uniform mat4 Projection = mat4(1);"
"smooth out vec4 color;"
"void main() {"
"mat4 MVP = Projection * View * Model;"
"gl_Position = MVP * vec4(in_position, 1);"
"color = in_color;"
"}"
);
string fragmentshader_source(
"#version 450 core\n"
"smooth in vec4 color;"
"out layout (location = 0) vec4 out_color;"
"void main() {"
"out_color = color;"
"}"
);
/* compile shaders */
if (!CompileShader(vertexshader, vertexshader_source))
return false;
if (!CompileShader(fragmentshader, fragmentshader_source))
return false;
/* link program */
list<GLuint> shaderlist;
shaderlist.push_back(vertexshader);
shaderlist.push_back(fragmentshader);
if (!LinkProgram(program, shaderlist))
return false;
/* query uniform locations */
uniformlocation_Model = glGetUniformLocation(program, "Model");
uniformlocation_View = glGetUniformLocation(program, "View");
uniformlocation_Projection = glGetUniformLocation(program, "Projection");
/* setup vertexarray */
glBindVertexArray(vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, Position)));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, Color)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
/* setup vertexbuffer */
vector<Vertex> vertices;
vertices.push_back(Vertex(vec3(0, 0, 0), vec4(1, 0, 0, 1)));
vertices.push_back(Vertex(vec3(1, 0, 0), vec4(0, 1, 0, 1)));
vertices.push_back(Vertex(vec3(0, 1, 0), vec4(0, 0, 1, 1)));
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
/* successfully initialized */
return true;
}
void Graphics::CleanUp()
{
/* destroy all objects */
glDeleteProgram(program);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
glDeleteVertexArrays(1, &vertexarray);
glDeleteBuffers(1, &vertexbuffer);
}
void Graphics::Render(const Scene& scene)
{
float aspectratio= (float)framebuffersize.x / framebuffersize.y;
mat4 Model = scene.triangle.Transformation();
mat4 View = scene.camera.View();
mat4 Projection = scene.camera.Projection(aspectratio);
glProgramUniformMatrix4fv(program, uniformlocation_Model, 1, GL_FALSE, value_ptr(Model));
glProgramUniformMatrix4fv(program, uniformlocation_View, 1, GL_FALSE, value_ptr(View));
glProgramUniformMatrix4fv(program, uniformlocation_Projection, 1, GL_FALSE, value_ptr(Projection));
/* clear framebuffer */
glClear(GL_COLOR_BUFFER_BIT);
/* draw triangle */
glUseProgram(program);
glBindVertexArray(vertexarray);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glUseProgram(0);
CheckForGLError();
}
void Graphics::Resize(unsigned int width, unsigned int height)
{
framebuffersize = uvec2(width, height);
glViewport(0, 0, width, height);
}
GLContextInfo Graphics::GetContextInfos()
{
GLContextInfo infos;
glGetIntegerv(GL_MAJOR_VERSION, &infos.Version.Major);
glGetIntegerv(GL_MINOR_VERSION, &infos.Version.Minor);
infos.Version.Driver = (const char*)glGetString(GL_VERSION);
infos.Vendor = (const char*)glGetString(GL_VENDOR);
infos.Renderer = (const char*)glGetString(GL_RENDERER);
infos.Version.ShadingLanguage = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
GLint numberofextensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numberofextensions);
for (int i = 0; i < numberofextensions; i++)
infos.SupportedExtensions.push_back((const char*)glGetStringi(GL_EXTENSIONS, i));
GLint numberofsupportedglslversions = 0;
glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &numberofsupportedglslversions);
for (int i = 0; i < numberofsupportedglslversions; i++)
infos.SupportedGLSLVersions.push_back((const char*)glGetStringi(GL_SHADING_LANGUAGE_VERSION, i));
return infos;
}
void Graphics::CheckForGLError()
{
for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)
{
cout << "OpenGL Error: \t";
if (error == GL_INVALID_ENUM)
cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
cout << "GL_INVALID_OPERATION";
if (error == GL_STACK_OVERFLOW)
cout << "GL_STACK_OVERFLOW";
if (error == GL_STACK_UNDERFLOW)
cout << "GL_STACK_UNDERFLOW";
if (error == GL_OUT_OF_MEMORY)
cout << "GL_OUT_OF_MEMORY";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_CONTEXT_LOST)
cout << "GL_CONTEXT_LOST";
cout << (char)7 << endl; /*play sound*/
cin.get();
}
}