#include "Framebuffer.h" #include #include #include #include "DebugColors.h" #include #include int checkFramebufferStatus() { int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch (status) { case GL_FRAMEBUFFER_COMPLETE: return 0; break; default: fprintf(stderr, "Framebuffer.h: " DC_ERROR " code: %#04x\n", (int)status); return -1; break; } } Framebuffer::Framebuffer(const int& width, const int& height) { glGenFramebuffers(1, &ID); glBindFramebuffer(GL_FRAMEBUFFER, ID); glGenTextures(1, &ColorBuffer); glBindTexture(GL_TEXTURE_2D, ColorBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorBuffer, 0); glGenRenderbuffers(1, &DepthStencilBuffer); glBindRenderbuffer(GL_RENDERBUFFER, DepthStencilBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, DepthStencilBuffer); if (checkFramebufferStatus() != 0) { glDeleteRenderbuffers(1, &DepthStencilBuffer); glDeleteTextures(1, &ColorBuffer); glDeleteFramebuffers(1, &ID); std::cerr << "Framebuffer.h: " DC_ERROR " Could not create framebuffer object." << std::endl; ID = 0; return; } glBindFramebuffer(GL_FRAMEBUFFER, 0); this->width = width; this->height = height; } Framebuffer::~Framebuffer() { destroy(); } void Framebuffer::destroy() { glDeleteRenderbuffers(1, &DepthStencilBuffer); glDeleteTextures(1, &ColorBuffer); glDeleteFramebuffers(1, &ID); } void Framebuffer::UpdateSize(const unsigned int& newWidth, const unsigned int& newHeight) { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glBindTexture(GL_TEXTURE_2D, ColorBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newWidth, newHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); glDeleteRenderbuffers(1, &DepthStencilBuffer); glGenRenderbuffers(1, &DepthStencilBuffer); glBindRenderbuffer(GL_RENDERBUFFER, DepthStencilBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, newWidth, newHeight); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, DepthStencilBuffer); } void Framebuffer::Use() { glBindFramebuffer(GL_FRAMEBUFFER, ID); } void Framebuffer::UseDefault() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } void Framebuffer::clearBuffers(float r, float g, float b, float a) { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } void Framebuffer::clearColorBuffer(float r, float g, float b, float a) { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); } void Framebuffer::clearDepthBuffer() { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glClear(GL_DEPTH_BUFFER_BIT); } void Framebuffer::clearStencilBuffer() { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glClear(GL_STENCIL_BUFFER_BIT); } void Framebuffer::clearDepthStencilBuffer() { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } unsigned int Framebuffer::getWidth() { return width; } unsigned int Framebuffer::getHeight() { return height; } bool Framebuffer::checkBind() { int id = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id); if ((unsigned int)id == ID) return true; return false; } unsigned int Framebuffer::getColorBuffer() { return ColorBuffer; } unsigned int Framebuffer::getDepthSnectilBuffer() { return DepthStencilBuffer; } std::string Framebuffer::screenshot(std::string path) { int id = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id); unsigned char *data = new unsigned char[(int)width * (int)height * 4]; this->Use(); glReadBuffer(GL_COLOR_ATTACHMENT0); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_flip_vertically_on_write(1); std::string scr_name = path + "/" + getScreenshotName(); stbi_write_png(scr_name.c_str(), width, height, 4, data, 4 * width); delete data; glBindFramebuffer(GL_FRAMEBUFFER, (unsigned int)id); return scr_name; } MSFramebuffer::MSFramebuffer(const int& width, const int& height, const unsigned int samples) : Nsamples(samples), intermediateFBO(NULL) { glGenFramebuffers(1, &ID); glBindFramebuffer(GL_FRAMEBUFFER, ID); glGenTextures(1, &ColorBuffer); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, ColorBuffer); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE); glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, ColorBuffer, 0); glGenRenderbuffers(1, &DepthStencilBuffer); glBindRenderbuffer(GL_RENDERBUFFER, DepthStencilBuffer); glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, DepthStencilBuffer); if (checkFramebufferStatus() != 0) { glDeleteRenderbuffers(1, &DepthStencilBuffer); glDeleteTextures(1, &ColorBuffer); glDeleteFramebuffers(1, &ID); std::cerr << "Framebuffer.h: " DC_ERROR " Could not create multisampled framebuffer object." << std::endl; ID = 0; return; } glBindFramebuffer(GL_FRAMEBUFFER, 0); this->width = width; this->height = height; intermediateFBO = new Framebuffer(width, height); } MSFramebuffer::~MSFramebuffer() { destroy(); delete intermediateFBO; } std::string MSFramebuffer::screenshot(std::string path) { int id = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id); unsigned char *data = new unsigned char[width * height * 4]; getColorBuffer(); intermediateFBO->Use(); glReadBuffer(GL_COLOR_ATTACHMENT0); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_flip_vertically_on_write(1); std::string scr_name = path + "/" + getScreenshotName(); stbi_write_png(scr_name.c_str(), width, height, 4, data, 4 * width); delete data; glBindFramebuffer(GL_FRAMEBUFFER, (unsigned int)id); return scr_name; } void MSFramebuffer::UpdateSize(const unsigned int& newWidth, const unsigned int& newHeight) { if (!checkBind()) glBindFramebuffer(GL_FRAMEBUFFER, ID); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, ColorBuffer); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, Nsamples, GL_RGBA, newWidth, newHeight, GL_TRUE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glDeleteRenderbuffers(1, &DepthStencilBuffer); glGenRenderbuffers(1, &DepthStencilBuffer); glBindRenderbuffer(GL_RENDERBUFFER, DepthStencilBuffer); glRenderbufferStorageMultisample(GL_RENDERBUFFER, Nsamples, GL_DEPTH24_STENCIL8, newWidth, newHeight); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, DepthStencilBuffer); } unsigned int MSFramebuffer::getColorBuffer() { glBindFramebuffer(GL_READ_FRAMEBUFFER, ID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO->ID); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); return intermediateFBO->getColorBuffer(); } unsigned int MSFramebuffer::getDepthSnectilBuffer() { glBindFramebuffer(GL_READ_FRAMEBUFFER, ID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO->ID); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); return intermediateFBO->getDepthSnectilBuffer(); } unsigned int MSFramebuffer::getColorBufferMS() { return ColorBuffer; } /* Static functions */ void Framebuffer::clearAllBuffers(float r, float g, float b, float a) { glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } std::string Framebuffer::getScreenshotName() { time_t tim = time(NULL); struct tm *ltm = localtime(&tim); char buf[50]; snprintf(buf, sizeof(buf), "screenshot_%i%02i%02i_%02i%02i%02i.png", ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec); return std::string(buf); } std::string Framebuffer::screenshot_defaultFBO(unsigned int width, unsigned int height, std::string path) { int id = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &id); uint8_t *data = new uint8_t[width * height * 4]; glBindFramebuffer(GL_FRAMEBUFFER, 0); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_flip_vertically_on_write(1); std::string scr_name = path + "/" + getScreenshotName(); stbi_write_png(scr_name.c_str(), width, height, 4, data, 4 * width); delete[] data; glBindFramebuffer(GL_FRAMEBUFFER, (unsigned int)id); return scr_name; } /* Depthmap Framebuffer */ DepthmapFramebuffer::DepthmapFramebuffer(const Resolution& resolution) : resolution(resolution), depth_texture(0), ID(0) { glGenFramebuffers(1, &ID); glGenTextures(1, &depth_texture); glBindTexture(GL_TEXTURE_2D, depth_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, resolution.Width, resolution.Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); float borderColor[]{ 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, ID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); if (checkFramebufferStatus() != 0) { glDeleteFramebuffers(1, &ID); glDeleteTextures(1, &depth_texture); std::cerr << "Framebuffer.h " << DC_ERROR << " Could not create Depthmap Framebuffer object.\n"; ID = 0; return; } } DepthmapFramebuffer::DepthmapFramebuffer(const unsigned int& width, const unsigned int& height) : DepthmapFramebuffer(Resolution{width, height}) {} DepthmapFramebuffer::~DepthmapFramebuffer() { glDeleteFramebuffers(1, &ID); glDeleteTextures(1, &depth_texture); } void DepthmapFramebuffer::Use() const { glBindFramebuffer(GL_FRAMEBUFFER, ID); } void DepthmapFramebuffer::Use(GLenum target) const { glBindFramebuffer(target, ID); } unsigned int DepthmapFramebuffer::GetDepthTexture() const { return depth_texture; } void DepthmapFramebuffer::ClearDepthBuffer() const { Use(); glClear(GL_DEPTH_BUFFER_BIT); } /* DepthCubemap Framebuffer */ DepthCubeFramebuffer::DepthCubeFramebuffer(int width, int height) : Width(width), Height(height) { init(); } DepthCubeFramebuffer::~DepthCubeFramebuffer() { glDeleteFramebuffers(1, &ID); glDeleteTextures(1, &depthCubemap); } void DepthCubeFramebuffer::init() { glGenTextures(1, &depthCubemap); glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); for (unsigned int i = 0; i < 6; i++) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glGenFramebuffers(1, &ID); glBindFramebuffer(GL_FRAMEBUFFER, ID); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0); if (checkFramebufferStatus() != 0) { ID = 0; } } unsigned int DepthCubeFramebuffer::getDepthCubemap() const { return depthCubemap; } void DepthCubeFramebuffer::clearDepth() const { glClear(GL_DEPTH_BUFFER_BIT); } void DepthCubeFramebuffer::Use() const { glBindFramebuffer(GL_FRAMEBUFFER, 0); }