/** * @file rasterizer.cpp * @brief Implementation of Rasterizer class */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace are { Rasterizer::Rasterizer(int width, int height) : gbuffer_(std::make_unique(width, height)) , gbuffer_shader_(std::make_unique()) , triangle_base_provider_(nullptr) , state_() , width_(width) , height_(height) { } Rasterizer::~Rasterizer() = default; void Rasterizer::set_state(const RasterizerState& state) { state_ = state; } void Rasterizer::set_triangle_base_provider(std::function provider) { triangle_base_provider_ = std::move(provider); } void Rasterizer::resize(int width, int height) { ARE_PROFILE_FUNCTION(); if (width == width_ && height == height_) return; width_ = width; height_ = height; gbuffer_->resize(width_, height_); } void Rasterizer::render_gbuffer(const SceneManager& scene, const Camera& camera) { ARE_PROFILE_FUNCTION(); if (!gbuffer_shader_ || !gbuffer_shader_->is_valid()) { ARE_LOG_ERROR("Rasterizer: gbuffer shader not ready"); return; } gbuffer_->bind(); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (state_.enable_depth_test) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); } else { glDisable(GL_DEPTH_TEST); } if (state_.enable_cull_face) { glEnable(GL_CULL_FACE); glCullFace(static_cast(state_.cull_face_mode)); glFrontFace(static_cast(state_.front_face)); } else { glDisable(GL_CULL_FACE); } gbuffer_shader_->use(); gbuffer_shader_->set_uniform("u_view", camera.get_view_matrix()); gbuffer_shader_->set_uniform("u_projection", camera.get_projection_matrix()); const auto& meshes = scene.get_all_meshes(); for (size_t mi = 0; mi < meshes.size(); ++mi) { const auto& mesh = meshes[mi]; if (mesh.is_empty() || !mesh.has_gpu_resources()) continue; Mat4 model = Mat4(1.0f); gbuffer_shader_->set_uniform("u_model", model); Mat3 normal_matrix = glm::inverseTranspose(Mat3(model)); gbuffer_shader_->set_uniform("u_normal_matrix", normal_matrix); uint32_t tri_base = triangle_base_provider_ ? triangle_base_provider_(mi) : 0u; // IMPORTANT: u_triangle_id_base is uint in GLSL, must use glUniform1ui gbuffer_shader_->set_uniform("u_triangle_id_base", tri_base); const Material* mat = scene.get_material(mesh.get_material()); if (mat) { gbuffer_shader_->set_uniform("u_albedo", mat->get_albedo()); gbuffer_shader_->set_uniform("u_metallic", mat->get_metallic()); gbuffer_shader_->set_uniform("u_roughness", mat->get_roughness()); } else { gbuffer_shader_->set_uniform("u_albedo", Vec3(0.8f)); gbuffer_shader_->set_uniform("u_metallic", 0.0f); gbuffer_shader_->set_uniform("u_roughness", 0.5f); } glBindVertexArray(mesh.get_vao()); glDrawElements(GL_TRIANGLES, static_cast(mesh.get_index_count()), GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } gbuffer_->unbind(); ARE_GL_CHECK(); } GBuffer& Rasterizer::get_gbuffer() { return *gbuffer_; } const GBuffer& Rasterizer::get_gbuffer() const { return *gbuffer_; } void Rasterizer::upload_mesh(Mesh& mesh) { ARE_PROFILE_FUNCTION(); if (mesh.is_empty()) { ARE_LOG_WARN("Rasterizer: upload_mesh on empty mesh"); return; } if (mesh.has_gpu_resources()) delete_mesh(mesh); setup_mesh_buffers(mesh); } void Rasterizer::delete_mesh(Mesh& mesh) { ARE_PROFILE_FUNCTION(); uint32_t vao = mesh.get_vao(); uint32_t vbo = mesh.get_vbo(); uint32_t ebo = mesh.get_ebo(); if (vao) glDeleteVertexArrays(1, &vao); if (vbo) glDeleteBuffers(1, &vbo); if (ebo) glDeleteBuffers(1, &ebo); mesh.set_vao(0); mesh.set_vbo(0); mesh.set_ebo(0); } void Rasterizer::initialize_shaders(const std::string& shader_dir) { ARE_PROFILE_FUNCTION(); bool ok = true; ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_VERTEX, shader_dir + "gbuffer/gbuffer.vert"); ok &= gbuffer_shader_->load_shader(ShaderType::ARE_SHADER_FRAGMENT, shader_dir + "gbuffer/gbuffer.frag"); ok &= gbuffer_shader_->link(); if (!ok) { ARE_LOG_ERROR("Rasterizer: Failed to init gbuffer shaders"); } } void Rasterizer::setup_mesh_buffers(Mesh& mesh) { ARE_PROFILE_FUNCTION(); uint32_t vao = 0, vbo = 0, ebo = 0; glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, mesh.get_vertex_count() * sizeof(Vertex), mesh.get_vertices().data(), GL_STATIC_DRAW); glGenBuffers(1, &ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.get_index_count() * sizeof(uint32_t), mesh.get_indices().data(), GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(get_position_offset())); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(get_normal_offset())); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(get_texcoord_offset())); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(get_tangent_offset())); glBindVertexArray(0); mesh.set_vao(vao); mesh.set_vbo(vbo); mesh.set_ebo(ebo); ARE_GL_CHECK(); } } // namespace are