For each vertex of a face, the resulting fragment color will be calculated, then the average color of all results will be given to each pixel.
The surface normals is the "geometric" normal of the surface, for example for a triangle it is the normalized cross product of 2 edge vectors.
NOTE:
Here i will use a new shader type, the "Geometry Shader" to have access to all vertices of a face at the same time.
That is necessary to calculate (precisely) the average color for a certain face.
The next tutorial "Gouraud Shading" will also make use of that shader type, but all further tutorials wont.
https://www.opengl.org/wiki/Geometry_Shader
To have access to all materials that our model will have, we will put all materials in a buffer and bind that buffer to a "uniform block" in our shader.
In addition to that, we will use 3 further buffers for the 3 diferent Light Source types.
https://www.opengl.org/wiki/Interface_Block_(GLSL)#Memory_layout
Complete Source Code:
Model.h
#pragma once
#include "Math.h"
#include <vector>
struct Vertex
{
vec3 Position;
vec2 TexCoord;
vec3 Normal;
int MaterialIndex;
};
struct ModelInstance
{
mat4 ModelMatrix;
int ObjectID;
};
// those padding variables are there to match the required memory layout for "std140":
// arrays of structs must be rounded up to a multiple of 16byte (or sizeof(vec4))
// vec3 are actually consuming sizeof(vec4) in GLSL
struct Material
{
vec3 Ka;
float PADDING1;
vec3 Kd;
float PADDING2;
vec3 Ks;
float PADDING3;
float Ns{ 1 };
float d{ 1 };
float PADDING4;
float PADDING5;
Material();
Material(const vec3& ambient, const vec3& diffuse, const vec3& specular, float shininess, float transparency);
};
struct DirectionalLight
{
vec3 Direction;
float PADDING1;
vec3 Intensity;
float PADDING2;
DirectionalLight(const vec3& direction, const vec3& intensity);
};
struct PointLight
{
vec3 Position;
float PADDING1;
vec3 Intensity;
float PADDING2;
float AttenuationLinear{ 0.5 };
float PADDING3;
float PADDING4;
float PADDING5;
PointLight(const vec3& position, const vec3& intensity, float attenuationlinear);
};
struct SpotLight
{
vec3 Position;
float PADDING1;
vec3 Direction;
float PADDING2;
vec3 Intensity;
float PADDING3;
int Exponent{ 1 };
float PADDING4;
float PADDING5;
float PADDING6;
SpotLight(const vec3& position, const vec3& direction, const vec3& intensity, int exponent);
};
void LoadSphere(
std::vector<Vertex>& vertexarray,
std::vector<Material>& materialarray,
unsigned int sectors, unsigned int slices);
Model.cpp
#include "Model.h"
Material::Material()
{}
Material::Material(const vec3 & ambient, const vec3 & diffuse, const vec3 & specular, float shininess, float transparency)
{
Ka.r = max2(0, min2(1, ambient.r));
Ka.g = max2(0, min2(1, ambient.g));
Ka.b = max2(0, min2(1, ambient.b));
Kd.r = max2(0, min2(1, diffuse.r));
Kd.g = max2(0, min2(1, diffuse.g));
Kd.b = max2(0, min2(1, diffuse.b));
Ks.r = max2(0, min2(1, specular.r));
Ks.g = max2(0, min2(1, specular.g));
Ks.b = max2(0, min2(1, specular.b));
Ns = max2(1, shininess);
d = max2(0, min2(1, transparency));
}
DirectionalLight::DirectionalLight(const vec3 & direction, const vec3 & intensity)
: Direction(direction)
{
Intensity.r = max2(0, min2(1, intensity.r));
Intensity.g = max2(0, min2(1, intensity.g));
Intensity.b = max2(0, min2(1, intensity.b));
}
PointLight::PointLight(const vec3 & position, const vec3 & intensity, float attenuationlinear)
: Position(position)
{
Intensity.r = max2(0, min2(1, intensity.r));
Intensity.g = max2(0, min2(1, intensity.g));
Intensity.b = max2(0, min2(1, intensity.b));
AttenuationLinear = max2(0, attenuationlinear);
}
SpotLight::SpotLight(const vec3 & position, const vec3 & direction, const vec3 & intensity, int exponent)
: Position(position), Direction(direction)
{
Intensity.r = max2(0, min2(1, intensity.r));
Intensity.g = max2(0, min2(1, intensity.g));
Intensity.b = max2(0, min2(1, intensity.b));
Exponent = max(1, exponent);
}
void LoadSphere(
std::vector<Vertex>& vertexarray,
std::vector<Material>& materialarray,
unsigned int sectors, unsigned int slices)
{
sectors = max2(5, sectors);
slices = max2(5, slices);
// add 1 material (green)
materialarray.push_back(Material(vec3(0, 1, 0), vec3(0, 1, 0), vec3(1, 1, 1), 5.0f, 1.0f));
int materialindex = materialarray.size() - 1;
float dtheta = PI / slices;
float dphi = 2 * PI / sectors;
for (unsigned int i = 0; i <= slices; i++)
{
float theta = -PI / 2 + i * dtheta;
for (unsigned int j = 0; j <= sectors; j++)
{
float phi = j * dphi;
float x, y, z, u, v;
x = cos(theta) * cos(phi);
y = sin(theta);
z = cos(theta) * sin(phi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
x = cos(theta) * cos(phi + dphi);
y = sin(theta);
z = cos(theta) * sin(phi + dphi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
x = cos(theta - dtheta) * cos(phi + dphi);
y = sin(theta - dtheta);
z = cos(theta - dtheta) * sin(phi + dphi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
x = cos(theta) * cos(phi);
y = sin(theta);
z = cos(theta) * sin(phi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
x = cos(theta - dtheta) * cos(phi + dphi);
y = sin(theta - dtheta);
z = cos(theta - dtheta) * sin(phi + dphi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
x = cos(theta - dtheta) * cos(phi);
y = sin(theta - dtheta);
z = cos(theta - dtheta) * sin(phi);
u = phi / (2 * PI);
v = (y + 1) / 2;
vertexarray.push_back({ { x, y, z },{ u, v },{ x, y, z },{ materialindex } });
}
}
}
Scene.h
#pragma once
#include "Math.h"
#include "Orientation.h"
#include "Model.h"
#include <vector>
struct Object : public Orientation
{
Object(const vec3& position)
: Orientation(position)
{
m_ID = GetUniqueID();
}
unsigned int ID() const { return m_ID; }
// ...
private:
unsigned int m_ID;
};
struct Scene
{
struct : public Orientation {
float FoV{ radians(45.0f) };
float ZNear{ 0.1f }, ZFar{ 100.0f };
} Camera;
struct {
vec3 Ambient{ 0.2f, 0.2f, 0.2f };
std::vector<DirectionalLight> DirectionalLights;
std::vector<PointLight> PointLights;
std::vector<SpotLight> SpotLights;
} Light;
std::vector<Object> Objects;
Scene();
void AnimateNextFrame(float timestep, int tracked_ID);
};
Scene.cpp
#include "Scene.h"
#include "Main.h"
Scene::Scene()
{
// put the camera back a little ...
Camera.SetPosition(vec3(0, 1, 5));
// create some lights
Light.DirectionalLights.push_back(DirectionalLight(vec3(5, -10, 0), vec3(1, 1, 1)));
//Light.PointLights.push_back(PointLight(vec3(0, 5, 0), vec3(1, 1, 1), 0.5f));
//Light.SpotLights.push_back(SpotLight(vec3(0, 5, 0), vec3(0, -1, 0), vec3(0.7f, 0.7f, 0.7f), 5));
// create some objects ...
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, int tracked_ID)
{
// 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(30.0f) * timestep);
// ----------------------------------------------------------------------------------------------------------
// animate lights
// ----------------------------------------------------------------------------------------------------------
double totaltime = Framework->Time();
// change intensity
//for (auto& directionallight : Light.DirectionalLights)
// directionallight.Intensity = vec3(abs2(std::cos(totaltime * 2));
// change Y-coordinate (height)
for (auto& pointlight : Light.PointLights)
pointlight.Position.y = 5 * std::cos(totaltime * 0.5f);
// rotate arount Y-axis
for (auto& spotlight : Light.SpotLights)
spotlight.Direction = vec3(std::cos(totaltime), -5, std::sin(totaltime));
// ----------------------------------------------------------------------------------------------------------
// just print the ID at cursor position:
// ----------------------------------------------------------------------------------------------------------
if (Framework->EventClicked(MOUSE_BUTTON_LEFT))
if (tracked_ID != -1)
std::cout << "clicked at object: \t" << tracked_ID << std::endl;
// ----------------------------------------------------------------------------------------------------------
// ...
}
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, int& tracked_ID);
protected:
unsigned int m_width{ 0 }, m_height{ 0 };
unsigned int m_framebuffer{ 0 };
unsigned int m_framebuffercolortexture{ 0 };
unsigned int m_framebufferintegertexture{ 0 };
unsigned int m_framebufferdepthtexture{ 0 };
unsigned int m_program{ 0 };
unsigned int m_vertexarray{ 0 };
unsigned int m_vertexbuffer{ 0 };
unsigned int m_instancebuffer{ 0 };
unsigned int m_materialbuffer{ 0 };
unsigned int m_directionallightbuffer{ 0 };
unsigned int m_pointlightbuffer{ 0 };
unsigned int m_spotlightbuffer{ 0 };
unsigned int m_vertexcount{ 0 };
};
Renderer_OpenGL.cpp
#include "Renderer_OpenGL.h"
#include "Shader.h"
#include "Model.h"
#include "OBJ.h"
#include "Main.h"
#include <iostream>
#define MAX_MODEL_INSTANCES 1000
#define MAX_MATERIALS 1000
#define MAX_POINTLIGHTS 10
#define MAX_DIRECTIONALLIGHTS 10
#define MAX_SPOTLIGHTS 10
void CheckForGLError()
{
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
{
std::cout << "ERROR: (Renderer_OpenGL) \t";
if (error == GL_INVALID_ENUM)
std::cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
std::cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
std::cout << "GL_INVALID_OPERATION";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_OUT_OF_MEMORY)
std::cout << "GL_OUT_OF_MEMORY";
if (error == GL_STACK_UNDERFLOW)
std::cout << "GL_STACK_UNDERFLOW";
if (error == GL_STACK_OVERFLOW)
std::cout << "GL_STACK_OVERFLOW";
std::cout << (char)7 << std::endl; /*play sound*/
std::cin.get();
}
}
Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)
{
// settings
// ----------------------------------------------------------------------------------------------------------
glEnable(GL_DEPTH_TEST); // 3D
glEnable(GL_CULL_FACE); // dont render backward faces
// ----------------------------------------------------------------------------------------------------------
// setup framebuffer:
// ----------------------------------------------------------------------------------------------------------
// make sure that the framebuffer isnt too small
m_width = max2(100, width);
m_height = max2(100, height);
glViewport(0, 0, m_width, m_height);
glGenTextures(1, &m_framebuffercolortexture);
glBindTexture(GL_TEXTURE_2D, m_framebuffercolortexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferintegertexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferintegertexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// special texture to store only integer values (32 bit)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, m_width, m_height, 0, GL_RED_INTEGER, GL_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferdepthtexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferdepthtexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create framebuffer
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffercolortexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_framebufferintegertexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_framebufferdepthtexture, 0);
std::vector<GLenum> drawbuffers = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(drawbuffers.size(), drawbuffers.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ERROR CHECK:
GLenum status = glCheckNamedFramebufferStatus(m_framebuffer, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (status == GL_FRAMEBUFFER_UNDEFINED)
std::cout << "ERROR: undefined framebuffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
std::cout << "ERROR: a necessary attachment is uninitialized" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
std::cout << "ERROR: no attachments" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
std::cout << "ERROR: incomplete draw buffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
std::cout << "ERROR: incomplete read buffer" << std::endl;
if (status == GL_FRAMEBUFFER_UNSUPPORTED)
std::cout << "ERROR: combination of attachments is not supported" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
std::cout << "ERROR: number if samples for all attachments does not match" << std::endl;
}
// ----------------------------------------------------------------------------------------------------------
// setup program
// ----------------------------------------------------------------------------------------------------------
m_program = glCreateProgram();
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
unsigned int geometryshader = glCreateShader(GL_GEOMETRY_SHADER);
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
CompileShader(vertexshader, LoadTextFile("shader.vs"));
CompileShader(geometryshader, LoadTextFile("shader.gs"));
CompileShader(fragmentshader, LoadTextFile("shader.fs"));
LinkProgram(m_program, { vertexshader , geometryshader, fragmentshader });
glDeleteShader(vertexshader);
glDeleteShader(geometryshader);
glDeleteShader(fragmentshader);
// ----------------------------------------------------------------------------------------------------------
// setup vertexarray
// ----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_instancebuffer);
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 5));
glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)(sizeof(float) * 8));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_instancebuffer);
glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));
glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));
glVertexAttribPointer(6, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));
glVertexAttribPointer(7, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));
glVertexAttribIPointer(8, 1, GL_INT, sizeof(ModelInstance), (void*)(sizeof(float) * 16));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
// sent these attributes only once per instance to the program:
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
glBindVertexArray(0);
// ----------------------------------------------------------------------------------------------------------
// setup vertex buffer
// ----------------------------------------------------------------------------------------------------------
std::vector<Vertex> vertices;
std::vector<Material> materials;
LoadSphere(vertices, materials, 10, 10);
if (vertices.size() == 0)
{
vertices.push_back({ { 0, 0, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 1, 0, 0 },{ 0, 1 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 0, 1, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
}
m_vertexcount = vertices.size();
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup instance buffer
// ----------------------------------------------------------------------------------------------------------
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ModelInstance) * MAX_MODEL_INSTANCES, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup material buffer
// ----------------------------------------------------------------------------------------------------------
materials.resize(MAX_MATERIALS);
glGenBuffers(1, &m_materialbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_materialbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(Material) * materials.size(), materials.data(), GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, m_materialbuffer);
// ----------------------------------------------------------------------------------------------------------
// setup light buffer
// ----------------------------------------------------------------------------------------------------------
glGenBuffers(1, &m_directionallightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLight) * MAX_DIRECTIONALLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_pointlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(PointLight) * MAX_POINTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_spotlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(SpotLight) * MAX_SPOTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// bind buffers to uniform blocks:
glBindBufferBase(GL_UNIFORM_BUFFER, 2, m_directionallightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 3, m_pointlightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 4, m_spotlightbuffer);
// ----------------------------------------------------------------------------------------------------------
}
Renderer_OpenGL::~Renderer_OpenGL()
{
glDeleteProgram(m_program);
glDeleteFramebuffers(1, &m_framebuffer);
glDeleteTextures(1, &m_framebuffercolortexture);
glDeleteTextures(1, &m_framebufferintegertexture);
glDeleteTextures(1, &m_framebufferdepthtexture);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_instancebuffer);
glDeleteBuffers(1, &m_materialbuffer);
glDeleteBuffers(1, &m_directionallightbuffer);
glDeleteBuffers(1, &m_pointlightbuffer);
glDeleteBuffers(1, &m_spotlightbuffer);
}
void Renderer_OpenGL::Render(const Scene & scene, int& tracked_ID)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
// explicitly clear each buffer:
float clearvalue0[] = { 0.1f, 0.2f, 0.7f, 0.0f }; // background color
int clearvalue1[] = { -1 }; // background integer values
glClearBufferfv(GL_COLOR, 0, clearvalue0);
glClearBufferiv(GL_COLOR, 1, clearvalue1);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_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)));
// ----------------------------------------------------------------------------------------------------------
// set light sources:
// ----------------------------------------------------------------------------------------------------------
glUniform3fv(glGetUniformLocation(m_program, "CameraPosition"), 1, value_ptr(scene.Camera.Position()));
glUniform3fv(glGetUniformLocation(m_program, "Ambient"), 1, value_ptr(scene.Light.Ambient));
unsigned int directionallightcount = min2(MAX_DIRECTIONALLIGHTS, scene.Light.DirectionalLights.size());
unsigned int pointlightcount = min2(MAX_POINTLIGHTS, scene.Light.PointLights.size());
unsigned int spotlightcount = min2(MAX_SPOTLIGHTS, scene.Light.SpotLights.size());
glUniform1i(glGetUniformLocation(m_program, "DirectionalLightCount"), directionallightcount);
glUniform1i(glGetUniformLocation(m_program, "PointLightCount"), pointlightcount);
glUniform1i(glGetUniformLocation(m_program, "SpotLightCount"), spotlightcount);
if (directionallightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLight) * directionallightcount, scene.Light.DirectionalLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (pointlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PointLight) * pointlightcount, scene.Light.PointLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (spotlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SpotLight) * spotlightcount, scene.Light.SpotLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// ----------------------------------------------------------------------------------------------------------
// create instance buffer data:
// ----------------------------------------------------------------------------------------------------------
std::vector<ModelInstance> instances;
for (auto& object : scene.Objects)
instances.push_back({ object.ModelMatrix(), (int)object.ID() });
// ----------------------------------------------------------------------------------------------------------
// determine instance count:
// ----------------------------------------------------------------------------------------------------------
unsigned int instancecount = instances.size();
if (instancecount >= MAX_MODEL_INSTANCES)
instancecount = MAX_MODEL_INSTANCES;
// ----------------------------------------------------------------------------------------------------------
// upload instance buffer data:
// ----------------------------------------------------------------------------------------------------------
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ModelInstance) * instancecount, instances.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// render objects in scene
glDrawArraysInstanced(GL_TRIANGLES, 0, m_vertexcount, instancecount);
glBindVertexArray(0);
glUseProgram(0);
// read ID at cursor position:
ivec2 cursorposition = Framework->CursorPosition();
ivec2 windowsize = Framework->WindowSize();
int x = cursorposition.x * (float)m_width / windowsize.x;
int y = cursorposition.y * (float)m_height / windowsize.y;
glReadBuffer(GL_COLOR_ATTACHMENT1);
glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &tracked_ID);
// copy to default framebuffer:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(
0, 0, m_width, m_height,
0, 0, windowsize.x, windowsize.y,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// check for errors
CheckForGLError();
}
Shader Source Code:
Vertex Shader: "shader.vs"
NOTE:
This vertex shader just passes all the input to the geometry shader, where all the values are processed.
#version 450 core
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec2 in_texcoord;
layout (location = 2) in vec3 in_normal;
layout (location = 3) in int in_materialindex;
layout (location = 4) in mat4 in_model;
//layout (location = 5) in use ...
//layout (location = 6) in use ...
//layout (location = 7) in use ...
layout (location = 8) in int in_objectID;
out VS_GS_INTERFACE {
mat4 model;
vec3 position;
vec2 texcoord;
vec3 normal;
flat int materialindex;
flat int objectID;
} vs_out;
void main(void)
{
vs_out.model = in_model;
vs_out.position = in_position;
vs_out.texcoord = in_texcoord;
vs_out.normal = in_normal;
vs_out.materialindex = in_materialindex;
vs_out.objectID = in_objectID;
}
Geometry Shader: "shader.gs"
#version 450 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
#define MAX_MATERIALS 1000
#define MAX_POINTLIGHTS 10
#define MAX_DIRECTIONALLIGHTS 10
#define MAX_SPOTLIGHTS 10
in VS_GS_INTERFACE {
mat4 model;
vec3 position;
vec2 texcoord;
vec3 normal;
flat int materialindex;
flat int objectID;
} vs_out[];
// here the definition has vec4s instead of vec3
// thats because some drivers get the std140 layout for vec3s wrong
struct Material {
vec4 Ka, Kd, Ks;
float Ns;
float d;
};
// has constant intensity
struct DirectionalLight {
vec4 Direction;
vec4 Intensity;
};
// has distance-dependent intensity
struct PointLight {
vec4 Position;
vec4 Intensity;
float AttenuationLinear;
};
// has angle-dependent intensity
struct SpotLight {
vec4 Position;
vec4 Direction;
vec4 Intensity;
int Exponent;
};
layout (std140, binding = 1) uniform MaterialBlock
{
Material Materials[MAX_MATERIALS];
};
layout (std140, binding = 2) uniform DirectionalLightBlock
{
DirectionalLight DirectionalLights[MAX_DIRECTIONALLIGHTS];
};
layout (std140, binding = 3) uniform PointLightBlock
{
PointLight PointLights[MAX_POINTLIGHTS];
};
layout (std140, binding = 4) uniform SpotLightBlock
{
SpotLight SpotLights[MAX_SPOTLIGHTS];
};
uniform vec3 CameraPosition = vec3(0, 0, 0);
uniform vec3 Ambient = vec3(0, 0, 0);
uniform int DirectionalLightCount = 0;
uniform int PointLightCount = 0;
uniform int SpotLightCount = 0;
uniform mat4 View = mat4(1);
uniform mat4 Projection = mat4(1);
out GS_FS_INTERFACE {
flat vec4 color;
flat int objectID;
} gs_out;
void ProcessDirectionalLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void ProcessPointLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void ProcessSpotLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void main(void)
{
mat4 MVP = Projection * View * vs_out[0].model;
int objectID = vs_out[0].objectID;
vec3 Ka = Materials[vs_out[0].materialindex].Ka.rgb;
vec3 Kd = Materials[vs_out[0].materialindex].Kd.rgb;
vec3 Ks = Materials[vs_out[0].materialindex].Ks.rgb;
float d = Materials[vs_out[0].materialindex].d;
vec3 VertexPosition0 = (vs_out[0].model * vec4(vs_out[0].position, 1)).xyz;
vec3 VertexPosition1 = (vs_out[0].model * vec4(vs_out[1].position, 1)).xyz;
vec3 VertexPosition2 = (vs_out[0].model * vec4(vs_out[2].position, 1)).xyz;
vec3 SurfaceNormal = normalize(cross(VertexPosition1 - VertexPosition0, VertexPosition2 - VertexPosition0));
// first calculate the colors for each vertex
vec4 allcolors[3];
for (int i = 0; i < 3; i++)
{
vec3 TotalDiffuse = vec3(0, 0, 0);
vec3 TotalSpecular = vec3(0, 0, 0);
vec3 VertexPosition = (vs_out[i].model * vec4(vs_out[i].position, 1)).xyz;
vec3 SurfaceToCamera = normalize(CameraPosition - VertexPosition);
ProcessDirectionalLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);
ProcessPointLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);
ProcessSpotLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);
allcolors[i] = vec4(Ambient * Ka + TotalDiffuse * Kd + TotalSpecular * Ks, d);
}
// calculate average color
vec4 color = (allcolors[0] + allcolors[1] + allcolors[2]) / 3;
// create triangle
for (int i = 0; i < 3; i++)
{
gl_Position = MVP * vec4(vs_out[i].position, 1);
gs_out.color = color;
gs_out.objectID = objectID;
EmitVertex();
}
EndPrimitive();
}
void ProcessDirectionalLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out[0].materialindex].Ns;
for (int i = 0; i < DirectionalLightCount; i++)
{
// for specular:
vec3 ReflectedLight = normalize(reflect(DirectionalLights[i].Direction.xyz, SurfaceNormal));
Diffuse += DirectionalLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, -normalize(DirectionalLights[i].Direction.xyz)));
Specular += DirectionalLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns)));
}
}
void ProcessPointLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out[0].materialindex].Ns;
for (int i = 0; i < PointLightCount; i++)
{
// for diffuse:
vec3 SurfaceToLight = PointLights[i].Position.xyz - SurfacePosition;
float DistanceLightSurface = length(SurfaceToLight);
SurfaceToLight /= DistanceLightSurface;
float Attenuation = 1.0f + PointLights[i].AttenuationLinear * DistanceLightSurface;
// for specular:
vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));
Diffuse += PointLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) / Attenuation;
Specular += PointLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) / Attenuation;
}
}
void ProcessSpotLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out[0].materialindex].Ns;
for (int i = 0; i < SpotLightCount; i++)
{
// for diffuse:
vec3 SurfaceToLight = normalize(SpotLights[i].Position.xyz - SurfacePosition);
float CosAngle = dot(-SurfaceToLight, normalize(SpotLights[i].Direction.xyz));
float Multiplier = pow(max(0, CosAngle), max(1, SpotLights[i].Exponent));
// for specular:
vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));
Diffuse += SpotLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) * Multiplier;
Specular += SpotLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) * Multiplier;
}
}
Fragment Shader: "shader.fs"
#version 450 core
in GS_FS_INTERFACE {
flat vec4 color;
flat int objectID;
} gs_out;
layout (location = 0) out vec4 Fragment0;
layout (location = 1) out int Fragment1;
void main(void)
{
Fragment0 = vec4(gs_out.color);
Fragment1 = gs_out.objectID;
}