From d361c830be7658e156740f4794b7549809c8e6a3 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 10 Jul 2026 10:27:43 +0800 Subject: [PATCH] feat(render): add FrameBuffer class to encapsulate framebuffer operations --- include/Cubed/render/frame_buffer.hpp | 46 ++++++++++++++ include/Cubed/render/renderer.hpp | 7 ++- include/Cubed/render/texture.hpp | 2 + src/CMakeLists.txt | 1 + src/render/frame_buffer.cpp | 79 ++++++++++++++++++++++++ src/render/renderer.cpp | 88 +++++++++++---------------- src/render/texture.cpp | 2 + 7 files changed, 169 insertions(+), 56 deletions(-) create mode 100644 include/Cubed/render/frame_buffer.hpp create mode 100644 src/render/frame_buffer.cpp diff --git a/include/Cubed/render/frame_buffer.hpp b/include/Cubed/render/frame_buffer.hpp new file mode 100644 index 0000000..4ae3d8d --- /dev/null +++ b/include/Cubed/render/frame_buffer.hpp @@ -0,0 +1,46 @@ +#pragma once +#include "Cubed/render/texture.hpp" + +#include +#include + +enum class Attachment : GLenum { + COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0, + COLOR_ATTACHMENT1 = GL_COLOR_ATTACHMENT1, + DEPTH_ATTACHMENT = GL_DEPTH_ATTACHMENT +}; + +enum class FrameBufferType : GLenum { + FRAMEBUFFER = GL_FRAMEBUFFER, + READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER, + DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER +}; + +namespace Cubed { +class FrameBuffer { +public: + FrameBuffer(); + ~FrameBuffer(); + FrameBuffer(const FrameBuffer&) = delete; + FrameBuffer(FrameBuffer&&) noexcept; + FrameBuffer& operator=(const FrameBuffer&) = delete; + FrameBuffer& operator=(FrameBuffer&&) noexcept; + + void bind(FrameBufferType type = FrameBufferType::FRAMEBUFFER) const; + GLuint id() const; + + void attach(Attachment attachment, const Texture& texture, + GLint level = 0) const; + static void unbind(); + + bool check_status() const; + + void draw_buffer(GLenum buf) const; + void read_buffer(GLenum src) const; + void draw_buffer(GLsizei n, const GLenum* bufs) const; + void draw_buffer(std::span bufs) const; + +private: + GLuint m_fbo = 0; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/renderer.hpp b/include/Cubed/render/renderer.hpp index 4ec4999..3802fb4 100644 --- a/include/Cubed/render/renderer.hpp +++ b/include/Cubed/render/renderer.hpp @@ -2,6 +2,7 @@ #include "Cubed/constants.hpp" #include "Cubed/primitive_data.hpp" +#include "Cubed/render/frame_buffer.hpp" #include "Cubed/render/player_renderer.hpp" #include "Cubed/render/texture.hpp" #include "Cubed/render/vertex_array.hpp" @@ -136,16 +137,16 @@ private: std::unique_ptr m_player_vbo; std::unique_ptr m_quad_vbo; - GLuint m_fbo = 0; + std::unique_ptr m_fbo; std::unique_ptr m_screen_texture; std::unique_ptr m_screen_depth_texture; - GLuint m_oit_fbo = 0; + std::unique_ptr m_oit_fbo = 0; std::unique_ptr m_accum_texture; std::unique_ptr m_reveal_texture; std::unique_ptr m_oit_depth_texture; - GLuint m_depth_map_fbo = 0; + std::unique_ptr m_depth_map_fbo = 0; std::unique_ptr m_depth_map_texture; glm::mat4 m_ui_proj; diff --git a/include/Cubed/render/texture.hpp b/include/Cubed/render/texture.hpp index e31222a..df975e8 100644 --- a/include/Cubed/render/texture.hpp +++ b/include/Cubed/render/texture.hpp @@ -83,6 +83,8 @@ public: void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const; void set_clamp_to_edge(bool r = true, bool s = true, bool t = true) const; + TextureType type() const; + private: GLuint m_id = 0; const TextureType M_TYPE; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3ccd42..5a87825 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,4 +57,5 @@ target_sources(${PROJECT_NAME} render/vertex_buffer.cpp render/vertex_array.cpp render/texture.cpp + render/frame_buffer.cpp ) \ No newline at end of file diff --git a/src/render/frame_buffer.cpp b/src/render/frame_buffer.cpp new file mode 100644 index 0000000..4949211 --- /dev/null +++ b/src/render/frame_buffer.cpp @@ -0,0 +1,79 @@ +#include "Cubed/render/frame_buffer.hpp" + +#include "Cubed/tools/log.hpp" + +#include + +namespace Cubed { +FrameBuffer::FrameBuffer() { glGenFramebuffers(1, &m_fbo); } +FrameBuffer::~FrameBuffer() { + if (m_fbo) { + glDeleteFramebuffers(1, &m_fbo); + } +} + +FrameBuffer::FrameBuffer(FrameBuffer&& o) noexcept + : m_fbo(std::exchange(o.m_fbo, 0)) {} + +FrameBuffer& FrameBuffer::operator=(FrameBuffer&& o) noexcept { + if (this == &o) { + return *this; + } + if (m_fbo) { + glDeleteFramebuffers(1, &m_fbo); + } + m_fbo = std::exchange(o.m_fbo, 0); + return *this; +} + +GLuint FrameBuffer::id() const { return m_fbo; } + +void FrameBuffer::bind(FrameBufferType type) const { + glBindFramebuffer(std::to_underlying(type), m_fbo); +} +void FrameBuffer::unbind() { + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} +void FrameBuffer::attach(Attachment attachment, const Texture& texture, + GLint level) const { + bind(); + auto type = texture.type(); + if (type == TextureType::TEXTURE_2D) { + glFramebufferTexture2D(GL_FRAMEBUFFER, std::to_underlying(attachment), + std::to_underlying(type), texture.id(), level); + } +} + +bool FrameBuffer::check_status() const { + bind(); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("FBO incomplete after resize!"); + return false; + } else { + Logger::info("Frame Buffer Complete!"); + return true; + } +} + +void FrameBuffer::draw_buffer(GLenum buf) const { + bind(); + glDrawBuffer(buf); +} +void FrameBuffer::read_buffer(GLenum src) const { + bind(); + glReadBuffer(src); +} +void FrameBuffer::draw_buffer(GLsizei n, const GLenum* bufs) const { + bind(); + glDrawBuffers(n, bufs); +} + +void FrameBuffer::draw_buffer(std::span bufs) const { + bind(); + glDrawBuffers(bufs.size(), bufs.data()); +} + +} // namespace Cubed \ No newline at end of file diff --git a/src/render/renderer.cpp b/src/render/renderer.cpp index 91e48d9..f57ae00 100644 --- a/src/render/renderer.cpp +++ b/src/render/renderer.cpp @@ -37,16 +37,16 @@ Renderer::~Renderer() { m_player_vbo.reset(); glBindVertexArray(0); m_vao.clear(); - glDeleteFramebuffers(1, &m_fbo); + m_fbo.reset(); m_screen_texture.reset(); m_screen_depth_texture.reset(); - glDeleteFramebuffers(1, &m_oit_fbo); + m_oit_fbo.reset(); m_accum_texture.reset(); m_oit_depth_texture.reset(); m_reveal_texture.reset(); - glDeleteFramebuffers(1, &m_depth_map_fbo); + m_depth_map_fbo.reset(); m_depth_map_texture.reset(); } } @@ -204,7 +204,7 @@ void Renderer::init_text() { void Renderer::render() { glDisable(GL_FRAMEBUFFER_SRGB); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + m_fbo->bind(); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); day_night_calculation(); @@ -212,7 +212,7 @@ void Renderer::render() { render_world(); render_outline(); render_player(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + FrameBuffer::unbind(); glEnable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0); @@ -475,19 +475,19 @@ void Renderer::updata_framebuffer(int width, int height) { if (width <= 0 || height <= 0) return; if (m_fbo == 0) { - glGenFramebuffers(1, &m_fbo); + m_fbo = std::make_unique(); } if (m_oit_fbo == 0) { - glGenFramebuffers(1, &m_oit_fbo); + m_oit_fbo = std::make_unique(); } - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + m_screen_texture = std::make_unique(TextureType::TEXTURE_2D); m_screen_texture->tex_image_2d(TextureFormat::RGB, TextureFormat::RGB, GL_UNSIGNED_BYTE, nullptr, width, height); m_screen_texture->set_linear(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - m_screen_texture->id(), 0); + + m_fbo->attach(Attachment::COLOR_ATTACHMENT0, *m_screen_texture); m_screen_depth_texture = std::make_unique(TextureType::TEXTURE_2D); m_screen_depth_texture->tex_image_2d(TextureFormat::DEPTH_COMPONENT32F, @@ -495,50 +495,37 @@ void Renderer::updata_framebuffer(int width, int height) { GL_FLOAT, nullptr, width, height); // m_screen_depth_texture->set_nearest(); m_screen_depth_texture->set_linear(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - m_screen_depth_texture->id(), 0); + m_fbo->attach(Attachment::DEPTH_ATTACHMENT, *m_screen_depth_texture); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - Logger::error("FBO incomplete after resize!"); - } else { - Logger::info("Frame Buffer Complete!"); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo); + m_fbo->check_status(); m_accum_texture = std::make_unique(TextureType::TEXTURE_2D); m_accum_texture->tex_image_2d(TextureFormat::RGBA16F, TextureFormat::RGBA, GL_HALF_FLOAT, nullptr, width, height); m_accum_texture->set_linear(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - m_accum_texture->id(), 0); + m_oit_fbo->attach(Attachment::COLOR_ATTACHMENT0, *m_accum_texture); m_reveal_texture = std::make_unique(TextureType::TEXTURE_2D); m_reveal_texture->tex_image_2d(TextureFormat::R16F, TextureFormat::RED, GL_HALF_FLOAT, nullptr, width, height); m_reveal_texture->set_linear(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, - m_reveal_texture->id(), 0); + m_oit_fbo->attach(Attachment::COLOR_ATTACHMENT1, *m_reveal_texture); m_oit_depth_texture = std::make_unique(TextureType::TEXTURE_2D); m_oit_depth_texture->tex_image_2d(TextureFormat::DEPTH_COMPONENT32F, TextureFormat::DEPTH_COMPONENT, GL_FLOAT, nullptr, width, height); // m_oit_depth_texture->set_nearest(); m_oit_depth_texture->set_linear(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - m_oit_depth_texture->id(), 0); - GLenum draw_buffer[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; - glDrawBuffers(2, draw_buffer); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - Logger::error("FBO incomplete after resize!"); - } else { - Logger::info("Frame Buffer Complete!"); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + m_oit_fbo->attach(Attachment::DEPTH_ATTACHMENT, *m_oit_depth_texture); + + std::array draw_buffer = {GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1}; + m_oit_fbo->draw_buffer(draw_buffer); + + m_oit_fbo->check_status(); // depth map fbo if (m_depth_map_fbo == 0) { - glGenFramebuffers(1, &m_depth_map_fbo); + m_depth_map_fbo = std::make_unique(); } m_depth_map_texture = std::make_unique(TextureType::TEXTURE_2D); @@ -557,18 +544,13 @@ void Renderer::updata_framebuffer(int width, int height) { // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, // GL_COMPARE_REF_TO_TEXTURE); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + m_depth_map_fbo->attach(Attachment::DEPTH_ATTACHMENT, *m_depth_map_texture); + m_depth_map_fbo->draw_buffer(GL_NONE); + m_depth_map_fbo->read_buffer(GL_NONE); + m_depth_map_fbo->check_status(); + + FrameBuffer::unbind(); - glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - m_depth_map_texture->id(), 0); - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - Logger::error("FBO incomplete after resize!"); - } else { - Logger::info("Frame Buffer Complete!"); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); m_width = width; m_height = height; } @@ -634,7 +616,7 @@ void Renderer::render_world() { glCullFace(GL_BACK); } - glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); + m_depth_map_fbo->bind(); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); @@ -680,7 +662,7 @@ void Renderer::render_world() { auto& player_shadow = get_shader("player_depth"); m_player_renderer.shadow_render(player_shadow, light_space_matrix); } - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + m_fbo->bind(); glCullFace(GL_BACK); glViewport(0, 0, m_width, m_height); @@ -784,15 +766,15 @@ void Renderer::render_world() { } // copy depth buffer + m_fbo->bind(FrameBufferType::READ_FRAMEBUFFER); + m_oit_fbo->bind(FrameBufferType::DRAW_FRAMEBUFFER); - glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo); glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT, GL_NEAREST); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo); + m_oit_fbo->bind(FrameBufferType::DRAW_FRAMEBUFFER); // pass one accumulate - glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo); + m_oit_fbo->bind(); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(glm::vec4(0.0f))); float one = 1.0f; @@ -908,7 +890,7 @@ void Renderer::render_world() { m_accum_texture->bind(0); m_reveal_texture->bind(1); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + m_fbo->bind(); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); diff --git a/src/render/texture.cpp b/src/render/texture.cpp index bc47b6e..2830472 100644 --- a/src/render/texture.cpp +++ b/src/render/texture.cpp @@ -134,6 +134,8 @@ void Texture::set_clamp_to_edge(bool r, bool s, bool t) const { } } +TextureType Texture::type() const { return M_TYPE; } + void Texture::unbind() { glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, 0);