From a9bda25031984aaa6271f275471fafc2cdd8f9fb Mon Sep 17 00:00:00 2001 From: Cristian Gora Date: Sun, 27 Sep 2026 05:33:36 +0200 Subject: [PATCH] Implemented uniform buffers. Optimized data upload to GPU --- Makefile | 1 + src/framebuffer.cpp | 2 + src/main.cpp | 2 +- src/material.cpp | 2 +- src/mesh.cpp | 2 +- src/renderer.cpp | 202 +++++++++++++++++++-------------------- src/renderer.hpp | 53 ++++++++++ src/shader.cpp | 27 +++++- src/shader.hpp | 12 ++- src/shaders/default.frag | 154 +++++++++++++++-------------- src/shaders/default.vert | 8 +- src/uniformbuffer.cpp | 70 ++++++++++++++ src/uniformbuffer.hpp | 42 ++++++++ 13 files changed, 382 insertions(+), 195 deletions(-) create mode 100644 src/uniformbuffer.cpp create mode 100644 src/uniformbuffer.hpp diff --git a/Makefile b/Makefile index 47ee51a..7c136be 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ OBJS := build/camera.o \ build/shader.o \ build/texture.o \ build/transform.o \ + build/uniformbuffer.o \ build/utils.o \ build/window.o \ build/glad.o \ diff --git a/src/framebuffer.cpp b/src/framebuffer.cpp index 9acee69..2f260f7 100644 --- a/src/framebuffer.cpp +++ b/src/framebuffer.cpp @@ -110,6 +110,8 @@ void FrameBuffer::resize(GLsizei width, GLsizei height) glBindRenderbuffer(GL_RENDERBUFFER, m_render_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_width, m_height); + + glBindTexture(GL_TEXTURE_2D, 0); } } // namespace Fan diff --git a/src/main.cpp b/src/main.cpp index 8699c06..82142d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,7 @@ int main() /******************** Resource loading ********************/ 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); diff --git a/src/material.cpp b/src/material.cpp index 0effea8..a21d014 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -5,7 +5,7 @@ namespace Fan void Material::bind() const { - shader->use(); + shader->bind(); // Diffuse (slot 0) if (diffuse_map) { diff --git a/src/mesh.cpp b/src/mesh.cpp index 21e7fc3..1019012 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -19,7 +19,7 @@ Rc Mesh::create(const std::vector& vertices, // Upload vertex data glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]), - &vertices[0], GL_STATIC_DRAW); + &vertices[0], GL_DYNAMIC_DRAW); // Upload index data if (has_indices) { diff --git a/src/renderer.cpp b/src/renderer.cpp index e0691a4..f3fcecd 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "renderer.hpp" @@ -8,12 +9,19 @@ namespace Fan { +Rc Renderer::s_camera_ubo = nullptr; +Rc Renderer::s_light_ubo = nullptr; + void Renderer::init() { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); 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) @@ -39,85 +47,35 @@ void Renderer::clear() void Renderer::draw_scene(const Scene& scene, const Camera& camera, int screen_width, int screen_height) { - glm::mat4 view_projection = camera.get_view_projection_matrix(screen_width, screen_height); - GLuint last_shader_id = 0; + update_global_data(scene, camera, screen_width, screen_height); - // TODO: Order entities by material to minimize uniform uploads - for (const Entity& entity : scene.get_entities()) { - if (!entity.mesh || !entity.material || !entity.material->shader) { continue; } + // Sort Entities: Shader -> Material -> Entities + using RenderQueue = std::unordered_map>>; + RenderQueue render_queue; - Rc shader = entity.material->shader; - GLuint current_shader_id = shader->get_id(); - - // Only upload scene uniforms if the shader changed - 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(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) { - std::cerr << "WARNING: RENDERER: Maximum number of spot lights exceeded" - << " (" << MAX_POINT_LIGHTS << ")" << std::endl; - } - int spot_count = std::min(static_cast(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; + for (const auto& entity : scene.get_entities()) { + if (!entity.mesh || !entity.material || !entity.material->shader) { + // Ignore empty entities + continue; } - // Bind the specific textures and the shininess float - entity.material->bind(); + Shader* shader_ptr = entity.material->shader.get(); + Material* material_ptr = entity.material.get(); + render_queue[shader_ptr][material_ptr].push_back(&entity); + } - // Upload the model matrix to transform the specific object - shader->set_mat4("u_model", entity.transform.get_model_matrix()); + // Draw using the sorted queue + for (const auto& [shader, materials] : render_queue) { + shader->bind(); - // Dispatch the draw call - entity.mesh->draw(); + 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(); + } + } } } @@ -126,46 +84,78 @@ void Renderer::draw_debug_wireframes(const Scene& scene, const Camera& camera, const Rc& debug_shader, const Rc& default_mesh) { - debug_shader->use(); - debug_shader->set_mat4("u_view_projection", camera.get_view_projection_matrix(screen_width, screen_height)); + update_global_data(scene, camera, screen_width, screen_height); - // Force wireframe mode and disable depth testing so we can see through objects set_wireframe(true); - glDisable(GL_DEPTH_TEST); - - // 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; + debug_shader->bind(); + for (const auto& entity : scene.get_entities()) { debug_shader->set_mat4("u_model", entity.transform.get_model_matrix()); - mesh->draw(); + + if (entity.mesh) { + entity.mesh->draw(); + } else { + default_mesh->draw(); + } } - // Draw point lights (yellow) - debug_shader->set_vec3("u_color", glm::vec3(0.7f, 0.7f, 0.0f)); - for (const PointLight& light : scene.light_environment.point_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(); - } - - // 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); } +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(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(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 diff --git a/src/renderer.hpp b/src/renderer.hpp index 719273f..bf869a2 100644 --- a/src/renderer.hpp +++ b/src/renderer.hpp @@ -3,6 +3,7 @@ #include "scene.hpp" #include "camera.hpp" +#include "uniformbuffer.hpp" namespace Fan { @@ -23,6 +24,58 @@ public: int screen_width, int screen_height, const Rc& debug_shader, const Rc& default_mesh); + +private: + static Rc s_camera_ubo; + static Rc 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 diff --git a/src/shader.cpp b/src/shader.cpp index 95d0bc8..f5a6a5c 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -9,7 +9,8 @@ namespace Fan { Rc 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); if (!vertex_shader) { @@ -46,7 +47,14 @@ Rc Shader::create(const std::string& vertex_shader_path, return nullptr; } - return make_rc(shader_program); + Rc shader = make_rc(shader_program); + + if (bind_standard_blocks) { + shader->bind_uniform_block("CameraBlock", BindingPoint::Camera); + shader->bind_uniform_block("LightEnvironmentBlock", BindingPoint::LightEnvironment); + } + + return shader; } Shader::~Shader() @@ -141,7 +149,7 @@ Opt Shader::compile_shader(const std::string& source_path, return shader; } -void Shader::use() const +void Shader::bind() const { if (m_id == 0) { return; @@ -150,4 +158,17 @@ void Shader::use() const 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 diff --git a/src/shader.hpp b/src/shader.hpp index f4e9b8b..a66e9d5 100644 --- a/src/shader.hpp +++ b/src/shader.hpp @@ -13,8 +13,15 @@ namespace Fan class Shader { public: + struct BindingPoint + { + static constexpr GLuint Camera = 0; + static constexpr GLuint LightEnvironment = 1; + }; + static Rc 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(); @@ -33,7 +40,8 @@ public: void set_vec3(const std::string& name, const glm::vec3& 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: GLuint m_id{}; diff --git a/src/shaders/default.frag b/src/shaders/default.frag index a002483..29a9079 100644 --- a/src/shaders/default.frag +++ b/src/shaders/default.frag @@ -1,143 +1,139 @@ #version 330 core -struct Material -{ +struct Material { sampler2D diffuse; sampler2D specular; float shininess; }; -struct DirectionalLight -{ - vec3 direction; +/******************** std140 struct layouts ********************/ - vec3 ambient; - vec3 diffuse; - vec3 specular; +struct DirLight { + vec4 direction; + vec4 ambient; + vec4 diffuse; + vec4 specular; }; struct PointLight { - vec3 position; - - float constant; - float linear; - float quadratic; - - vec3 ambient; - vec3 diffuse; - vec3 specular; + vec4 position; + vec4 ambient; + vec4 diffuse; + vec4 specular; + vec4 attenuation; // x = constant, y = linear, z = quadratic }; struct SpotLight { - vec3 position; - vec3 direction; - - float cut_off; - float outer_cut_off; - - float constant; - float linear; - float quadratic; - - vec3 ambient; - vec3 diffuse; - vec3 specular; + vec4 position; + vec4 direction; + vec4 ambient; + vec4 diffuse; + vec4 specular; + vec4 attenuation; // x = constant, y = linear, z = quadratic + vec4 cut_offs; // x = cut_off (cos), y = outer_cut_off (cos) }; +/******************** 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 normal; in vec2 texture_coords; out vec4 frag_color; -uniform vec3 u_view_pos; -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_directional_light(DirLight light, vec3 normal, 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); void main() { 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) { - result += calculate_point_light(u_point_lights[i], norm, frag_position, view_dir); + // DirectionalLight + 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) { - result += calculate_spot_light(u_spot_lights[i], norm, frag_position, view_dir); + // SpotLights + 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); } -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); vec3 reflect_dir = reflect(-light_dir, normal); 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 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords)); - vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords)); + 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; } 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); vec3 reflect_dir = reflect(-light_dir, normal); 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)); - vec3 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords)); - vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords)); - ambient *= attenuation; - diffuse *= attenuation; - specular *= attenuation; + float dist = length(light.position.xyz - frag_position); + float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist)); - 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 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); vec3 reflect_dir = reflect(-light_dir, normal); 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 epsilon = light.cut_off - light.outer_cut_off; - float intensity = clamp((theta - light.outer_cut_off) / epsilon, 0.0, 1.0); + float dist = length(light.position.xyz - frag_position); + float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist)); - vec3 ambient = light.ambient * vec3(texture(u_material.diffuse, texture_coords)); - vec3 diffuse = light.diffuse * diff * vec3(texture(u_material.diffuse, texture_coords)); - vec3 specular = light.specular * spec * vec3(texture(u_material.specular, texture_coords)); - ambient *= attenuation * intensity; - diffuse *= attenuation * intensity; - specular *= attenuation * intensity; + float theta = dot(light_dir, normalize(-light.direction.xyz)); + float epsilon = light.cut_offs.x - light.cut_offs.y; + float intensity = clamp((theta - light.cut_offs.y) / epsilon, 0.0, 1.0); - 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; } diff --git a/src/shaders/default.vert b/src/shaders/default.vert index c72ff01..2982ec0 100644 --- a/src/shaders/default.vert +++ b/src/shaders/default.vert @@ -8,12 +8,16 @@ out vec3 frag_position; out vec3 normal; out vec2 texture_coords; +layout (std140) uniform CameraBlock { + mat4 view; + mat4 projection; +}; + uniform mat4 u_model; -uniform mat4 u_view_projection; 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)); diff --git a/src/uniformbuffer.cpp b/src/uniformbuffer.cpp new file mode 100644 index 0000000..8975c89 --- /dev/null +++ b/src/uniformbuffer.cpp @@ -0,0 +1,70 @@ +#include +#include + +#include "uniformbuffer.hpp" + +namespace Fan +{ + +Rc 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(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 diff --git a/src/uniformbuffer.hpp b/src/uniformbuffer.hpp new file mode 100644 index 0000000..848a799 --- /dev/null +++ b/src/uniformbuffer.hpp @@ -0,0 +1,42 @@ +#ifndef FAN_UNIFORMBUFFER_HPP_ +#define FAN_UNIFORMBUFFER_HPP_ + +#include +#include + +#include "utils.hpp" + +namespace Fan +{ + +class UniformBuffer +{ +public: + static Rc 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_