Framebuffer Object
Framebuffer Object
#include <easy3d/viewer/framebuffer_object.h>
#include <algorithm>
#include <cstring>
#include <easy3d/viewer/opengl_error.h>
#include <easy3d/viewer/opengl_info.h>
#include <easy3d/fileio/image_io.h>
#include <easy3d/util/file_system.h>
#include <easy3d/util/logging.h>
namespace easy3d {
bool FramebufferObject::is_supported() {
return OpenglInfo::is_supported("GL_VERSION_3_2") ||
OpenglInfo::is_supported("GL_ARB_framebuffer_object") ||
OpenglInfo::has_entension("GL_EXT_framebuffer_object");
}
init(w, h, samples);
}
valid_ = is_supported();
if (!valid_)
return;
FramebufferObject::~FramebufferObject()
{
clear();
}
void FramebufferObject::clear() {
if (is_bound())
release();
if (glIsTexture(depth_texture_)) {
glDeleteTextures(1, &depth_texture_);
easy3d_debug_log_gl_error;
depth_texture_ = 0;
}
if (depth_buffer_) {
glDeleteRenderbuffers(1, &depth_buffer_); easy3d_debug_log_gl_error;
depth_buffer_ = 0;
}
if (fbo_id_) {
glDeleteFramebuffers(1, &fbo_id_);
easy3d_debug_log_gl_error;
fbo_id_ = 0;
}
if (resolved_fbo_) {
delete resolved_fbo_;
easy3d_debug_log_gl_error;
resolved_fbo_ = nullptr;
}
}
////////////////////////////////////////////////////////////////////////
//
clear();
init(w, h, samples_);
if (need_depth) {
if (depth_as_texture)
add_depth_texture(depth_internal_format_, depth_texture_filter_,
depth_texture_compare_mode_, depth_texture_compare_func_);
else
add_depth_buffer(depth_internal_format_);
}
valid_ = check_status();
if (!valid_) {
glDeleteFramebuffers(1, &fbo_id_); easy3d_debug_log_gl_error;
}
}
static bool color_format_compatible(GLenum internal_format, GLenum format,
GLenum type)
{
if (format != GL_RED && format != GL_RG && format != GL_RGB && format !=
GL_BGR && format != GL_RGBA && format != GL_BGRA) {
LOG(ERROR) << "the provided format is not accepted";
return false;
}
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//
return true;
}
bool FramebufferObject::add_color_texture(
GLenum internal_format /* = GL_RGBA8*/, // GL_[components][size][type],
e.g., GL_RG8, GL_RGBA16, GL_R16F, GL_RG16, GL_RGBA32F ...
GLenum format /* = GL_RGBA*/, // The format of the pixel
data (GL_RED, GL_RG, GL_RGB, GL_BGR, GL_BGRA ...)
GLenum type /* = GL_UNSIGNED_BYTE*/, // The data type of the pixel
data (GL_BYTE, GL_SHORT, GL_UNSIGNED_INT, GL_INT, GL_FLOAT ...)
GLenum filter /* = GL_NEAREST*/ // The texture
minifying/magnification functions. e.g., GL_NEAREST, GL_LINEAR
)
{
if (fbo_id_ == 0) {
LOG(ERROR) << "fbo not created";
return false;
}
GLint max_attachments;
// How many color attachment points an FBO can have.
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_attachments);
if (color_attachments_.size() >= max_attachments) {
LOG(ERROR) << "maximum color attachment reached";
return false;
}
ColorAttachment attachment;
attachment.internal_format = internal_format;
attachment.format = format;
attachment.type = type;
attachment.texture_filter = filter;
//----------------------------------------------------------------------
------------------------
// color render buffer bound to texture
// see
https://www.khronos.org/opengl/wiki/Framebuffer_Object_Extension_Examples#Quick_
example.2C_render_to_texture_.282D.29
glGenTextures(1, &(attachment.texture));
easy3d_debug_log_gl_error;
if (samples_ > 0) {
texture_target_ = GL_TEXTURE_2D_MULTISAMPLE;
glBindTexture(texture_target_, attachment.texture);
easy3d_debug_log_gl_error;
glTexImage2DMultisample(texture_target_, samples_, internal_format,
width_, height_, GL_TRUE); easy3d_debug_log_gl_error;
}
else {
texture_target_ = GL_TEXTURE_2D;
glBindTexture(texture_target_, attachment.texture);
easy3d_debug_log_gl_error;
//----------------------------------------------------------------------
------------------------
GLuint currentFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
// attach texture
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index,
texture_target_, attachment.texture, 0); easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
//----------------------------------------------------------------------
------------------------
valid_ = check_status();
if (valid_)
color_attachments_.push_back(attachment);
else
glDeleteTextures(1, &(attachment.texture));
easy3d_debug_log_gl_error;
return valid_;
}
bool FramebufferObject::add_color_buffer(
GLenum internal_format /* = GL_RGBA8*/, // GL_[components][size][type],
e.g., GL_RG8, GL_RGBA16, GL_R16F, GL_RG16, GL_RGBA32F ...
GLenum format /* = GL_RGBA*/, // The format of the pixel
data (GL_RED, GL_RG, GL_RGB, GL_BGR, GL_BGRA ...)
GLenum type /* = GL_UNSIGNED_BYTE*/) // The data type of the pixel
data (GL_BYTE, GL_SHORT, GL_UNSIGNED_INT, GL_INT, GL_FLOAT ...)
{
if (fbo_id_ == 0) {
LOG(ERROR) << "fbo not generated";
return false;
}
GLint max_attachments;
// How many color attachment points an FBO can have.
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_attachments);
if (color_attachments_.size() >= max_attachments) {
LOG(ERROR) << "maximum color attachment reached";
return false;
}
ColorAttachment attachment;
attachment.internal_format = internal_format;
attachment.format = format;
attachment.type = type;
attachment.texture_filter = GL_NEAREST; // set to default value
//----------------------------------------------------------------------
------------------------
glGenRenderbuffers(1, &(attachment.buffer));
easy3d_debug_log_gl_error;
glBindRenderbuffer(GL_RENDERBUFFER, attachment.buffer);
easy3d_debug_log_gl_error;
if (samples_ > 0) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples_,
internal_format, width_, height_); easy3d_debug_log_gl_error;
}
else {
glRenderbufferStorage(GL_RENDERBUFFER, internal_format, width_,
height_); easy3d_debug_log_gl_error;
}
//----------------------------------------------------------------------
------------------------
GLuint currentFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
//----------------------------------------------------------------------
------------------------
valid_ = check_status();
if (valid_) {
// Query the actual number of samples. This can be greater than the
requested
// value since the typically supported values are 0, 4, 8, ..., and
the
// requests are mapped to the next supported value.
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_SAMPLES, &samples_); easy3d_debug_log_gl_error;
color_attachments_.push_back(attachment);
}
else {
glDeleteRenderbuffers(1, &(attachment.buffer));
easy3d_debug_log_gl_error;
}
return valid_;
}
depth_internal_format_ = internal_format;
depth_texture_filter_ = filter;
depth_texture_compare_mode_ = compare_mode;
depth_texture_compare_func_ = compare_func;
//----------------------------------------------------------------------
------------------------
// see
https://www.khronos.org/opengl/wiki/Framebuffer_Object_Extension_Examples#Quick_
example.2C_render_to_texture_.282D.29
glGenTextures(1, &depth_texture_); easy3d_debug_log_gl_error;
if (samples_ > 0) {
texture_target_ = GL_TEXTURE_2D_MULTISAMPLE;
glBindTexture(texture_target_, depth_texture_);
easy3d_debug_log_gl_error;
// Liangliang: It seems no parameters need to be set for multisample
depth texture.
//glTexParameteri(texture_target_, GL_DEPTH_TEXTURE_MODE,
GL_LUMINANCE); easy3d_debug_log_gl_error;
glBindTexture(texture_target_, depth_texture_);
easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE); easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); easy3d_debug_log_gl_error;
//glTexParameteri(texture_target_, GL_DEPTH_TEXTURE_MODE,
GL_LUMINANCE); easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_COMPARE_MODE,
compare_mode); easy3d_debug_log_gl_error;
if (compare_mode != GL_NONE) {
glTexParameteri(texture_target_, GL_TEXTURE_COMPARE_FUNC,
compare_func); easy3d_debug_log_gl_error;
// GL_COMPARE_REF_TO_TEXTURE sets the comparison function to
'less than or equal', so the
// result of the sample operation will be 1.0 if the reference
value is less than or equal
// to the value in the texture and zero otherwise.
}
//----------------------------------------------------------------------
------------------------
GLuint currentFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
// attach texture
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, texture_target_,
depth_texture_, 0); easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
//----------------------------------------------------------------------
------------------------
valid_ = check_status();
if (!valid_) {
glDeleteTextures(1, &depth_texture_); easy3d_debug_log_gl_error;
}
glBindTexture(texture_target_, 0);
easy3d_debug_log_gl_error;
return valid_;
}
GLenum attachment;
if ((internal_format == GL_DEPTH24_STENCIL8) || (internal_format ==
GL_DEPTH32F_STENCIL8)) {
attachment = GL_DEPTH_STENCIL_ATTACHMENT;
}
else {
attachment = GL_DEPTH_ATTACHMENT;
}
depth_internal_format_ = internal_format;
depth_texture_filter_ = GL_NEAREST; // set to default value
//----------------------------------------------------------------------
------------------------
glGenRenderbuffers(1, &depth_buffer_);
easy3d_debug_log_gl_error;
glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer_);
easy3d_debug_log_gl_error;
if (samples_ > 0) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples_,
internal_format, width_, height_); easy3d_debug_log_gl_error;
}
else {
glRenderbufferStorage(GL_RENDERBUFFER, internal_format, width_,
height_); easy3d_debug_log_gl_error;
}
//----------------------------------------------------------------------
------------------------
GLuint currentFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
//----------------------------------------------------------------------
------------------------
valid_ = check_status();
if (!valid_) {
glDeleteRenderbuffers(1, &depth_buffer_);
easy3d_debug_log_gl_error;
depth_buffer_ = 0;
}
return valid_;
}
// attach texture
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, target,
texture_id, 0); easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
ColorAttachment attach;
attach.texture = texture_id;
// attach.internal_format = internal_format;
// attach.format = format;
// attach.type = type;
// attach.texture_filter = GL_NEAREST;
LOG(WARNING) << "attach an externally created texture not fully
supported";
color_attachments_.push_back(attach);
//------------------------------------------------------------------
----------------------------
valid_ = check_status();
if (!valid_)
glDeleteTextures(1, &texture_id); easy3d_debug_log_gl_error;
return valid_;
}
GLuint currentFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
// attach texture
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, target,
texture_id, 0); easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
//------------------------------------------------------------------
----------------------------
valid_ = check_status();
if (!valid_)
glDeleteTextures(1, &texture_id); easy3d_debug_log_gl_error;
return valid_;
}
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
return complete;
}
/*!
Returns true if the framebuffer object is valid.
The framebuffer can also become invalid if the OpenGL context that the
framebuffer was created within is destroyed and there are no other
shared contexts that can take over ownership of the framebuffer.
*/
bool FramebufferObject::is_valid() const {
return valid_ && (fbo_id_ != 0);
}
GLint currentFbo = 0;
switch (target)
{
case GL_DRAW_FRAMEBUFFER:
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
case GL_READ_FRAMEBUFFER:
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
case GL_FRAMEBUFFER:
default:
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
}
GLuint currentFbo = 0;
switch (target)
{
case GL_DRAW_FRAMEBUFFER:
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
prev_draw_fbo_ = currentFbo;
}
break;
case GL_READ_FRAMEBUFFER:
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
prev_read_fbo_ = currentFbo;
}
break;
case GL_FRAMEBUFFER:
default:
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (fbo_id_ != currentFbo) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
prev_draw_fbo_ = currentFbo;
prev_read_fbo_ = currentFbo;
}
break;
}
return true;
}
GLuint currentFbo = 0;
switch (target)
{
case GL_DRAW_FRAMEBUFFER:
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
case GL_READ_FRAMEBUFFER:
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
case GL_FRAMEBUFFER:
default:
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
break;
}
return true;
}
////////////////////////////////////////////////////////////////////////
//
index += GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, &index); easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLint maxbuffers;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxbuffers);
delete[] buffers;
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLuint maxbuffers;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, (GLint*)&maxbuffers);
delete[] buffers;
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
void FramebufferObject::deactivate_draw_buffers() {
GLuint currentFbo = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
index += GL_COLOR_ATTACHMENT0;
glReadBuffer((GLenum)index); easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
void FramebufferObject::deactivate_read_buffer() {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////////
//
glReadBuffer(GL_NONE); easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
void FramebufferObject::_prepare_resolve_fbo() {
if (!resolved_fbo_) {
resolved_fbo_ = new FramebufferObject(width(), height(), 0);
easy3d_debug_log_gl_error;
for (std::size_t i = 0; i < color_attachments_.size(); ++i) {
const ColorAttachment& att = color_attachments_[i];
if (att.texture)
resolved_fbo_->add_color_texture(att.internal_format,
att.format, att.type);
else
resolved_fbo_->add_color_buffer(att.internal_format,
att.format, att.type);
}
resolved_fbo_->ensure_size(width(), height());
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLint maxColorAttachments;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
int index = 0;
while (index < maxColorAttachments) {
LOG(INFO) << "color attachment " << index << ":";
_print_attachment(GL_COLOR_ATTACHMENT0 + index);
++index;
}
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLint params;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, ¶ms);
switch (params)
{
case GL_NONE:
LOG(INFO) << "\tthis attachment is empty";
break;
case GL_TEXTURE:
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, ¶ms);
LOG(INFO) << "\tthis attachment is a texture with name: " << params;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, ¶ms);
LOG(INFO) << "\tits mipmap level is: " << params;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, ¶ms);
if (params == 0)
LOG(INFO) << "\tthis is not a cube map texture.";
else
LOG(INFO) << "\tthis is a cube map texture and the image is
contained in face " << params;
#ifdef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET, ¶ms);
if (params == 0)
LOG(INFO) << "\tthis is not 3D texture.";
else
LOG(INFO) << "\tthis is a 3D texture and the z-offset of the
attached image is " << params;
break;
#endif
case GL_RENDERBUFFER:
LOG(INFO) << "\tthis attachment is a render buffer";
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, ¶ms);
LOG(INFO) << "\tthis attachment is a render buffer with name: " <<
params;
glBindRenderbuffer(GL_RENDERBUFFER, params);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
¶ms);
LOG(INFO) << "\trender buffer width = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_HEIGHT, ¶ms);
LOG(INFO) << "\trender buffer height = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_SAMPLES, ¶ms);
LOG(INFO) << "\trender buffer samples = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_INTERNAL_FORMAT, ¶ms);
LOG(INFO) << "\trender buffer internal format = 0x" << std::hex <<
params << std::dec;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_RED_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the red
component = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_GREEN_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the green
component = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_BLUE_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the blue
component = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_ALPHA_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the alpha
component = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_DEPTH_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the depth
component = " << params;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_STENCIL_SIZE, ¶ms);
LOG(INFO) << "\trender buffer actual resolution for the stencil
component = " << params;
break;
default:
LOG(INFO) << "\tunexpected value.";
break;
}
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLint ivalue = 1;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &ivalue);
GLint index = 0;
int c = ivalue;
while (index < c) {
glGetIntegerv(GL_DRAW_BUFFER0 + index, &ivalue);
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
////////////////////////////////////////////////////////////////////////
//
GLint ivalue;
glGetIntegerv(GL_READ_BUFFER, &ivalue);
LOG(INFO) << "read buffer = ";
_print_buffer(ivalue);
////////////////////////////////////////////////////////////////////////
//
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
glFinish();
if (samples_ <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
const_cast<FramebufferObject*>(this)->activate_read_buffer(index);
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
// flip vertically
if (flip_vertically) {
unsigned int row_size = width_ * bytes_per_pixel;
unsigned int half_height = height_ / 2;
#if 0
for (int j = 0; j < half_height; ++j) {
// get a pointer to the two lines we will swap
unsigned char* row1 = buffer + j * row_size;
unsigned char* row2 = buffer + (height_ - 1 - j) * row_size;
// for each point on line, swap all the channels
for (int i = 0; i < width_; ++i) {
for (int k = 0; k < bytes_per_pixel; k++) {
std::swap(row1[bytes_per_pixel*i + k],
row2[bytes_per_pixel*i + k]);
}
}
}
#else
unsigned char *line = new unsigned char[row_size];
for (unsigned int i=0, j=height_-1; i<half_height; ++i) {
memcpy(line, buffer + i * row_size, row_size);
memcpy(buffer + i * row_size, buffer + j * row_size,
row_size);
memcpy(buffer + j * row_size, line, row_size);
j--;
}
delete[] line;
#endif
}
}
else {
const_cast<FramebufferObject*>(this)->_prepare_resolve_fbo();
blit_framebuffer(const_cast<FramebufferObject*>(resolved_fbo_),
this, index, index, GL_COLOR_BUFFER_BIT); easy3d_debug_log_gl_error;
resolved_fbo_->read_color(index, buffer, format, flip_vertically);
}
return true;
}
// I can acutally alway ask for RGBA format and use my ImageIO for
saving.
// But for ppm, bmp and tga formats, this causes extra internal
// formatting (in stb_image_write).
glFinish();
if (samples_ <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
// flip vertically
if (flip_vertically) {
int half_height = height_ / 2;
for (int j = 0; j < half_height; ++j) {
// get a pointer to the two lines we will swap
float* row1 = &(buffer[0]) + j * width_;
float* row2 = &(buffer[0]) + (height_ - 1 - j) * width_;
// swap for each point on line
for (int i = 0; i < width_; ++i) {
std::swap(row1[i], row2[i]);
}
}
}
}
else {
const_cast<FramebufferObject*>(this)->_prepare_resolve_fbo();
blit_framebuffer(const_cast<FramebufferObject*>(resolved_fbo_),
this, GL_DEPTH_BUFFER_BIT); easy3d_debug_log_gl_error;
resolved_fbo_->read_depth(buffer, flip_vertically);
}
return true;
}
glFinish();
if (samples_ <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////
//////
const_cast<FramebufferObject*>(this)->activate_read_buffer(index);
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
else {
const_cast<FramebufferObject*>(this)->_prepare_resolve_fbo();
blit_framebuffer(const_cast<FramebufferObject*>(resolved_fbo_),
this, index, index, GL_COLOR_BUFFER_BIT); easy3d_debug_log_gl_error;
resolved_fbo_->read_color(rgba, x, y, index);
}
return true;
}
glFinish();
if (samples_ <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
}
else {
const_cast<FramebufferObject*>(this)->_prepare_resolve_fbo();
blit_framebuffer(const_cast<FramebufferObject*>(resolved_fbo_),
this, GL_DEPTH_BUFFER_BIT); easy3d_debug_log_gl_error;
resolved_fbo_->read_depth(depth, x, y);
}
return true;
}
// a specified region
void FramebufferObject::blit_framebuffer(
FramebufferObject* target, int tx0, int ty0, int tx1, int ty1,
const FramebufferObject* source, int sx0, int sy0, int sx1, int sy1,
GLbitfield buffers,
GLenum filter /* = GL_NEAREST*/)
{
blit_framebuffer(
target, tx0, ty0, tx1, ty1,
source, sx0, sy0, sx1, sy1,
0, 0, buffers, filter
);
}
// a specified region
void FramebufferObject::blit_framebuffer(
FramebufferObject* target, int tx0, int ty0, int tx1, int ty1,
const FramebufferObject* source, int sx0, int sy0, int sx1, int sy1,
int target_color_attachment_index, int source_color_attachment_index,
GLbitfield buffers,
GLenum filter /* = GL_NEAREST*/)
{
if (!source->is_valid()) {
LOG(ERROR) << "source framebuffer not valid";
return;
}
if (!target->is_valid()) {
LOG(ERROR) << "target framebuffer not valid";
return;
}
// if (!GLEW_EXT_framebuffer_blit) {
// LOG(ERROR) << "FramebufferBlit not supported";
// return;
// }
// check if source and target FBOs both have the required buffer(s)
// color
if (buffers == GL_COLOR_BUFFER_BIT ||
buffers == (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ||
buffers == (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) ||
buffers == (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT)
)
{
if (source_color_attachment_index >= source-
>num_color_attachements()) {
LOG(ERROR) << "source color attachment " <<
source_color_attachment_index << " does not exist";
return;
}
if (target_color_attachment_index >= target-
>num_color_attachements()) {
LOG(ERROR) << "target color attachment " <<
target_color_attachment_index << " does not exist";
return;
}
blit_color = true;
}
// depth
else if (buffers == GL_DEPTH_BUFFER_BIT ||
buffers == (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) ||
buffers == (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) ||
buffers == (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT)
)
{
if (!source->has_depth_attachment()) {
LOG(ERROR) << "the source FBO does not have depth attachment";
return;
}
if (!target->has_depth_attachment()) {
LOG(ERROR) << "the target FBO does not have depth attachment";
return;
}
if (tx1 - tx0 != sx1 - sx0 || ty1 - ty0 != sy1 - sy0) {
LOG(ERROR) << "source and target FBO regions should have the
same size";
return;
}
// The sizes must also be the same if any of the framebuffer objects are
multisample framebuffers.
if (source->samaples() > 0 || target->samaples() > 0) {
if (tx1 - tx0 != sx1 - sx0 || ty1 - ty0 != sy1 - sy0) {
LOG(ERROR) << "source and target FBO regions should have the
same size";
return;
}
}
if (blit_color) {
glReadBuffer(GL_COLOR_ATTACHMENT0 + source_color_attachment_index);
easy3d_debug_log_gl_error;
if (target) {
GLenum drawBuf = GL_COLOR_ATTACHMENT0 +
target_color_attachment_index;
glDrawBuffers(1, &drawBuf); easy3d_debug_log_gl_error;
// or
//glDrawBuffer(GL_COLOR_ATTACHMENT0 +
target_color_attachment_index); easy3d_debug_log_gl_error;
}
}
bool FramebufferObject::copy_color_to_texture(
GLuint& texture_handle,
unsigned int index /* = 0 */,
int internalFormat /* = GL_RGBA8 */,
GLenum format /* = GL_RGBA */,
GLenum type /* = GL_UNSIGNED_BYTE */,
GLenum filter /* = GL_NEAREST*/)
{
if (!has_color_attachment(index)) {
LOG(ERROR) << "color attachment " << index << " does not exist";
return false;
}
if (samaples() <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////
//////
if (!glIsTexture(texture_handle)) {
glGenTextures(1, &texture_handle);
easy3d_debug_log_gl_error;
glBindTexture(texture_target_, texture_handle);
easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, filter);
easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, filter);
easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE); easy3d_debug_log_gl_error;
glTexParameteri(texture_target_, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); easy3d_debug_log_gl_error;
glTexImage2D(texture_target_, 0, internalFormat, width_,
height_, 0, format, type, nullptr); easy3d_debug_log_gl_error;
}
else
glBindTexture(texture_target_, texture_handle);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////
//////
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
return true;
}
else {
_prepare_resolve_fbo();
return resolved_fbo_->copy_color_to_texture(texture_handle, index,
internalFormat, format, type, filter);
}
}
bool FramebufferObject::copy_depth_to_texture(
GLuint& texture_handle,
unsigned int internal_format /* = GL_DEPTH24_STENCIL8 */,
GLenum filter /* = GL_NEAREST */ )
{
if (!has_depth_attachment()) {
LOG(ERROR) << "depth attachment does not exist";
return false;
}
if (samaples() <= 0) {
GLuint currentFbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)¤tFbo);
easy3d_debug_log_gl_error;
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
easy3d_debug_log_gl_error;
//------------------------------------------------------------------
----------------------------
//------------------------------------------------------------------
----------------------------
if (!glIsTexture(texture_handle)) {
glGenTextures(1, &texture_handle);
easy3d_debug_log_gl_error;
glBindTexture(texture_target_, texture_handle);
easy3d_debug_log_gl_error;
////////////////////////////////////////////////////////////////////
//////
if (currentFbo != fbo_id_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFbo);
easy3d_debug_log_gl_error;
return true;
}
else {
_prepare_resolve_fbo();
return resolved_fbo_->copy_depth_to_texture(texture_handle,
internal_format, filter);
}
}
} // namespace easy3d