feat(render): add FrameBuffer class to encapsulate framebuffer operations

This commit is contained in:
2026-07-10 10:27:43 +08:00
parent c7383a4208
commit d361c830be
7 changed files with 169 additions and 56 deletions

View File

@@ -0,0 +1,46 @@
#pragma once
#include "Cubed/render/texture.hpp"
#include <glad/glad.h>
#include <span>
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<const GLenum> bufs) const;
private:
GLuint m_fbo = 0;
};
} // namespace Cubed

View File

@@ -2,6 +2,7 @@
#include "Cubed/constants.hpp" #include "Cubed/constants.hpp"
#include "Cubed/primitive_data.hpp" #include "Cubed/primitive_data.hpp"
#include "Cubed/render/frame_buffer.hpp"
#include "Cubed/render/player_renderer.hpp" #include "Cubed/render/player_renderer.hpp"
#include "Cubed/render/texture.hpp" #include "Cubed/render/texture.hpp"
#include "Cubed/render/vertex_array.hpp" #include "Cubed/render/vertex_array.hpp"
@@ -136,16 +137,16 @@ private:
std::unique_ptr<VertexBuffer> m_player_vbo; std::unique_ptr<VertexBuffer> m_player_vbo;
std::unique_ptr<VertexBuffer> m_quad_vbo; std::unique_ptr<VertexBuffer> m_quad_vbo;
GLuint m_fbo = 0; std::unique_ptr<FrameBuffer> m_fbo;
std::unique_ptr<Texture> m_screen_texture; std::unique_ptr<Texture> m_screen_texture;
std::unique_ptr<Texture> m_screen_depth_texture; std::unique_ptr<Texture> m_screen_depth_texture;
GLuint m_oit_fbo = 0; std::unique_ptr<FrameBuffer> m_oit_fbo = 0;
std::unique_ptr<Texture> m_accum_texture; std::unique_ptr<Texture> m_accum_texture;
std::unique_ptr<Texture> m_reveal_texture; std::unique_ptr<Texture> m_reveal_texture;
std::unique_ptr<Texture> m_oit_depth_texture; std::unique_ptr<Texture> m_oit_depth_texture;
GLuint m_depth_map_fbo = 0; std::unique_ptr<FrameBuffer> m_depth_map_fbo = 0;
std::unique_ptr<Texture> m_depth_map_texture; std::unique_ptr<Texture> m_depth_map_texture;
glm::mat4 m_ui_proj; glm::mat4 m_ui_proj;

View File

@@ -83,6 +83,8 @@ public:
void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const; 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; void set_clamp_to_edge(bool r = true, bool s = true, bool t = true) const;
TextureType type() const;
private: private:
GLuint m_id = 0; GLuint m_id = 0;
const TextureType M_TYPE; const TextureType M_TYPE;

View File

@@ -57,4 +57,5 @@ target_sources(${PROJECT_NAME}
render/vertex_buffer.cpp render/vertex_buffer.cpp
render/vertex_array.cpp render/vertex_array.cpp
render/texture.cpp render/texture.cpp
render/frame_buffer.cpp
) )

View File

@@ -0,0 +1,79 @@
#include "Cubed/render/frame_buffer.hpp"
#include "Cubed/tools/log.hpp"
#include <utility>
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<const GLenum> bufs) const {
bind();
glDrawBuffers(bufs.size(), bufs.data());
}
} // namespace Cubed

View File

@@ -37,16 +37,16 @@ Renderer::~Renderer() {
m_player_vbo.reset(); m_player_vbo.reset();
glBindVertexArray(0); glBindVertexArray(0);
m_vao.clear(); m_vao.clear();
glDeleteFramebuffers(1, &m_fbo); m_fbo.reset();
m_screen_texture.reset(); m_screen_texture.reset();
m_screen_depth_texture.reset(); m_screen_depth_texture.reset();
glDeleteFramebuffers(1, &m_oit_fbo); m_oit_fbo.reset();
m_accum_texture.reset(); m_accum_texture.reset();
m_oit_depth_texture.reset(); m_oit_depth_texture.reset();
m_reveal_texture.reset(); m_reveal_texture.reset();
glDeleteFramebuffers(1, &m_depth_map_fbo); m_depth_map_fbo.reset();
m_depth_map_texture.reset(); m_depth_map_texture.reset();
} }
} }
@@ -204,7 +204,7 @@ void Renderer::init_text() {
void Renderer::render() { void Renderer::render() {
glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_FRAMEBUFFER_SRGB);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); m_fbo->bind();
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
day_night_calculation(); day_night_calculation();
@@ -212,7 +212,7 @@ void Renderer::render() {
render_world(); render_world();
render_outline(); render_outline();
render_player(); render_player();
glBindFramebuffer(GL_FRAMEBUFFER, 0); FrameBuffer::unbind();
glEnable(GL_FRAMEBUFFER_SRGB); glEnable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0); 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) if (width <= 0 || height <= 0)
return; return;
if (m_fbo == 0) { if (m_fbo == 0) {
glGenFramebuffers(1, &m_fbo); m_fbo = std::make_unique<FrameBuffer>();
} }
if (m_oit_fbo == 0) { if (m_oit_fbo == 0) {
glGenFramebuffers(1, &m_oit_fbo); m_oit_fbo = std::make_unique<FrameBuffer>();
} }
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
m_screen_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D); m_screen_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
m_screen_texture->tex_image_2d(TextureFormat::RGB, TextureFormat::RGB, m_screen_texture->tex_image_2d(TextureFormat::RGB, TextureFormat::RGB,
GL_UNSIGNED_BYTE, nullptr, width, height); GL_UNSIGNED_BYTE, nullptr, width, height);
m_screen_texture->set_linear(); 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<Texture>(TextureType::TEXTURE_2D); m_screen_depth_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
m_screen_depth_texture->tex_image_2d(TextureFormat::DEPTH_COMPONENT32F, 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); GL_FLOAT, nullptr, width, height);
// m_screen_depth_texture->set_nearest(); // m_screen_depth_texture->set_nearest();
m_screen_depth_texture->set_linear(); m_screen_depth_texture->set_linear();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_fbo->attach(Attachment::DEPTH_ATTACHMENT, *m_screen_depth_texture);
m_screen_depth_texture->id(), 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { m_fbo->check_status();
Logger::error("FBO incomplete after resize!");
} else {
Logger::info("Frame Buffer Complete!");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo);
m_accum_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D); m_accum_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
m_accum_texture->tex_image_2d(TextureFormat::RGBA16F, TextureFormat::RGBA, m_accum_texture->tex_image_2d(TextureFormat::RGBA16F, TextureFormat::RGBA,
GL_HALF_FLOAT, nullptr, width, height); GL_HALF_FLOAT, nullptr, width, height);
m_accum_texture->set_linear(); m_accum_texture->set_linear();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_oit_fbo->attach(Attachment::COLOR_ATTACHMENT0, *m_accum_texture);
m_accum_texture->id(), 0);
m_reveal_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D); m_reveal_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
m_reveal_texture->tex_image_2d(TextureFormat::R16F, TextureFormat::RED, m_reveal_texture->tex_image_2d(TextureFormat::R16F, TextureFormat::RED,
GL_HALF_FLOAT, nullptr, width, height); GL_HALF_FLOAT, nullptr, width, height);
m_reveal_texture->set_linear(); m_reveal_texture->set_linear();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_oit_fbo->attach(Attachment::COLOR_ATTACHMENT1, *m_reveal_texture);
m_reveal_texture->id(), 0);
m_oit_depth_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D); m_oit_depth_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
m_oit_depth_texture->tex_image_2d(TextureFormat::DEPTH_COMPONENT32F, m_oit_depth_texture->tex_image_2d(TextureFormat::DEPTH_COMPONENT32F,
TextureFormat::DEPTH_COMPONENT, GL_FLOAT, TextureFormat::DEPTH_COMPONENT, GL_FLOAT,
nullptr, width, height); nullptr, width, height);
// m_oit_depth_texture->set_nearest(); // m_oit_depth_texture->set_nearest();
m_oit_depth_texture->set_linear(); m_oit_depth_texture->set_linear();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_oit_fbo->attach(Attachment::DEPTH_ATTACHMENT, *m_oit_depth_texture);
m_oit_depth_texture->id(), 0);
GLenum draw_buffer[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; std::array<GLenum, 2> draw_buffer = {GL_COLOR_ATTACHMENT0,
glDrawBuffers(2, draw_buffer); GL_COLOR_ATTACHMENT1};
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { m_oit_fbo->draw_buffer(draw_buffer);
Logger::error("FBO incomplete after resize!");
} else { m_oit_fbo->check_status();
Logger::info("Frame Buffer Complete!");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// depth map fbo // depth map fbo
if (m_depth_map_fbo == 0) { if (m_depth_map_fbo == 0) {
glGenFramebuffers(1, &m_depth_map_fbo); m_depth_map_fbo = std::make_unique<FrameBuffer>();
} }
m_depth_map_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D); m_depth_map_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D);
@@ -557,18 +544,13 @@ void Renderer::updata_framebuffer(int width, int height) {
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
// GL_COMPARE_REF_TO_TEXTURE); // GL_COMPARE_REF_TO_TEXTURE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); // 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_width = width;
m_height = height; m_height = height;
} }
@@ -634,7 +616,7 @@ void Renderer::render_world() {
glCullFace(GL_BACK); glCullFace(GL_BACK);
} }
glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); m_depth_map_fbo->bind();
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@@ -680,7 +662,7 @@ void Renderer::render_world() {
auto& player_shadow = get_shader("player_depth"); auto& player_shadow = get_shader("player_depth");
m_player_renderer.shadow_render(player_shadow, light_space_matrix); m_player_renderer.shadow_render(player_shadow, light_space_matrix);
} }
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); m_fbo->bind();
glCullFace(GL_BACK); glCullFace(GL_BACK);
glViewport(0, 0, m_width, m_height); glViewport(0, 0, m_width, m_height);
@@ -784,15 +766,15 @@ void Renderer::render_world() {
} }
// copy depth buffer // 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, glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height,
GL_DEPTH_BUFFER_BIT, GL_NEAREST); GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo); m_oit_fbo->bind(FrameBufferType::DRAW_FRAMEBUFFER);
// pass one accumulate // pass one accumulate
glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo); m_oit_fbo->bind();
glClearBufferfv(GL_COLOR, 0, glm::value_ptr(glm::vec4(0.0f))); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(glm::vec4(0.0f)));
float one = 1.0f; float one = 1.0f;
@@ -908,7 +890,7 @@ void Renderer::render_world() {
m_accum_texture->bind(0); m_accum_texture->bind(0);
m_reveal_texture->bind(1); m_reveal_texture->bind(1);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); m_fbo->bind();
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0); glBindVertexArray(0);

View File

@@ -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() { void Texture::unbind() {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, 0);