We are adding a uniform Material:
/* set lighting properties */
Material material{
vec3(0, 1, 0), // diffuse
vec3(1, 1, 1), // specular
1 // shininess
};
The diffuse color is green, the specular color is white (unused).
The scene has no light source yet, the reason why the cube isnt totally black is that the scene has a "ambient" intensity, that is light comming from everywhere, together with te diffuse color it results in a dark-green cube.
The fragment shader already has the final lighting equation:
// final fragment color
vec3 color = Kd * (Ia + Id) + Ks * Is;
Since we dont have any light sources, the diffuse (Id) and specular intensity (Is) are 0:
// light parts
vec3 Ia = AmbientIntensity;
vec3 Id = vec3(0, 0, 0);
vec3 Is = vec3(0, 0, 0);
Complete Source Code:
Scene.h
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <list>
struct Object
{
glm::vec3 Position{ 0, 0, 0 };
glm::quat Rotation{ 1, 0, 0, 0 };
glm::vec3 Size{ 1, 1, 1 };
};
struct Scene
{
Scene();
struct {
glm::vec3 Position{ 2, 2, 5 };
glm::vec3 Forward{ 0, 0, -1 };
glm::vec3 Up{ 0, 1, 0 };
float FieldOfView{ glm::radians(45.0f) };
float ZNear{ 0.1f };
float ZFar{ 100.0f };
} Camera;
std::list<Object> Objects;
glm::vec3 AmbientIntensity{ 0.2f, 0.2f, 0.2f };
};
Graphics.h
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "Scene.h"
struct Vertex
{
glm::vec3 Position;
glm::vec3 Normal;
};
struct Material
{
glm::vec3 Kd;
glm::vec3 Ks;
float Ns;
};
class Graphics
{
public:
Graphics();
virtual ~Graphics();
bool Initialize(unsigned int width, unsigned int height);
void Render(const Scene& scene);
void CleanUp();
void SetWindowSize(unsigned int width, unsigned int height);
protected:
static void CheckForGLError();
glm::uvec2 m_windowsize{ 0, 0 };
GLuint m_program = 0;
GLuint m_vertexshader = 0;
GLuint m_fragmentshader = 0;
GLuint m_vertexarray = 0;
GLuint m_vertexbuffer = 0;
struct {
GLint Model{ -1 };
GLint View{ -1 };
GLint Projection{ -1 };
GLint material_Kd{ -1 };
GLint material_Ks{ -1 };
GLint material_Ns{ -1 };
GLint AmbientIntensity{ -1 };
GLint CameraPosition{ -1 };
} m_uniformlocation;
};
Graphics.cpp
#include "Graphics.h"
#include "Shader.h"
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/quaternion.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace glm;
Graphics::Graphics()
{}
Graphics::~Graphics()
{}
bool Graphics::Initialize(unsigned int width, unsigned int height)
{
// cleanup previously allocated objects
CleanUp();
// background color
glClearColor(0.3f, 0.5f, 0.8f, 0.0f);
glEnable(GL_DEPTH_TEST);
SetWindowSize(width, height);
// create all objects
m_program = glCreateProgram();
m_vertexshader = glCreateShader(GL_VERTEX_SHADER);
m_fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
// compile shaders and link program
if (!CompileShader(m_vertexshader, LoadTextFile("model.vertexshader.txt")))
{ cin.get(); return false; }
if (!CompileShader(m_fragmentshader, LoadTextFile("model.fragmentshader.txt")))
{ cin.get(); return false; }
if (!LinkProgram(m_program, { m_vertexshader, m_fragmentshader }))
{ cin.get(); return false; }
// query uniform locations
m_uniformlocation.Model = glGetUniformLocation(m_program, "Model");
m_uniformlocation.View = glGetUniformLocation(m_program, "View");
m_uniformlocation.Projection = glGetUniformLocation(m_program, "Projection");
m_uniformlocation.material_Kd = glGetUniformLocation(m_program, "material.Kd");
m_uniformlocation.material_Ks = glGetUniformLocation(m_program, "material.Ks");
m_uniformlocation.material_Ns = glGetUniformLocation(m_program, "material.Ns");
m_uniformlocation.AmbientIntensity = glGetUniformLocation(m_program, "AmbientIntensity");
m_uniformlocation.CameraPosition = glGetUniformLocation(m_program, "CameraPosition");
// setup vertex array
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(float) * 3));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
// setup vertex buffer (a simple triangularized cube)
vector<Vertex> vertices = {
/* +x */
{ { +1, -1, -1 }, { +1, 0, 0 } },
{ { +1, +1, -1 }, { +1, 0, 0 } },
{ { +1, +1, +1 }, { +1, 0, 0 } },
{ { +1, -1, -1 }, { +1, 0, 0 } },
{ { +1, +1, +1 }, { +1, 0, 0 } },
{ { +1, -1, +1 }, { +1, 0, 0 } },
/* -x */
{ { -1, +1, -1 }, { -1, 0, 0 } },
{ { -1, -1, -1 }, { -1, 0, 0 } },
{ { -1, -1, +1 }, { -1, 0, 0 } },
{ { -1, +1, -1 }, { -1, 0, 0 } },
{ { -1, -1, +1 }, { -1, 0, 0 } },
{ { -1, +1, +1 }, { -1, 0, 0 } },
/* +y */
{ { +1, +1, -1 }, { 0, +1, 0 } },
{ { -1, +1, -1 }, { 0, +1, 0 } },
{ { -1, +1, +1 }, { 0, +1, 0 } },
{ { +1, +1, -1 }, { 0, +1, 0 } },
{ { -1, +1, +1 }, { 0, +1, 0 } },
{ { +1, +1, +1 }, { 0, +1, 0 } },
/* -y */
{ { -1, -1, -1 }, { 0, -1, 0 } },
{ { +1, -1, -1 }, { 0, -1, 0 } },
{ { +1, -1, +1 }, { 0, -1, 0 } },
{ { -1, -1, -1 }, { 0, -1, 0 } },
{ { +1, -1, +1 }, { 0, -1, 0 } },
{ { -1, -1, +1 }, { 0, -1, 0 } },
/* +z */
{ { -1, -1, +1 }, { 0, 0, +1 } },
{ { +1, -1, +1 }, { 0, 0, +1 } },
{ { +1, +1, +1 }, { 0, 0, +1 } },
{ { -1, -1, +1 }, { 0, 0, +1 } },
{ { +1, +1, +1 }, { 0, 0, +1 } },
{ { -1, +1, +1 }, { 0, 0, +1 } },
/* -z */
{ { +1, -1, -1 }, { 0, 0, -1 } },
{ { -1, -1, -1 }, { 0, 0, -1 } },
{ { -1, +1, -1 }, { 0, 0, -1 } },
{ { +1, -1, -1 }, { 0, 0, -1 } },
{ { -1, +1, -1 }, { 0, 0, -1 } },
{ { +1, +1, -1 }, { 0, 0, -1 } },
};
glBindBuffer(GL_ARRAY_BUFFER, m_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::Render(const Scene& scene)
{
/* set lighting properties */
Material material{
vec3(0, 1, 0), // diffuse
vec3(1, 1, 1), // specular
1 // shininess
};
glProgramUniform3fv(m_program, m_uniformlocation.material_Kd, 1, value_ptr(material.Kd));
glProgramUniform3fv(m_program, m_uniformlocation.material_Ks, 1, value_ptr(material.Ks));
glProgramUniform1f(m_program, m_uniformlocation.material_Ns, material.Ns);
glProgramUniform3fv(m_program, m_uniformlocation.AmbientIntensity, 1, value_ptr(scene.AmbientIntensity));
glProgramUniform3fv(m_program, m_uniformlocation.CameraPosition, 1, value_ptr(scene.Camera.Position));
/* set scene camera matrices */
float aspectratio = (float)m_windowsize.x / m_windowsize.y;
mat4 View = lookAt(scene.Camera.Position, scene.Camera.Position + scene.Camera.Forward, scene.Camera.Up);
mat4 Projection = perspective(scene.Camera.FieldOfView, aspectratio, scene.Camera.ZNear, scene.Camera.ZFar);
glProgramUniformMatrix4fv(m_program, m_uniformlocation.View, 1, false, value_ptr(View));
glProgramUniformMatrix4fv(m_program, m_uniformlocation.Projection, 1, false, value_ptr(Projection));
/* clear framebuffer */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
/* draw objects */
for (auto& object : scene.Objects)
{
mat4 Model = translate(object.Position) * toMat4(object.Rotation) * scale(object.Size);
glProgramUniformMatrix4fv(m_program, m_uniformlocation.Model, 1, false, value_ptr(Model));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(0);
glUseProgram(0);
/* check for any error */
CheckForGLError();
}
void Graphics::CleanUp()
{
// destroy all objects
glDeleteProgram(m_program);
glDeleteShader(m_vertexshader);
glDeleteShader(m_fragmentshader);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
}
void Graphics::SetWindowSize(unsigned int width, unsigned int height)
{
if (width < 100)
width = 100;
if (height < 100)
height = 100;
m_windowsize = glm::uvec2(width, height);
glViewport(0, 0, m_windowsize.x, m_windowsize.y);
}
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();
}
}
Shader Source Code:
Fragment Shader: "model.fragmentshader.txt"
#version 450 core
struct Material {
vec3 Kd; // diffuse
vec3 Ks; // specular
float Ns; // shininess
};
in VS_FS_INTERFACE {
vec3 position;
vec3 normal;
} vertex;
// example material and other lighting properties
uniform Material material = Material ( vec3(0, 0, 0), vec3(0, 0, 0), 0 );
uniform vec3 AmbientIntensity = vec3(0, 0, 0);
uniform vec3 CameraPosition = vec3(0, 0, 0);
out layout (location = 0) vec4 out_color;
void main ()
{
// material
vec3 Kd = material.Kd;
vec3 Ks = material.Ks;
float Ns = material.Ns;
// light parts
vec3 Ia = AmbientIntensity;
vec3 Id = vec3(0, 0, 0);
vec3 Is = vec3(0, 0, 0);
// process light sources here ...
// final fragment color
vec3 color = Kd * (Ia + Id) + Ks * Is;
out_color = vec4(color, 1);
}