File FrameBuffer.cpp
File List > buffers > FrameBuffer.cpp
Go to the documentation of this file
#include "FrameBuffer.h"
#include "macros.h"
Texture::TextureType FrameBuffer::PareseTexType(FBType _type)
{
Texture::TextureType textype = Texture::NONE_TEXTURE;
if (ISIN(_type, 0, 1, 2, 3, 4, 5))
textype = Texture::HDR_BUFFER_TEXTURE;
else if (ISIN(_type, ALBEDO_FB, MASK_FB, RAND_FB, ID_FB))
textype = Texture::BUFFER_TEXTURE;
else if (ISIN(_type, SINGLE_FB, SHADOW_FB))
textype = Texture::FLOAT_BUFFER_TEXTURE;
else if (_type == -32) {}
return textype;
}
void FrameBuffer::_cpyInfo(const FrameBuffer& fb)
{
fb_ID = fb.GetFrameBufferID(); fb_attach = fb.fb_attach; fb_w = fb.fb_w; fb_h = fb.fb_h;
}
void FrameBuffer::_delFB()
{
glDeleteFramebuffers(1, &fb_ID);
fb_ID = 0;
}
FrameBuffer::FrameBuffer()
{}
FrameBuffer::FrameBuffer(FBType type/*=NONE_FB*/, GLuint attach)
:renderBuffer(RenderBuffer(GL_DEPTH24_STENCIL8)), fb_w(SCREEN_W), fb_h(SCREEN_H)
{
fb_type_list[(FBType)type] = 0;
Texture::TextureType textype = FrameBuffer::PareseTexType(type);
fb_tex_list.emplace_back("", textype, GL_LINEAR);
fb_tex_list[0].OffsetSlot(type);
//IDTexture = Texture("", BUFFER_TEXTURE, GL_NEAREST);
//IDTexture.SlotAdd(1);
//BufferTexture.Bind(BufferTexture.Tex_type);
glGenFramebuffers(1, &fb_ID);//GLDEBUG
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + type, GL_TEXTURE_2D, fb_tex_list[0].GetTexID(), 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->GetRenderBufferID());
//glDrawBuffer(fb_type);GLDEBUG
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "framebuffer error\n";
_delFB();
}
else
{
DEBUG("framebuffer is complete\n");
}
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, attachments);
//UnbindFrameBuffer();
}
FrameBuffer::FrameBuffer(int count, ...)
:renderBuffer(RenderBuffer(GL_DEPTH24_STENCIL8)), fb_w(SCREEN_W), fb_h(SCREEN_H)
{
glGenFramebuffers(1, &fb_ID);//GLDEBUG
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
std::vector<GLenum> attachments(count);
va_list arg_ptr;
va_start(arg_ptr, count);
LOOP(count) {
int type_inp = va_arg(arg_ptr, int);
Texture::TextureType textype = FrameBuffer::PareseTexType((FBType)type_inp);
fb_type_list[(FBType)type_inp] = i;
fb_tex_list.emplace_back("", textype, GL_LINEAR);
fb_tex_list[i].OffsetSlot(type_inp);
attachments[i] = GL_COLOR_ATTACHMENT0 + i;
glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, fb_tex_list[i].GetTexID(), 0);
}
va_end(arg_ptr);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->GetRenderBufferID());
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "framebuffer error\n";
_delFB();
}
else
{
DEBUG("framebuffer is complete");
}
glDrawBuffers(count, attachments.data());
UnbindFrameBuffer();
}
FrameBuffer::FrameBuffer(const std::vector<FBType>& _tars)
:renderBuffer(RenderBuffer(GL_DEPTH24_STENCIL8)), fb_w(SCREEN_W), fb_h(SCREEN_H)
{
glGenFramebuffers(1, &fb_ID);//GLDEBUG
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
std::vector<GLenum> attachments(_tars.size());
fb_tex_list.reserve(_tars.size());
for (int i = 0; auto type_inp : _tars) {
Texture::TextureType textype = FrameBuffer::PareseTexType(type_inp);
fb_type_list[(FBType)type_inp] = i;
fb_tex_list.emplace_back(SCREEN_W, SCREEN_H, textype, GL_LINEAR);
fb_tex_list[i].OffsetSlot(type_inp);
attachments[i] = GL_COLOR_ATTACHMENT0 + i;
glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, fb_tex_list[i].GetTexID(), 0);
i++;
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer->GetRenderBufferID());
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "framebuffer error\n";
_delFB();
}
else
{
DEBUG("framebuffer is complete");
}
glDrawBuffers(attachments.size(), attachments.data());
UnbindFrameBuffer();
}
FrameBuffer::FrameBuffer(Texture&& tex)
: fb_w(SCREEN_W), fb_h(SCREEN_H)
{
auto [_1, format, _3, gl_type] = Texture::ParseFormat(tex.tex_type);
const bool is_depth = (format == GL_DEPTH_COMPONENT);
GLenum attachment = is_depth ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0;
glGenFramebuffers(1, &fb_ID);
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
// Attach texture
switch (gl_type)
{
case GL_TEXTURE_2D:
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex.GetTexID(), 0);
break;
case GL_TEXTURE_CUBE_MAP:
glFramebufferTexture(GL_FRAMEBUFFER, attachment, tex.GetTexID(), 0);
break;
}
if (is_depth)
{
// Depth-only FBO
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
}
else
{
// Color attachment FBO
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, drawBuffers);
}
// Validate framebuffer
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cerr << "Framebuffer not complete!" << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
fb_tex_list.emplace_back(std::move(tex));
}
void FrameBuffer::_deepCopyFrom(const FrameBuffer& fb)
{
fb_attach = fb.fb_attach;
fb_w = fb.fb_w;
fb_h = fb.fb_h;
fb_type_list = fb.fb_type_list;
fb_tex_list = fb.fb_tex_list; // Texture has correct deep copy
if (fb.renderBuffer)
renderBuffer = fb.renderBuffer; // RenderBuffer now has correct deep copy
if (fb.fb_ID == 0)
{
fb_ID = 0;
return;
}
glGenFramebuffers(1, &fb_ID);
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
std::vector<GLenum> drawAttachments;
bool has_depth_tex = false;
auto attach_tex = [&](int idx, GLenum attachment)
{
const Texture& tex = fb_tex_list[idx];
auto [_1, _2, _3, gl_tex_type] = Texture::ParseFormat(tex.tex_type);
switch (gl_tex_type)
{
case GL_TEXTURE_2D:
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex.GetTexID(), 0);
break;
default:
glFramebufferTexture(GL_FRAMEBUFFER, attachment, tex.GetTexID(), 0);
break;
}
};
if (!fb_type_list.empty())
{
// Standard multi-target FBO: re-attach each texture at its original slot.
for (auto& [type, idx] : fb_type_list)
{
auto [_1, format, _3, _4] = Texture::ParseFormat(fb_tex_list[idx].tex_type);
const bool is_depth = (format == GL_DEPTH_COMPONENT);
if (is_depth)
{
has_depth_tex = true;
attach_tex(idx, GL_DEPTH_ATTACHMENT);
}
else
{
GLenum att = GL_COLOR_ATTACHMENT0 + idx;
attach_tex(idx, att);
drawAttachments.push_back(att);
}
}
}
else if (!fb_tex_list.empty())
{
// Texture-only FBO (FrameBuffer(Texture&&) constructor path).
for (int i = 0; i < (int)fb_tex_list.size(); i++)
{
auto [_1, format, _3, _4] = Texture::ParseFormat(fb_tex_list[i].tex_type);
const bool is_depth = (format == GL_DEPTH_COMPONENT);
if (is_depth)
{
has_depth_tex = true;
attach_tex(i, GL_DEPTH_ATTACHMENT);
}
else
{
GLenum att = GL_COLOR_ATTACHMENT0;
attach_tex(i, att);
drawAttachments.push_back(att);
}
}
}
if (renderBuffer)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, renderBuffer->GetRenderBufferID());
if (!drawAttachments.empty())
glDrawBuffers((GLsizei)drawAttachments.size(), drawAttachments.data());
else if (has_depth_tex)
{
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FrameBuffer::FrameBuffer(const FrameBuffer& fb)
{
_deepCopyFrom(fb);
}
FrameBuffer::FrameBuffer(FrameBuffer&& fb) noexcept
{
_cpyInfo(fb);
fb_type_list = std::move(fb.fb_type_list);
fb_tex_list = std::move(fb.fb_tex_list);
if (fb.renderBuffer)
renderBuffer = std::move(fb.renderBuffer);
fb.fb_ID = 0;
}
FrameBuffer& FrameBuffer::operator=(const FrameBuffer& fb)
{
if (this == &fb)
return *this;
if (fb_ID != 0)
_delFB();
renderBuffer.reset();
fb_type_list.clear();
fb_tex_list.clear();
_deepCopyFrom(fb);
return *this;
}
FrameBuffer& FrameBuffer::operator=(FrameBuffer&& fb) noexcept
{
if (this == &fb)
return *this;
if (fb_ID != 0)
_delFB();
renderBuffer.reset();
_cpyInfo(fb);
fb_type_list = std::move(fb.fb_type_list);
fb_tex_list = std::move(fb.fb_tex_list);
if (fb.renderBuffer)
renderBuffer = std::move(fb.renderBuffer);
fb.fb_ID = 0;
return *this;
}
FrameBuffer::~FrameBuffer()
{
if (GetFrameBufferID() != 0)
_delFB();
}
void FrameBuffer::BindFrameBuffer() const
{
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
}
void FrameBuffer::UnbindFrameBuffer()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::LinkTexture(const Texture& _tex)
{
auto [_1, _data, _3, gl_type] = Texture::ParseFormat(_tex.tex_type);
const bool is_depth = _data == GL_DEPTH_COMPONENT;
GLenum attachment = is_depth ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0;
BindFrameBuffer();
switch (gl_type)
{
case GL_TEXTURE_2D:
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, gl_type, _tex.GetTexID(), 0);
break;
case GL_TEXTURE_CUBE_MAP:
glFramebufferTexture(GL_FRAMEBUFFER, attachment, _tex.GetTexID(), 0);
break;
}
if (is_depth)
{
// Depth-only FBO
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
}
else
{
// Color attachment FBO
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, drawBuffers);
}
Resize(_tex.GetW(), _tex.GetH());
UnbindFrameBuffer();
}
void FrameBuffer::AppendTexture(const Texture& _tex, FBType _type)
{
std::vector<GLenum> attachments(fb_tex_list.size() + 1);
auto [_1, _2, _3, gl_type] = Texture::ParseFormat(_tex.tex_type);
LOOP(attachments.size())
attachments[i] = GL_COLOR_ATTACHMENT0 + i;
fb_type_list[(FBType)_type] = fb_tex_list.size();
fb_tex_list.emplace_back(_tex);
fb_tex_list[fb_tex_list.size() - 1].OffsetSlot(_type);
switch (gl_type)
{
case GL_TEXTURE_2D:
glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[attachments.size()-1], gl_type, _tex.GetTexID(), 0);
break;
case GL_TEXTURE_CUBE_MAP:
glFramebufferTexture(GL_FRAMEBUFFER, attachments[attachments.size() - 1], _tex.GetTexID(), 0);
break;
}
glDrawBuffers(attachments.size(), attachments.data());
}
void FrameBuffer::Resize(const glm::vec2& size, bool all)
{
Resize(GLuint(size[0]), GLuint(size[1]), all);
}
void FrameBuffer::Resize(GLuint w, GLuint h, bool all)
{
if (fb_w == w and fb_h == h) return;
fb_w = w;
fb_h = h;
if (renderBuffer.has_value())
renderBuffer->Resize(w, h);
for (auto& tex : fb_tex_list)
tex.Resize(w, h);
glBindFramebuffer(GL_FRAMEBUFFER, fb_ID);
}
#include "glm/glm.hpp"
//https://docs.gl/gl3/glReadPixels
FBPixel FrameBuffer::ReadPix(GLuint x, GLuint y, FBType type) const
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_ID);
glReadBuffer(GL_COLOR_ATTACHMENT0 + fb_type_list[type]);
FBPixel Pixel;
glReadPixels(x, fb_h - y + 20, 1, 1, GL_RGBA, GL_FLOAT, &Pixel);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
Pixel.RGBA[0] = glm::round(Pixel.RGBA[0] * 256);
return Pixel;
}
void FrameBuffer::BindFrameBufferTex(int count, ...) const
{
if (count == 0) {
for(auto& pair : fb_type_list)
fb_tex_list[pair.second].Bind(Texture::BUFFER_TEXTURE + pair.first);
return;
}
va_list arg_ptr;
va_start(arg_ptr, count);
LOOP(count) {
int type = va_arg(arg_ptr, int);
fb_tex_list[fb_type_list[(FBType)type]].Bind(Texture::BUFFER_TEXTURE + type);
}
va_end(arg_ptr);
}
void FrameBuffer::BindFrameBufferTex(const std::vector<FBType>& _tars) const
{
for (FBType tar : _tars) {
fb_tex_list[fb_type_list[tar]].Bind(Texture::BUFFER_TEXTURE + tar);
}
}
void FrameBuffer::BindFrameBufferTex(FBType tar, GLuint slot) const
{
fb_tex_list[fb_type_list[tar]].Bind(slot);
}
void FrameBuffer::BindFrameBufferTexR(FBType tar, GLuint slot) const
{
fb_tex_list[fb_type_list[tar]].BindC(slot, GL_READ_WRITE);
}
void FrameBuffer::UnbindFrameBufferTexR(FBType tar, GLuint slot) const
{
fb_tex_list[fb_type_list[tar]].UnbindC(slot);
fb_tex_list[fb_type_list[tar]].Unbind();
}
void FrameBuffer::UnbindFrameBufferTex() const
{
for (auto& tar : fb_tex_list) {
tar.Unbind();
}
}