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/texture.o \
|
||||
build/transform.o \
|
||||
build/uniformbuffer.o \
|
||||
build/utils.o \
|
||||
build/window.o \
|
||||
build/glad.o \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Fan
|
||||
|
||||
void Material::bind() const
|
||||
{
|
||||
shader->use();
|
||||
shader->bind();
|
||||
|
||||
// Diffuse (slot 0)
|
||||
if (diffuse_map) {
|
||||
|
||||
@@ -19,7 +19,7 @@ Rc<Mesh> Mesh::create(const std::vector<Vertex>& 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) {
|
||||
|
||||
202
src/renderer.cpp
202
src/renderer.cpp
@@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include "renderer.hpp"
|
||||
@@ -8,12 +9,19 @@
|
||||
namespace Fan
|
||||
{
|
||||
|
||||
Rc<UniformBuffer> Renderer::s_camera_ubo = nullptr;
|
||||
Rc<UniformBuffer> 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<Shader*, std::unordered_map<Material*, std::vector<const Entity*>>>;
|
||||
RenderQueue render_queue;
|
||||
|
||||
Rc<Shader> 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<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) {
|
||||
std::cerr << "WARNING: RENDERER: Maximum number of spot lights exceeded"
|
||||
<< " (" << MAX_POINT_LIGHTS << ")" << std::endl;
|
||||
}
|
||||
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;
|
||||
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<Shader>& debug_shader,
|
||||
const Rc<Mesh>& 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<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
|
||||
|
||||
@@ -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<Shader>& debug_shader,
|
||||
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
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace Fan
|
||||
{
|
||||
|
||||
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);
|
||||
if (!vertex_shader) {
|
||||
@@ -46,7 +47,14 @@ Rc<Shader> Shader::create(const std::string& vertex_shader_path,
|
||||
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()
|
||||
@@ -141,7 +149,7 @@ Opt<GLuint> 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
|
||||
|
||||
@@ -13,8 +13,15 @@ namespace Fan
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
struct BindingPoint
|
||||
{
|
||||
static constexpr GLuint Camera = 0;
|
||||
static constexpr GLuint LightEnvironment = 1;
|
||||
};
|
||||
|
||||
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();
|
||||
@@ -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{};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
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