Implemented uniform buffers. Optimized data upload to GPU
This commit is contained in:
1
Makefile
1
Makefile
@@ -26,6 +26,7 @@ OBJS := build/camera.o \
|
|||||||
build/shader.o \
|
build/shader.o \
|
||||||
build/texture.o \
|
build/texture.o \
|
||||||
build/transform.o \
|
build/transform.o \
|
||||||
|
build/uniformbuffer.o \
|
||||||
build/utils.o \
|
build/utils.o \
|
||||||
build/window.o \
|
build/window.o \
|
||||||
build/glad.o \
|
build/glad.o \
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ void FrameBuffer::resize(GLsizei width, GLsizei height)
|
|||||||
glBindRenderbuffer(GL_RENDERBUFFER, m_render_buffer);
|
glBindRenderbuffer(GL_RENDERBUFFER, m_render_buffer);
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
|
||||||
m_width, m_height);
|
m_width, m_height);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ int main()
|
|||||||
/******************** Resource loading ********************/
|
/******************** Resource loading ********************/
|
||||||
|
|
||||||
auto shader = Shader::create("./src/shaders/default.vert", "./src/shaders/default.frag");
|
auto shader = Shader::create("./src/shaders/default.vert", "./src/shaders/default.frag");
|
||||||
auto debug_shader = Shader::create("./src/shaders/debug.vert", "./src/shaders/debug.frag");
|
auto debug_shader = Shader::create("./src/shaders/debug.vert", "./src/shaders/debug.frag", false);
|
||||||
|
|
||||||
auto backpack = load_obj("./assets/backpack/backpack.obj", shader);
|
auto backpack = load_obj("./assets/backpack/backpack.obj", shader);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Fan
|
|||||||
|
|
||||||
void Material::bind() const
|
void Material::bind() const
|
||||||
{
|
{
|
||||||
shader->use();
|
shader->bind();
|
||||||
|
|
||||||
// Diffuse (slot 0)
|
// Diffuse (slot 0)
|
||||||
if (diffuse_map) {
|
if (diffuse_map) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
|
|||||||
// Upload vertex data
|
// Upload vertex data
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||||
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]),
|
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]),
|
||||||
&vertices[0], GL_STATIC_DRAW);
|
&vertices[0], GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
// Upload index data
|
// Upload index data
|
||||||
if (has_indices) {
|
if (has_indices) {
|
||||||
|
|||||||
194
src/renderer.cpp
194
src/renderer.cpp
@@ -1,6 +1,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <glad/gl.h>
|
#include <glad/gl.h>
|
||||||
|
|
||||||
#include "renderer.hpp"
|
#include "renderer.hpp"
|
||||||
@@ -8,12 +9,19 @@
|
|||||||
namespace Fan
|
namespace Fan
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Rc<UniformBuffer> Renderer::s_camera_ubo = nullptr;
|
||||||
|
Rc<UniformBuffer> Renderer::s_light_ubo = nullptr;
|
||||||
|
|
||||||
void Renderer::init()
|
void Renderer::init()
|
||||||
{
|
{
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_BACK);
|
||||||
glFrontFace(GL_CCW);
|
glFrontFace(GL_CCW);
|
||||||
|
|
||||||
|
using bp = Shader::BindingPoint;
|
||||||
|
s_camera_ubo = UniformBuffer::create(sizeof(CameraData), bp::Camera);
|
||||||
|
s_light_ubo = UniformBuffer::create(sizeof(LightEnvironmentData), bp::LightEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::set_viewport(int x, int y, int width, int height)
|
void Renderer::set_viewport(int x, int y, int width, int height)
|
||||||
@@ -39,85 +47,35 @@ void Renderer::clear()
|
|||||||
void Renderer::draw_scene(const Scene& scene, const Camera& camera,
|
void Renderer::draw_scene(const Scene& scene, const Camera& camera,
|
||||||
int screen_width, int screen_height)
|
int screen_width, int screen_height)
|
||||||
{
|
{
|
||||||
glm::mat4 view_projection = camera.get_view_projection_matrix(screen_width, screen_height);
|
update_global_data(scene, camera, screen_width, screen_height);
|
||||||
GLuint last_shader_id = 0;
|
|
||||||
|
|
||||||
// TODO: Order entities by material to minimize uniform uploads
|
// Sort Entities: Shader -> Material -> Entities
|
||||||
for (const Entity& entity : scene.get_entities()) {
|
using RenderQueue = std::unordered_map<Shader*, std::unordered_map<Material*, std::vector<const Entity*>>>;
|
||||||
if (!entity.mesh || !entity.material || !entity.material->shader) { continue; }
|
RenderQueue render_queue;
|
||||||
|
|
||||||
Rc<Shader> shader = entity.material->shader;
|
for (const auto& entity : scene.get_entities()) {
|
||||||
GLuint current_shader_id = shader->get_id();
|
if (!entity.mesh || !entity.material || !entity.material->shader) {
|
||||||
|
// Ignore empty entities
|
||||||
// Only upload scene uniforms if the shader changed
|
continue;
|
||||||
if (current_shader_id != last_shader_id) {
|
|
||||||
shader->use();
|
|
||||||
|
|
||||||
// Upload camera uniforms
|
|
||||||
shader->set_mat4("u_view_projection", view_projection);
|
|
||||||
shader->set_vec3("u_view_pos", camera.position);
|
|
||||||
|
|
||||||
// Upload lighting uniforms
|
|
||||||
const LightEnvironment& env = scene.light_environment;
|
|
||||||
|
|
||||||
shader->set_vec3("u_directional_light.direction", env.directional_light.direction);
|
|
||||||
shader->set_vec3("u_directional_light.ambient", env.directional_light.ambient);
|
|
||||||
shader->set_vec3("u_directional_light.diffuse", env.directional_light.diffuse);
|
|
||||||
shader->set_vec3("u_directional_light.specular", env.directional_light.specular);
|
|
||||||
|
|
||||||
if (env.point_lights.size() > MAX_POINT_LIGHTS) {
|
|
||||||
std::cerr << "WARNING: RENDERER: Maximum number of point lights exceeded"
|
|
||||||
<< " (" << MAX_POINT_LIGHTS << ")" << std::endl;
|
|
||||||
}
|
|
||||||
int point_count = std::min(static_cast<int>(env.point_lights.size()), MAX_POINT_LIGHTS);
|
|
||||||
shader->set_int("u_point_light_count", point_count);
|
|
||||||
for (int i = 0; i < point_count; ++i) {
|
|
||||||
std::string prefix = "u_point_lights[" + std::to_string(i) + "].";
|
|
||||||
shader->set_vec3(prefix + "position", env.point_lights[i].position);
|
|
||||||
|
|
||||||
shader->set_float(prefix + "constant", env.point_lights[i].constant);
|
|
||||||
shader->set_float(prefix + "linear", env.point_lights[i].linear);
|
|
||||||
shader->set_float(prefix + "quadratic", env.point_lights[i].quadratic);
|
|
||||||
|
|
||||||
shader->set_vec3(prefix + "ambient", env.point_lights[i].ambient);
|
|
||||||
shader->set_vec3(prefix + "diffuse", env.point_lights[i].diffuse);
|
|
||||||
shader->set_vec3(prefix + "specular", env.point_lights[i].specular);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.spot_lights.size() > MAX_SPOT_LIGHTS) {
|
Shader* shader_ptr = entity.material->shader.get();
|
||||||
std::cerr << "WARNING: RENDERER: Maximum number of spot lights exceeded"
|
Material* material_ptr = entity.material.get();
|
||||||
<< " (" << MAX_POINT_LIGHTS << ")" << std::endl;
|
render_queue[shader_ptr][material_ptr].push_back(&entity);
|
||||||
}
|
|
||||||
int spot_count = std::min(static_cast<int>(env.spot_lights.size()), MAX_SPOT_LIGHTS);
|
|
||||||
shader->set_int("u_spot_light_count", spot_count);
|
|
||||||
for (int i = 0; i < spot_count; ++i) {
|
|
||||||
std::string prefix = "u_spot_lights[" + std::to_string(i) + "].";
|
|
||||||
shader->set_vec3(prefix + "position", env.spot_lights[i].position);
|
|
||||||
shader->set_vec3(prefix + "direction", env.spot_lights[i].direction);
|
|
||||||
|
|
||||||
shader->set_float(prefix + "cut_off", env.spot_lights[i].cut_off);
|
|
||||||
shader->set_float(prefix + "outer_cut_off", env.spot_lights[i].outer_cut_off);
|
|
||||||
|
|
||||||
shader->set_float(prefix + "constant", env.spot_lights[i].constant);
|
|
||||||
shader->set_float(prefix + "linear", env.spot_lights[i].linear);
|
|
||||||
shader->set_float(prefix + "quadratic", env.spot_lights[i].quadratic);
|
|
||||||
|
|
||||||
shader->set_vec3(prefix + "ambient", env.spot_lights[i].ambient);
|
|
||||||
shader->set_vec3(prefix + "diffuse", env.spot_lights[i].diffuse);
|
|
||||||
shader->set_vec3(prefix + "specular", env.spot_lights[i].specular);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
last_shader_id = current_shader_id;
|
// Draw using the sorted queue
|
||||||
|
for (const auto& [shader, materials] : render_queue) {
|
||||||
|
shader->bind();
|
||||||
|
|
||||||
|
for (const auto& [material, entities] : materials) {
|
||||||
|
material->bind();
|
||||||
|
|
||||||
|
for (const auto& entity : entities) {
|
||||||
|
shader->set_mat4("u_model", entity->transform.get_model_matrix());
|
||||||
|
entity->mesh->draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the specific textures and the shininess float
|
|
||||||
entity.material->bind();
|
|
||||||
|
|
||||||
// Upload the model matrix to transform the specific object
|
|
||||||
shader->set_mat4("u_model", entity.transform.get_model_matrix());
|
|
||||||
|
|
||||||
// Dispatch the draw call
|
|
||||||
entity.mesh->draw();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,46 +84,78 @@ void Renderer::draw_debug_wireframes(const Scene& scene, const Camera& camera,
|
|||||||
const Rc<Shader>& debug_shader,
|
const Rc<Shader>& debug_shader,
|
||||||
const Rc<Mesh>& default_mesh)
|
const Rc<Mesh>& default_mesh)
|
||||||
{
|
{
|
||||||
debug_shader->use();
|
update_global_data(scene, camera, screen_width, screen_height);
|
||||||
debug_shader->set_mat4("u_view_projection", camera.get_view_projection_matrix(screen_width, screen_height));
|
|
||||||
|
|
||||||
// Force wireframe mode and disable depth testing so we can see through objects
|
|
||||||
set_wireframe(true);
|
set_wireframe(true);
|
||||||
glDisable(GL_DEPTH_TEST);
|
debug_shader->bind();
|
||||||
|
|
||||||
// Draw all standard entities (green)
|
|
||||||
debug_shader->set_vec3("u_color", glm::vec3(0.0f, 0.7f, 0.0f));
|
|
||||||
for (const Entity& entity : scene.get_entities()) {
|
|
||||||
// If an entity has no mesh, use the default debug mesh
|
|
||||||
auto mesh = entity.mesh ? entity.mesh : default_mesh;
|
|
||||||
|
|
||||||
|
for (const auto& entity : scene.get_entities()) {
|
||||||
debug_shader->set_mat4("u_model", entity.transform.get_model_matrix());
|
debug_shader->set_mat4("u_model", entity.transform.get_model_matrix());
|
||||||
mesh->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw point lights (yellow)
|
if (entity.mesh) {
|
||||||
debug_shader->set_vec3("u_color", glm::vec3(0.7f, 0.7f, 0.0f));
|
entity.mesh->draw();
|
||||||
for (const PointLight& light : scene.light_environment.point_lights) {
|
} else {
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), light.position);
|
|
||||||
model = glm::scale(model, glm::vec3(0.2f));
|
|
||||||
|
|
||||||
debug_shader->set_mat4("u_model", model);
|
|
||||||
default_mesh->draw();
|
default_mesh->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw spot lights (cyan)
|
|
||||||
debug_shader->set_vec3("u_color", glm::vec3(0.0f, 0.7f, 0.7f));
|
|
||||||
for (const SpotLight& light : scene.light_environment.spot_lights) {
|
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), light.position);
|
|
||||||
model = glm::scale(model, glm::vec3(0.2f));
|
|
||||||
|
|
||||||
debug_shader->set_mat4("u_model", model);
|
|
||||||
default_mesh->draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore state
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
set_wireframe(false);
|
set_wireframe(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::update_global_data(const Scene& scene, const Camera& camera, int screen_width, int screen_height)
|
||||||
|
{
|
||||||
|
// Update camera UBO
|
||||||
|
CameraData cam_data{};
|
||||||
|
cam_data.view = camera.get_view_matrix();
|
||||||
|
cam_data.projection = camera.get_projection_matrix(screen_width, screen_height);
|
||||||
|
s_camera_ubo->update(&cam_data, sizeof(CameraData));
|
||||||
|
|
||||||
|
// Update light UBO
|
||||||
|
LightEnvironmentData light_data{};
|
||||||
|
const LightEnvironment& env = scene.light_environment;
|
||||||
|
|
||||||
|
// Map DirectionalLight
|
||||||
|
const auto& dir_src = env.directional_light;
|
||||||
|
light_data.dir_light.direction = glm::vec4(dir_src.direction, 0.0f);
|
||||||
|
light_data.dir_light.ambient = glm::vec4(dir_src.ambient, 0.0f);
|
||||||
|
light_data.dir_light.diffuse = glm::vec4(dir_src.diffuse, 0.0f);
|
||||||
|
light_data.dir_light.specular = glm::vec4(dir_src.specular, 0.0f);
|
||||||
|
|
||||||
|
// Map PointLights
|
||||||
|
if (light_data.active_point_lights > MAX_POINT_LIGHTS) {
|
||||||
|
std::cerr << "WARNING: RENDERER: Maximum number of point lights reached"
|
||||||
|
<< " (" << MAX_POINT_LIGHTS << ")" << std::endl;
|
||||||
|
}
|
||||||
|
light_data.active_point_lights = std::min(static_cast<int>(env.point_lights.size()), MAX_POINT_LIGHTS);
|
||||||
|
for (int i = 0; i < light_data.active_point_lights; ++i) {
|
||||||
|
const auto& src = env.point_lights[i];
|
||||||
|
|
||||||
|
light_data.point_lights[i].position = glm::vec4(src.position, 1.0f);
|
||||||
|
light_data.point_lights[i].ambient = glm::vec4(src.ambient, 1.0f);
|
||||||
|
light_data.point_lights[i].diffuse = glm::vec4(src.diffuse, 1.0f);
|
||||||
|
light_data.point_lights[i].specular = glm::vec4(src.specular, 1.0f);
|
||||||
|
light_data.point_lights[i].attenuation = glm::vec4(src.constant, src.linear, src.quadratic, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map SpotLights
|
||||||
|
if (light_data.active_spot_lights > MAX_SPOT_LIGHTS) {
|
||||||
|
std::cerr << "WARNING: RENDERER: Maximum number of spot lights reached"
|
||||||
|
<< " (" << MAX_SPOT_LIGHTS << ")" << std::endl;
|
||||||
|
}
|
||||||
|
light_data.active_spot_lights = std::min(static_cast<int>(env.spot_lights.size()), MAX_SPOT_LIGHTS);
|
||||||
|
for (int i = 0; i < light_data.active_spot_lights; ++i) {
|
||||||
|
const auto& src = env.spot_lights[i];
|
||||||
|
|
||||||
|
light_data.spot_lights[i].position = glm::vec4(src.position, 1.0f);
|
||||||
|
light_data.spot_lights[i].direction = glm::vec4(src.direction, 0.0f);
|
||||||
|
light_data.spot_lights[i].ambient = glm::vec4(src.ambient, 1.0f);
|
||||||
|
light_data.spot_lights[i].diffuse = glm::vec4(src.diffuse, 1.0f);
|
||||||
|
light_data.spot_lights[i].specular = glm::vec4(src.specular, 1.0f);
|
||||||
|
light_data.spot_lights[i].attenuation = glm::vec4(src.constant, src.linear, src.quadratic, 0.0f);
|
||||||
|
light_data.spot_lights[i].cut_offs = glm::vec4(src.cut_off, src.outer_cut_off, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_light_ubo->update(&light_data, sizeof(LightEnvironmentData));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "scene.hpp"
|
#include "scene.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
|
#include "uniformbuffer.hpp"
|
||||||
|
|
||||||
namespace Fan
|
namespace Fan
|
||||||
{
|
{
|
||||||
@@ -23,6 +24,58 @@ public:
|
|||||||
int screen_width, int screen_height,
|
int screen_width, int screen_height,
|
||||||
const Rc<Shader>& debug_shader,
|
const Rc<Shader>& debug_shader,
|
||||||
const Rc<Mesh>& default_mesh);
|
const Rc<Mesh>& default_mesh);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Rc<UniformBuffer> s_camera_ubo;
|
||||||
|
static Rc<UniformBuffer> s_light_ubo;
|
||||||
|
|
||||||
|
static void update_global_data(const Scene& scene, const Camera& camera,
|
||||||
|
int screen_width, int screen_height);
|
||||||
|
|
||||||
|
struct CameraData {
|
||||||
|
glm::mat4 view;
|
||||||
|
glm::mat4 projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps to Fan::DirectionalLight
|
||||||
|
struct DirectionalLightData {
|
||||||
|
glm::vec4 direction;
|
||||||
|
glm::vec4 ambient;
|
||||||
|
glm::vec4 diffuse;
|
||||||
|
glm::vec4 specular;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps to Fan::PointLight
|
||||||
|
struct PointLightData {
|
||||||
|
glm::vec4 position;
|
||||||
|
glm::vec4 ambient;
|
||||||
|
glm::vec4 diffuse;
|
||||||
|
glm::vec4 specular;
|
||||||
|
glm::vec4 attenuation; // x = constant, y = linear, z = quadratic
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps to Fan::SpotLight
|
||||||
|
struct SpotLightData {
|
||||||
|
glm::vec4 position;
|
||||||
|
glm::vec4 direction;
|
||||||
|
glm::vec4 ambient;
|
||||||
|
glm::vec4 diffuse;
|
||||||
|
glm::vec4 specular;
|
||||||
|
glm::vec4 attenuation; // x = constant, y = linear, z = quadratic
|
||||||
|
glm::vec4 cut_offs; // x = cut_off (cos), y = outer_cut_off (cos)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LightEnvironmentData {
|
||||||
|
DirectionalLightData dir_light;
|
||||||
|
|
||||||
|
int active_point_lights;
|
||||||
|
int active_spot_lights;
|
||||||
|
int _padding[2]; // Pad the 8 bytes of integers to a 16-byte boundary
|
||||||
|
|
||||||
|
PointLightData point_lights[MAX_POINT_LIGHTS];
|
||||||
|
SpotLightData spot_lights[MAX_SPOT_LIGHTS];
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ namespace Fan
|
|||||||
{
|
{
|
||||||
|
|
||||||
Rc<Shader> Shader::create(const std::string& vertex_shader_path,
|
Rc<Shader> Shader::create(const std::string& vertex_shader_path,
|
||||||
const std::string& fragment_shader_path)
|
const std::string& fragment_shader_path,
|
||||||
|
bool bind_standard_blocks/* = true*/)
|
||||||
{
|
{
|
||||||
auto vertex_shader = compile_shader(vertex_shader_path, GL_VERTEX_SHADER);
|
auto vertex_shader = compile_shader(vertex_shader_path, GL_VERTEX_SHADER);
|
||||||
if (!vertex_shader) {
|
if (!vertex_shader) {
|
||||||
@@ -46,7 +47,14 @@ Rc<Shader> Shader::create(const std::string& vertex_shader_path,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return make_rc<Shader>(shader_program);
|
Rc<Shader> shader = make_rc<Shader>(shader_program);
|
||||||
|
|
||||||
|
if (bind_standard_blocks) {
|
||||||
|
shader->bind_uniform_block("CameraBlock", BindingPoint::Camera);
|
||||||
|
shader->bind_uniform_block("LightEnvironmentBlock", BindingPoint::LightEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader::~Shader()
|
Shader::~Shader()
|
||||||
@@ -141,7 +149,7 @@ Opt<GLuint> Shader::compile_shader(const std::string& source_path,
|
|||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::use() const
|
void Shader::bind() const
|
||||||
{
|
{
|
||||||
if (m_id == 0) {
|
if (m_id == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -150,4 +158,17 @@ void Shader::use() const
|
|||||||
glUseProgram(m_id);
|
glUseProgram(m_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::bind_uniform_block(const std::string& name, GLuint binding_point) const
|
||||||
|
{
|
||||||
|
GLuint block_index = glGetUniformBlockIndex(m_id, name.c_str());
|
||||||
|
|
||||||
|
if (block_index == GL_INVALID_INDEX) {
|
||||||
|
std::cerr << "ERROR: SHADER: Uniform block not found: "
|
||||||
|
<< name << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glUniformBlockBinding(m_id, block_index, binding_point);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
@@ -13,8 +13,15 @@ namespace Fan
|
|||||||
class Shader
|
class Shader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct BindingPoint
|
||||||
|
{
|
||||||
|
static constexpr GLuint Camera = 0;
|
||||||
|
static constexpr GLuint LightEnvironment = 1;
|
||||||
|
};
|
||||||
|
|
||||||
static Rc<Shader> create(const std::string& vertex_path,
|
static Rc<Shader> create(const std::string& vertex_path,
|
||||||
const std::string& fragment_path);
|
const std::string& fragment_path,
|
||||||
|
bool bind_standard_blocks = true);
|
||||||
Shader(GLuint id) : m_id{id} {}
|
Shader(GLuint id) : m_id{id} {}
|
||||||
|
|
||||||
~Shader();
|
~Shader();
|
||||||
@@ -33,7 +40,8 @@ public:
|
|||||||
void set_vec3(const std::string& name, const glm::vec3& value);
|
void set_vec3(const std::string& name, const glm::vec3& value);
|
||||||
void set_mat4(const std::string& name, const glm::mat4& value);
|
void set_mat4(const std::string& name, const glm::mat4& value);
|
||||||
|
|
||||||
void use() const;
|
void bind() const;
|
||||||
|
void bind_uniform_block(const std::string& name, GLuint binding_point) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint m_id{};
|
GLuint m_id{};
|
||||||
|
|||||||
@@ -1,143 +1,139 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
struct Material
|
struct Material {
|
||||||
{
|
|
||||||
sampler2D diffuse;
|
sampler2D diffuse;
|
||||||
sampler2D specular;
|
sampler2D specular;
|
||||||
float shininess;
|
float shininess;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DirectionalLight
|
/******************** std140 struct layouts ********************/
|
||||||
{
|
|
||||||
vec3 direction;
|
|
||||||
|
|
||||||
vec3 ambient;
|
struct DirLight {
|
||||||
vec3 diffuse;
|
vec4 direction;
|
||||||
vec3 specular;
|
vec4 ambient;
|
||||||
|
vec4 diffuse;
|
||||||
|
vec4 specular;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PointLight {
|
struct PointLight {
|
||||||
vec3 position;
|
vec4 position;
|
||||||
|
vec4 ambient;
|
||||||
float constant;
|
vec4 diffuse;
|
||||||
float linear;
|
vec4 specular;
|
||||||
float quadratic;
|
vec4 attenuation; // x = constant, y = linear, z = quadratic
|
||||||
|
|
||||||
vec3 ambient;
|
|
||||||
vec3 diffuse;
|
|
||||||
vec3 specular;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpotLight {
|
struct SpotLight {
|
||||||
vec3 position;
|
vec4 position;
|
||||||
vec3 direction;
|
vec4 direction;
|
||||||
|
vec4 ambient;
|
||||||
float cut_off;
|
vec4 diffuse;
|
||||||
float outer_cut_off;
|
vec4 specular;
|
||||||
|
vec4 attenuation; // x = constant, y = linear, z = quadratic
|
||||||
float constant;
|
vec4 cut_offs; // x = cut_off (cos), y = outer_cut_off (cos)
|
||||||
float linear;
|
|
||||||
float quadratic;
|
|
||||||
|
|
||||||
vec3 ambient;
|
|
||||||
vec3 diffuse;
|
|
||||||
vec3 specular;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************** Uniform buffers ********************/
|
||||||
|
|
||||||
|
layout (std140) uniform CameraBlock {
|
||||||
|
mat4 view;
|
||||||
|
mat4 projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_POINT_LIGHTS 32
|
||||||
|
#define MAX_SPOT_LIGHTS 32
|
||||||
|
|
||||||
|
layout (std140) uniform LightEnvironmentBlock {
|
||||||
|
DirLight dir_light;
|
||||||
|
int active_point_lights;
|
||||||
|
int active_spot_lights;
|
||||||
|
PointLight point_lights[MAX_POINT_LIGHTS];
|
||||||
|
SpotLight spot_lights[MAX_SPOT_LIGHTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Material u_material;
|
||||||
|
|
||||||
in vec3 frag_position;
|
in vec3 frag_position;
|
||||||
in vec3 normal;
|
in vec3 normal;
|
||||||
in vec2 texture_coords;
|
in vec2 texture_coords;
|
||||||
|
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
uniform vec3 u_view_pos;
|
vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir);
|
||||||
uniform Material u_material;
|
|
||||||
|
|
||||||
uniform DirectionalLight u_directional_light;
|
|
||||||
|
|
||||||
#define MAX_POINT_LIGHTS 32
|
|
||||||
#define MAX_SPOT_LIGHTS 32
|
|
||||||
|
|
||||||
uniform PointLight u_point_lights[MAX_POINT_LIGHTS];
|
|
||||||
uniform int u_point_light_count;
|
|
||||||
|
|
||||||
uniform SpotLight u_spot_lights[MAX_SPOT_LIGHTS];
|
|
||||||
uniform int u_spot_light_count;
|
|
||||||
|
|
||||||
vec3 calculate_directional_light(DirectionalLight light, vec3 normal, vec3 view_dir);
|
|
||||||
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
||||||
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec3 norm = normalize(normal);
|
vec3 norm = normalize(normal);
|
||||||
vec3 view_dir = normalize(u_view_pos - frag_position);
|
|
||||||
|
|
||||||
vec3 result = calculate_directional_light(u_directional_light, norm, view_dir);
|
vec3 view_pos = vec3(inverse(view)[3]);
|
||||||
|
vec3 view_dir = normalize(view_pos - frag_position);
|
||||||
|
|
||||||
for (int i = 0; i < u_point_light_count; ++i) {
|
// DirectionalLight
|
||||||
result += calculate_point_light(u_point_lights[i], norm, frag_position, view_dir);
|
vec3 result = calculate_directional_light(dir_light, norm, view_dir);
|
||||||
|
|
||||||
|
// PointLights
|
||||||
|
for (int i = 0; i < active_point_lights; ++i) {
|
||||||
|
result += calculate_point_light(point_lights[i], norm, frag_position, view_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < u_spot_light_count; ++i) {
|
// SpotLights
|
||||||
result += calculate_spot_light(u_spot_lights[i], norm, frag_position, view_dir);
|
for (int i = 0; i < active_spot_lights; ++i) {
|
||||||
|
result += calculate_spot_light(spot_lights[i], norm, frag_position, view_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
frag_color = vec4(result, 1.0);
|
frag_color = vec4(result, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 calculate_directional_light(DirectionalLight light, vec3 normal, vec3 view_dir)
|
vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir)
|
||||||
{
|
{
|
||||||
vec3 light_dir = normalize(-light.direction);
|
vec3 light_dir = normalize(-light.direction.xyz);
|
||||||
float diff = max(dot(normal, light_dir), 0.0);
|
float diff = max(dot(normal, light_dir), 0.0);
|
||||||
vec3 reflect_dir = reflect(-light_dir, normal);
|
vec3 reflect_dir = reflect(-light_dir, normal);
|
||||||
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
||||||
|
|
||||||
vec3 ambient = light.ambient * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
vec3 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords));
|
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||||
|
|
||||||
return ambient + diffuse + specular;
|
return ambient + diffuse + specular;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir)
|
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir)
|
||||||
{
|
{
|
||||||
vec3 light_dir = normalize(light.position - frag_position);
|
vec3 light_dir = normalize(light.position.xyz - frag_position);
|
||||||
float diff = max(dot(normal, light_dir), 0.0);
|
float diff = max(dot(normal, light_dir), 0.0);
|
||||||
vec3 reflect_dir = reflect(-light_dir, normal);
|
vec3 reflect_dir = reflect(-light_dir, normal);
|
||||||
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
||||||
float dist = length(light.position - frag_position);
|
|
||||||
float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * (dist * dist));
|
|
||||||
|
|
||||||
vec3 ambient = light.ambient * vec3(texture(u_material.diffuse, texture_coords));
|
float dist = length(light.position.xyz - frag_position);
|
||||||
vec3 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords));
|
float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist));
|
||||||
vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords));
|
|
||||||
ambient *= attenuation;
|
|
||||||
diffuse *= attenuation;
|
|
||||||
specular *= attenuation;
|
|
||||||
|
|
||||||
return ambient + diffuse + specular;
|
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
|
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
|
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||||
|
|
||||||
|
return (ambient + diffuse + specular) * attenuation;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir)
|
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir)
|
||||||
{
|
{
|
||||||
vec3 light_dir = normalize(light.position - frag_position);
|
vec3 light_dir = normalize(light.position.xyz - frag_position);
|
||||||
float diff = max(dot(normal, light_dir), 0.0);
|
float diff = max(dot(normal, light_dir), 0.0);
|
||||||
vec3 reflect_dir = reflect(-light_dir, normal);
|
vec3 reflect_dir = reflect(-light_dir, normal);
|
||||||
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
||||||
float dist = length(light.position - frag_position);
|
|
||||||
float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * (dist* dist));
|
|
||||||
|
|
||||||
float theta = dot(light_dir, normalize(-light.direction));
|
float dist = length(light.position.xyz - frag_position);
|
||||||
float epsilon = light.cut_off - light.outer_cut_off;
|
float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist));
|
||||||
float intensity = clamp((theta - light.outer_cut_off) / epsilon, 0.0, 1.0);
|
|
||||||
|
|
||||||
vec3 ambient = light.ambient * vec3(texture(u_material.diffuse, texture_coords));
|
float theta = dot(light_dir, normalize(-light.direction.xyz));
|
||||||
vec3 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords));
|
float epsilon = light.cut_offs.x - light.cut_offs.y;
|
||||||
vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords));
|
float intensity = clamp((theta - light.cut_offs.y) / epsilon, 0.0, 1.0);
|
||||||
ambient *= attenuation * intensity;
|
|
||||||
diffuse *= attenuation * intensity;
|
|
||||||
specular *= attenuation * intensity;
|
|
||||||
|
|
||||||
return ambient + diffuse + specular;
|
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
|
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||||
|
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||||
|
|
||||||
|
return (ambient + diffuse + specular) * attenuation * intensity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ out vec3 frag_position;
|
|||||||
out vec3 normal;
|
out vec3 normal;
|
||||||
out vec2 texture_coords;
|
out vec2 texture_coords;
|
||||||
|
|
||||||
|
layout (std140) uniform CameraBlock {
|
||||||
|
mat4 view;
|
||||||
|
mat4 projection;
|
||||||
|
};
|
||||||
|
|
||||||
uniform mat4 u_model;
|
uniform mat4 u_model;
|
||||||
uniform mat4 u_view_projection;
|
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = u_view_projection * u_model * vec4(a_position, 1.0);
|
gl_Position = projection * view * u_model * vec4(a_position, 1.0);
|
||||||
|
|
||||||
frag_position = vec3(u_model * vec4(a_position, 1.0));
|
frag_position = vec3(u_model * vec4(a_position, 1.0));
|
||||||
|
|
||||||
|
|||||||
70
src/uniformbuffer.cpp
Normal file
70
src/uniformbuffer.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "uniformbuffer.hpp"
|
||||||
|
|
||||||
|
namespace Fan
|
||||||
|
{
|
||||||
|
|
||||||
|
Rc<UniformBuffer> UniformBuffer::create(size_t size, GLuint binding_point)
|
||||||
|
{
|
||||||
|
GLuint id;
|
||||||
|
|
||||||
|
glGenBuffers(1, &id);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, id);
|
||||||
|
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, binding_point, id);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
|
|
||||||
|
GLenum err = glGetError();
|
||||||
|
if (err != GL_NO_ERROR) {
|
||||||
|
std::cerr << "ERROR: UNIFORMBUFFER: Could not create uniformbuffer" << std::endl;
|
||||||
|
glDeleteBuffers(1, &id);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_rc<UniformBuffer>(id, size, binding_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformBuffer::~UniformBuffer()
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &m_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformBuffer::UniformBuffer(UniformBuffer&& other) noexcept
|
||||||
|
: m_id(std::exchange(other.m_id, 0))
|
||||||
|
, m_size(std::exchange(other.m_size, 0))
|
||||||
|
, m_binding_point(std::exchange(other.m_binding_point, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformBuffer& UniformBuffer::operator=(UniformBuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other) { return *this; }
|
||||||
|
|
||||||
|
std::swap(m_id, other.m_id);
|
||||||
|
std::swap(m_size, other.m_size);
|
||||||
|
std::swap(m_binding_point, other.m_binding_point);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UniformBuffer::update(const void* data, size_t size, size_t offset) const
|
||||||
|
{
|
||||||
|
if (size + offset > m_size) {
|
||||||
|
std::cerr << "ERROR: UNIFORMBUFFER: Attempted to upload data outside of bounds on update" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
std::cerr << "ERROR: UNIFORMBUFFER: Provided null data pointer on update" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, m_id);
|
||||||
|
glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Fan
|
||||||
42
src/uniformbuffer.hpp
Normal file
42
src/uniformbuffer.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef FAN_UNIFORMBUFFER_HPP_
|
||||||
|
#define FAN_UNIFORMBUFFER_HPP_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <glad/gl.h>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace Fan
|
||||||
|
{
|
||||||
|
|
||||||
|
class UniformBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Rc<UniformBuffer> create(size_t size, GLuint binding_point);
|
||||||
|
UniformBuffer(GLuint id, size_t size, GLuint binding_point)
|
||||||
|
: m_id(id)
|
||||||
|
, m_size(size)
|
||||||
|
, m_binding_point(binding_point) {}
|
||||||
|
|
||||||
|
~UniformBuffer();
|
||||||
|
UniformBuffer(const UniformBuffer&) = delete;
|
||||||
|
UniformBuffer& operator=(const UniformBuffer&) = delete;
|
||||||
|
UniformBuffer(UniformBuffer&& other) noexcept;
|
||||||
|
UniformBuffer& operator=(UniformBuffer&& other) noexcept;
|
||||||
|
|
||||||
|
GLuint get_id() const;
|
||||||
|
size_t get_size() const;
|
||||||
|
GLuint get_binding_point() const;
|
||||||
|
|
||||||
|
// Upload data to the GPU buffer
|
||||||
|
void update(const void* data, size_t size, size_t offset = 0) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint m_id{};
|
||||||
|
size_t m_size{};
|
||||||
|
GLuint m_binding_point{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Fan
|
||||||
|
|
||||||
|
#endif // FAN_UNIFORMBUFFER_HPP_
|
||||||
Reference in New Issue
Block a user