To check if the scene camera works correctly, this tutorial will render for each object in the scene a rotating triangle.
Complete Source Code:
Use the same source files from the previous Tutorial 00: System Interface.
Shader.h
#pragma once
#include <string>
#include <list>
// shader functions
std::string LoadTextFile(const std::string& filepath);
std::string ShaderTypeName(unsigned int shader);
bool CompileShader(unsigned int shader, const std::string& sourcecode);
std::string ShaderInfoLog(unsigned int shader);
bool LinkProgram(unsigned int program, const std::list<unsigned int>& shaderlist);
std::string ProgramInfoLog(unsigned int program);
Shader.cpp
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <GL/glew.h>
std::string LoadTextFile(const std::string & filepath)
{
std::string result(""), line;
std::fstream f(filepath, std::ios::in);
while (f.good())
{
std::getline(f, line);
result += line + '\n';
}
return result;
}
std::string ShaderTypeName(unsigned int shader)
{
if (glIsShader(shader))
{
int type;
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(unsigned int 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;
}
// array of source code components
const char* sourcearray[] = { sourcecode.c_str() };
// set source code
glShaderSource(shader, 1, sourcearray, NULL);
// compile shaders
glCompileShader(shader);
// check compile status
int status;
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(unsigned int shader)
{
if (glIsShader(shader))
{
int logsize;
char infolog[1024] = { 0 };
glGetShaderInfoLog(shader, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid shader";
}
bool LinkProgram(unsigned int program, const std::list<unsigned int>& 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 (auto& shader : shaderlist)
{
if (glIsShader(shader))
glAttachShader(program, shader);
}
// link program
glLinkProgram(program);
// detach all shaders again
for (auto& shader : shaderlist)
{
if (glIsShader(shader))
glDetachShader(program, shader);
}
int status;
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(unsigned int program)
{
if (glIsProgram(program))
{
int logsize;
char infolog[1024] = { 0 };
glGetProgramInfoLog(program, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid program";
}
Orientation.h
#pragma once
#include "Math.h"
class Orientation
{
public:
Orientation(const vec3& position = vec3(0, 0, 0));
virtual ~Orientation();
vec3 Position() const;
vec3 Forward() const;
vec3 Up() const;
vec3 Right() const;
quat Rotation() const;
void SetPosition(const vec3& position);
void Move(const vec3& vector);
void Rotate(float angle, const vec3& axis);
void Roll(float angle);
void Yaw(float angle);
void Pitch(float angle);
mat4 ModelMatrix(const mat4& scale = mat4(1)) const;
mat4 View() const;
protected:
vec3 m_position;
quat m_rotation;
};
Orientation.cpp
#include "Orientation.h"
#define ORIENTATION_DEFAULT_FORWARD (vec3(0, 0, -1))
#define ORIENTATION_DEFAULT_UP (vec3(0, 1, 0))
#define ORIENTATION_DEFAULT_RIGHT (vec3(1, 0, 0))
Orientation::Orientation(const vec3& position)
: m_position(position), m_rotation(1, 0, 0, 0)
{}
Orientation::~Orientation()
{}
vec3 Orientation::Position() const
{
return m_position;
}
vec3 Orientation::Forward() const
{
return glm::rotate(m_rotation, ORIENTATION_DEFAULT_FORWARD);
}
vec3 Orientation::Up() const
{
return glm::rotate(m_rotation, ORIENTATION_DEFAULT_UP);
}
vec3 Orientation::Right() const
{
return glm::rotate(m_rotation, ORIENTATION_DEFAULT_RIGHT);
}
quat Orientation::Rotation() const
{
return m_rotation;
}
void Orientation::SetPosition(const vec3& position)
{
m_position = position;
}
void Orientation::Move(const vec3& vector)
{
m_position += vector;
}
void Orientation::Rotate(float angle, const vec3& axis)
{
m_rotation = glm::rotate(m_rotation, angle, axis);
}
void Orientation::Roll(float angle)
{
Rotate(angle, ORIENTATION_DEFAULT_FORWARD);
}
void Orientation::Yaw(float angle)
{
Rotate(angle, ORIENTATION_DEFAULT_UP);
}
void Orientation::Pitch(float angle)
{
Rotate(angle, ORIENTATION_DEFAULT_RIGHT);
}
mat4 Orientation::ModelMatrix(const mat4& scale) const
{
return translate(m_position) * toMat4(m_rotation) * scale;
}
mat4 Orientation::View() const
{
return lookAt(m_position, m_position + Forward(), Up());
}
Scene.h
#pragma once
#include "Math.h"
#include "Orientation.h"
#include <vector>
struct Object : public Orientation
{
Object(const vec3& position)
: Orientation(position)
{}
// ...
};
struct Scene
{
struct : public Orientation {
float FoV{ radians(45.0f) };
float ZNear{ 0.1f }, ZFar{ 100.0f };
} Camera;
std::vector<Object> Objects;
Scene();
void AnimateNextFrame(float timestep);
};
Scene.cpp
#include "Scene.h"
#include "Main.h"
Scene::Scene()
{
// put the camera back a little ...
Camera.SetPosition(vec3(0, 1, 5));
// create some object ...
Objects.push_back(Object(vec3(-2, 0, -2)));
Objects.push_back(Object(vec3(+2, 0, -2)));
Objects.push_back(Object(vec3(+2, 0, +2)));
Objects.push_back(Object(vec3(-2, 0, +2)));
// ...
}
void Scene::AnimateNextFrame(float timestep)
{
// camera control
// hold right mouse button to rotate the camera
// scroll up/down to move the camera
// ----------------------------------------------------------------------------------------------------------
if (Framework->IsClicked(MOUSE_BUTTON_RIGHT))
{
Camera.Yaw(Framework->EventCursorMoved().x * -0.01f);
Camera.Pitch(Framework->EventCursorMoved().y * +0.01f);
}
if (Framework->ScrolledUp())
Camera.Move(Camera.Forward() * +0.5f);
if (Framework->ScrolledDown())
Camera.Move(Camera.Forward() * -0.5f);
// ----------------------------------------------------------------------------------------------------------
// animate all objects
// ----------------------------------------------------------------------------------------------------------
for (auto& object : Objects)
object.Yaw(radians(360.0f) * timestep);
// ----------------------------------------------------------------------------------------------------------
// ...
}
Model.h
#pragma once
#include "Math.h"
struct Vertex
{
vec3 Position;
vec4 Color;
};
Renderer_OpenGL.h
#pragma once
#include "Renderer.h"
#include <GL/glew.h>
class Renderer_OpenGL : public Renderer
{
public:
Renderer_OpenGL(unsigned int width, unsigned int height);
virtual ~Renderer_OpenGL();
virtual void Render(const Scene& scene);
protected:
unsigned int m_width{ 0 }, m_height{ 0 };
unsigned int m_program{ 0 };
unsigned int m_vertexarray{ 0 };
unsigned int m_vertexbuffer{ 0 };
};
Renderer_OpenGL.cpp
#include "Renderer_OpenGL.h"
#include "Shader.h"
#include "Model.h"
#include <iostream>
Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)
{
// setup program
// ----------------------------------------------------------------------------------------------------------
m_program = glCreateProgram();
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
CompileShader(vertexshader, LoadTextFile("shader.vs"));
CompileShader(fragmentshader, LoadTextFile("shader.fs"));
LinkProgram(m_program, { vertexshader , fragmentshader });
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
// ----------------------------------------------------------------------------------------------------------
// setup vertexarray
// ----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
// ----------------------------------------------------------------------------------------------------------
// setup vertex buffer
// ----------------------------------------------------------------------------------------------------------
Vertex vertices[] = {
{ { 0, 0, 0 },{ 1, 0, 0, 1 } },
{ { 1, 0, 0 },{ 0, 1, 0, 1 } },
{ { 0, 1, 0 },{ 0, 0, 1, 1 } },
};
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
}
Renderer_OpenGL::~Renderer_OpenGL()
{
glDeleteProgram(m_program);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
}
void Renderer_OpenGL::Render(const Scene & scene)
{
glClearColor(0, 0.5, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
// set camera
glUniformMatrix4fv(glGetUniformLocation(m_program, "View"), 1, false, value_ptr(scene.Camera.View()));
glUniformMatrix4fv(glGetUniformLocation(m_program, "Projection"), 1, false, value_ptr(perspective(scene.Camera.FoV, 1.33f, scene.Camera.ZNear, scene.Camera.ZFar)));
// render objects in scene
for (auto& object : scene.Objects)
{
glUniformMatrix4fv(glGetUniformLocation(m_program, "Model"), 1, false, value_ptr(object.ModelMatrix()));
glDrawArrays(GL_TRIANGLES, 0, 3);
}
glBindVertexArray(0);
glUseProgram(0);
}
Shader Source Code:
Vertex Shader: "shader.vs"
#version 450 core
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec4 in_color;
out VS_FS_INTERFACE {
vec4 color;
} vs_out;
uniform mat4 Model = mat4(1);
uniform mat4 View = mat4(1);
uniform mat4 Projection = mat4(1);
void main(void)
{
mat4 MVP = Projection * View * Model;
gl_Position = MVP * vec4(in_position, 1);
vs_out.color = in_color;
}
Fragment Shader: "shader.fs"
#version 450 core
in VS_FS_INTERFACE {
vec4 color;
} vs_out;
layout (location = 0) out vec4 Fragment0;
void main(void)
{
Fragment0 = vs_out.color;
}