/* QGL Framebuffer Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "glframebuffer.h" #include #include Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum _target) : f(f_) , pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) { is_depth = withDepth; target_ = _target; color_formats.fill(colorFormat_, colorAttachments_); colors.fill(0, colorAttachments_); fbo = drbo = 0; tex_d = 0; wid = hei = 0; pbo_queried = 0; is_changed = false; } Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, QVector colors_, bool withDepth, GLenum _target) : f(f_) , pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) { is_depth = withDepth; target_ = _target; color_formats = colors_; colors.fill(0, colors_.size()); fbo = drbo = 0; tex_d = 0; wid = hei = 0; pbo_queried = 0; is_changed = false; } Framebuffer::~Framebuffer() { if (fbo > 0) deleteGLFramebuffer(fbo); deleteGLRenderbuffer(drbo); for (int i = 0; i < colors.size(); ++i) deleteGLTexture(f, colors[i]); deleteGLTexture(f, tex_d); } void Framebuffer::resize(int width, int height, bool force) { if (fbo > 0) { if ((wid == width) && (hei == height) && !force) return; } wid = width; hei = height; if (fbo > 0) deleteGLFramebuffer(fbo); f->glGenFramebuffers(1, &fbo); f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); // qDebug() << "resize" << f << wid << hei << fbo; for (int i = 0; i < colors.size(); ++i) { deleteGLTexture(f, colors[i]); createGLTexture(f, colors[i], width, height, color_formats[i], target_); f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); f->glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4); f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0); } if (is_depth) { deleteGLTexture(f, tex_d); if (drbo > 0) deleteGLRenderbuffer(drbo); f->glGenRenderbuffers(1, &drbo); f->glBindRenderbuffer(GL_RENDERBUFFER, drbo); f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo); createGLTexture(f, tex_d, width, height, GL_DEPTH_COMPONENT); f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0); } f->glBindFramebuffer(GL_FRAMEBUFFER, 0); if (pbo.isInit()) { enablePixelBuffer(); } is_changed = false; } void Framebuffer::reinit() { pbo.reinit(); colors.fill(0); fbo = drbo = 0; tex_d = 0; pbo_queried = 0; is_changed = true; } QImage Framebuffer::grab() const { return QImage(); } void Framebuffer::queryPoint(int index, QPoint p) { pbo_queried = 0; if (index < 0 || index >= colors.size()) return; if (!rect().contains(p) || !pbo.isInit()) return; f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); pbo.bind(f); f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); pbo_queried = 1; pbo.release(f); } void Framebuffer::queryPoints(int index, QRect rect_, GLenum pixel_format) { pbo_queried = 0; if (index < 0 || index >= colors.size()) return; rect_ &= rect(); if (rect_.isEmpty() || !pbo.isInit()) return; f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); pbo.bind(f); f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, pixel_format, 0); pbo_queried = rect_.width() * rect_.height(); pbo.release(f); } void Framebuffer::queryImage(int index) { queryPoints(index, rect()); } uint Framebuffer::getPoint() const { if (!pbo.isInit() || (pbo_queried == 0)) return 0; uint ret = 0; pbo.bind(f); void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint)); if (map) memcpy(&ret, map, sizeof(uint)); pbo.unmap(f); pbo.release(f); return ret; } QVector Framebuffer::getPointsByte() const { QVector ret; if (!pbo.isInit() || (pbo_queried == 0)) return ret; ret.resize(pbo_queried); pbo.bind(f); void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint)); if (map) memcpy(ret.data(), map, pbo_queried * sizeof(uint)); pbo.unmap(f); pbo.release(f); return ret; } QVector Framebuffer::getPointsFloat() const { QVector ret; if (!pbo.isInit() || (pbo_queried == 0)) return ret; ret.resize(pbo_queried); pbo.bind(f); void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(QVector4D)); if (map) memcpy(ret.data(), map, pbo_queried * sizeof(QVector4D)); pbo.unmap(f); pbo.release(f); return ret; } QImage Framebuffer::getImage() const { QImage ret; if (!pbo.isInit() || (pbo_queried == 0)) return ret; ret = QImage(size(), QImage::Format_RGBA8888); int bytes = width() * height() * 4; pbo.bind(f); void * map = pbo.map(f, GL_MAP_READ_BIT, bytes); if (map) memcpy(ret.bits(), map, bytes); pbo.unmap(f); pbo.release(f); return ret; } QVector Framebuffer::grabF(int index) const { QVector ret; if (index < 0 || index >= colors.size()) return ret; f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); ret.resize(wid * hei * 4); f->glReadPixels(0, 0, wid, hei, GL_RGBA, GL_FLOAT, ret.data()); return ret; } void Framebuffer::blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask, GLenum filter) const { if (index_from < 0 || index_from >= colors.size()) return; GLenum e = GL_COLOR_ATTACHMENT0 + index_to; f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_to); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index_from); f->glDrawBuffers(1, &e); f->glBlitFramebuffer(from.x(), from.y(), from.right(), from.bottom(), to.x(), to.y(), to.right(), to.bottom(), mask, filter); } void Framebuffer::bind() { if (is_changed) resize(wid, hei); if (fbo == 0) return; f->glGetIntegerv(GL_VIEWPORT, prev_view); f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); setWriteBuffers(); f->glReadBuffer(GL_COLOR_ATTACHMENT0); f->glViewport(0, 0, wid, hei); } void Framebuffer::release() { is_changed = false; if (fbo == 0) return; f->glBindFramebuffer(GL_FRAMEBUFFER, 0); f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]); } void Framebuffer::setWriteBuffer(int index) { unsetWriteBuffers(); GLenum e = GL_COLOR_ATTACHMENT0 + index; f->glDrawBuffers(1, &e); } void Framebuffer::setWriteBuffers(const int * indeces, int count) { unsetWriteBuffers(); QVector buffers; for (int i = 0; i < count; ++i) buffers << GL_COLOR_ATTACHMENT0 + indeces[i]; f->glDrawBuffers(buffers.size(), buffers.constData()); } void Framebuffer::setWriteBuffers() { QVector buffers; for (int i = 0; i < colors.size(); ++i) buffers << GL_COLOR_ATTACHMENT0 + i; f->glDrawBuffers(buffers.size(), buffers.constData()); } void Framebuffer::unsetWriteBuffers() { QVector buffers(colors.size(), GL_NONE); f->glDrawBuffers(buffers.size(), buffers.constData()); } void Framebuffer::enablePixelBuffer() { pbo.init(f); pbo.bind(f); pbo.resize(f, width() * height() * 4 * 4); pbo.release(f); } void Framebuffer::setColorTextureFiltering(int index, GLenum filter) { bindColorTexture(index); f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, filter); f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, filter); } void Framebuffer::bindColorTexture(int index, int channel) { if (index < 0 || index >= colors.size()) return; f->glActiveTexture(GL_TEXTURE0 + channel); f->glBindTexture(GL_TEXTURE_2D, colors[index]); } void Framebuffer::bindColorTextures() { for (int i = colors.size() - 1; i >= 0; --i) { f->glActiveTexture(GL_TEXTURE0 + i); f->glBindTexture(GL_TEXTURE_2D, colors[i]); } } void Framebuffer::bindDepthTexture(int channel) { f->glActiveTexture(GL_TEXTURE0 + channel); f->glBindTexture(GL_TEXTURE_2D, tex_d); } void Framebuffer::deleteGLRenderbuffer(GLuint & drbo) { if (drbo != 0) f->glDeleteRenderbuffers(1, &drbo); drbo = 0; } void Framebuffer::deleteGLFramebuffer(GLuint & fbo) { if (fbo != 0) f->glDeleteFramebuffers(1, &fbo); fbo = 0; }