Implemented uniform buffers. Optimized data upload to GPU
This commit is contained in:
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
|
||||
|
||||
Reference in New Issue
Block a user