OpenGL rendering to texture looks jagged - c++

I am making a program with OpenGL that renders frames in the GPU, which I then transfer to memory so I can use them in another program. I have no need for a window or render to screen, so I am using GLFW but with a hidden window and context. Following opengl-tutorial.com I set up a Framebuffer with a texture and a depth renderbuffer so I can render to the texture and then read it's pixels. Just to check things I can make the window visible and I am rendering the texture back on the screen on a quad and using a passthrough shader.
My problem is that when I render to screen directly (with no Framebuffer or texture) the image looks great and smooth. However, when I render to texture and then render the texture to screen, it looks jagged. I don't think the problem is when rendering the texture to screen, because I am also saving the pixels I read into a .jpg and it looks jagged there too.
Both the window and texture are 512x512 pixels in size.
Here is the code where I set up the framebuffer:
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
//GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);
numBytes = textureWidth * textureHeight * 3; // RGB
pixels = new unsigned char[numBytes]; // allocate image data into RAM
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//GLuint depthrenderbuffer;
glGenRenderbuffers(1, &depthrenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
//GLuint quad_vertexbuffer;
glGenBuffers(1, &quad_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, g_quad_vertex_buffer_data.size() * sizeof(GLfloat), &g_quad_vertex_buffer_data[0], GL_STATIC_DRAW);
// PBOs
glGenBuffers(cantPBOs, pboIds);
for(int i = 0; i < cantPBOs; ++i) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, numBytes, 0, GL_DYNAMIC_READ);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
index = 0;
nextIndex = 0;
Here is the code where I render to the texture:
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,textureWidth,textureHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}
Where draw(ShaderProgram) is the function that calls glDrawArrays. And here is the code where I render the texture to screen:
// Render to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render on the whole framebuffer, complete from the lower left corner to the upper right
glViewport(0,0,textureWidth,textureHeight);
// Clear the screen
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderTexToScreen.getProgramID());
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
// Set our "renderedTexture" sampler to user Texture Unit 0
glUniform1i(shaderTexToScreen.getUniformLocation("renderedTexture"), 0);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
This is what I get when rendering the scene to screen directly:
And this is what I get when rendering the scene to texture:
I can include the code for the vertex and fragment shaders used in rendering the texture to screen, but as I am reading the pixel data straight from the texture and writing it to a file and that still looks jagged, I don't think that is the problem. If there is anything else you want me to include, let me know!
I thought maybe it could be that there is some hidden scaling when doing the rendering to texture and so GL_NEAREST makes it look bad, but if it really is pixel to pixel (both windows and texture are the same size) there shouldn't be a problem there right?

As pointed out by genpfault and Frischer Hering, there is no antialiasing when rendering to a normal texture. However, you can render to a Multisample texture, which will hold information for as many samples as you request. To render this to screen you need to sample the texture to get one color for each pixel, which can be done by calling glBlitFramebuffer. According to the OpenGL reference on glBlitFramebuffer:
If SAMPLE_BUFFERS for the read framebuffer is greater than zero and SAMPLE_BUFFERS for the draw framebuffer is zero, the samples corresponding to each pixel location in the source are converted to a single sample before being written to the destination.
These two links were very helpful too:
http://www.learnopengl.com/#!Advanced-OpenGL/Anti-aliasing
http://ake.in.th/2013/04/02/offscreening-and-multisampling-with-opengl/
Here is my solution, creation of objects:
/// FRAMEBUFFER MULTISAMPLE
framebufferMS = 0;
glGenFramebuffers(1, &framebufferMS);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glGenTextures(1, &renderedTextureMS);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, renderedTextureMS);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, SAMPLES, textureFormat, textureWidth, textureHeight, GL_TRUE);
glGenRenderbuffers(1, &depthrenderbufferMS);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbufferMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, SAMPLES, GL_DEPTH24_STENCIL8, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbufferMS);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTextureMS, 0);
DrawBuffersMS[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffersMS);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
/// FRAMEBUFFER SIMPLE
framebuffer = 0;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
And the rendering process:
// Render to framebuffer multisample
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glViewport(0,0,textureWidth,textureHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}
// Sample to normal texture
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, textureWidth, textureHeight, 0, 0, textureWidth, textureHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
You can also find more questions on the subject here on stackoverflow which I missed because I searches for terms like "jagged" instead of multisampling :P
Thanks a lot for your help!

Related

Get depth stored in different FBOs that has different shaders

The goal of my program is to compare between the depth of the RGB-D camera and OpenGL depth.
The camera is facing a model (real one) and it has its own shaders (fragment and vertex).
To make comparison, a 3D Model is made, this one has also its own shaders (the depth is normally linearized as I followed what is explained in this tutorial: https://learnopengl.com/Advanced-OpenGL/Depth-testing).
So the comparison will be done on the depth received from the camera and the depth related to the 3D model.
So,the steps I have done are:
1.In the initialization function
Create two textures for each one (color and depth textures).
Generate two FBOs (one for the camera, the other one for the 3D Model).
Attach textures to their FBOs.
/* Other stuff needed for initialization */
...
// Generate color texture
glGenTextures(1, &colorTex);
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, _windowWidth, _windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Generate depth texture
glGenTextures(1, &depthTex);
glBindTexture(GL_TEXTURE_2D, depthTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _windowWidth, _windowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Attach the color and depth textures to current FrameBuffer
glGenFramebuffers(1, &_fboGL);
glBindFramebuffer(GL_FRAMEBUFFER, _fboGL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
In the main function for Drawing and rendering
Render the camera stream by binding its FBO.
Draw 3D Model by binding its own FBO.
Try to print openGL depth related to the 3D Model.
A fragment of the code looks like below:
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, camColorTex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, camDepthTex, 0);
glViewport(0, 0, windowWidth, windowHeight);
glEnable(GL_DEPTH_TEST);
glClearColor(0.25f, 0.25f, 0.25f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*******************************************/
/* Some code for displaying camera stream */
/*******************************************/
.............
drawModelFunction();
printDepthFunction();
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, camColorTex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, camDepthTex, 0);
/***********************************************************/
/* Some stuff to render a GUI to interact with the program */
/***********************************************************/
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, _windowWidth, _windowHeight, 0, 0, _windowWidth, _windowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBlitFramebuffer(0, 0, _windowWidth, _windowHeight, 0, 0, _windowWidth, _windowHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
A fragment code for drawModelFunction()
glBindFramebuffer(GL_FRAMEBUFFER, fboGL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
/*************************************/
/* Some stuff to render the 3D Model */
/*************************************/
A fragment code for printDepthFunction()
GLfloat *pixels = (GLfloat *)malloc(_windowWidth * _windowHeight * sizeof *pixels);
assert(pixels);
glBindTexture(GL_TEXTURE_2D, depthTex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);
/* print the the values stored in **pixels** */
free(pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
When I try to display the values stored in the depth, I only obtain one value "1.0f" and the color returns me only the value "0.0f". In addition to that, the rendering is not here as I mixed everything with FBOs. I have a screen with mixture of camera stream, a red square inserted in the middle of the screen with the GUI in it (that took the red color of the square). And No 3D Model. (This is the result I have obtained after binding the framebuffers everywhere like shown before).

OpenGL Framebuffer Missing Faces

I'm having an issue using OpenGL to implement multi-pass shaders to enable HDR.
The first pass renders the scene to a framebuffer.
The second pass uses the framebuffer with color and depth to render to a quad.
(I'm following this tutorial.)
The problem is it does not render certain (front, top and one side) cube faces.
If I render without the framebuffer (without changing any of the other render code), it works as it should.
I have tried to change the winding of the faces using GL_CCW and GL_CW and changing the glDepthFunc to no avail.
This is the code the renderer is initialized with:
Renderer::Renderer(Window window): window(window) {
this->materials = std::map<std::string, Material>();
this->meshes = std::map<std::string, Mesh>();
// glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_FRAMEBUFFER_SRGB);
glViewport(0, 0, window.width, window.height);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
this->load_textures();
this->load_materials();
this->load_meshes();
this->load_shader_programs();
this->create_hdr(this->shader_programs.find("hdr")->second);
}
This generates the framebuffer before the first render:
void Renderer::create_hdr(ShaderProgram sp_hdr) {
glGenFramebuffers(1, &this->hdr_fbo);
glGenTextures(1, &this->color_buffer);
glBindTexture(GL_TEXTURE_2D, this->color_buffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, this->window.width, this->window.height, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenRenderbuffers(1, &this->render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, this->render_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, this->window.width, this->window.height);
glBindFramebuffer(GL_FRAMEBUFFER, this->hdr_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->color_buffer, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->depth_buffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Framebuffer not complete!" << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(sp_hdr.id);
glUniform1i(glGetUniformLocation(sp_hdr.id, "hdr_buffer"), 0);
}
This renders the level in two passes:
void Renderer::render(Level level, Camera camera, std::vector<std::reference_wrapper<DirectionalLight>> d_lights, std::vector<PointLight> p_lights, std::vector<SpotLight> s_lights) {
// 1. First Pass - HDR
glBindFramebuffer(GL_FRAMEBUFFER, this->hdr_fbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ShaderProgram sp_render = this->shader_programs.find("render")->second;
glUseProgram(sp_render.id);
attach_camera(camera, sp_render);
attach_projection_matrix(camera, sp_render);
attach_view_matrix(camera, sp_render);
unsigned int d_light_num = 0;
for (DirectionalLight d_light : d_lights) {
attach_d_light(d_light, sp_render, d_light_num);
d_light_num++;
}
for (Block block : level.blocks) {
attach_position(block.position, sp_render);
Material material = this->materials.find(block.material_id)->second;
attach_material(material, sp_render);
Mesh mesh = this->meshes.find(block.mesh_id)->second;
draw_mesh(mesh, sp_render);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 2. Second Pass - Render to quad
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ShaderProgram sp_hdr = this->shader_programs.find("hdr")->second;
glUseProgram(sp_hdr.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->color_buffer);
draw_mesh(this->meshes.find("quad")->second, sp_hdr);
}
I fixed this by actually connecting the correct depth render buffer:
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->depth_buffer);
to
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->render_buffer);

OpenGL MSAA in 2 different ways. What are the differences?

I have been looking for OpenGL Multi Sample Anti Aliasing tutorials and I found many but I'll take 2.
They use a different way to do this. I have tested both ways and both work for my project so I can use any of them.
I use this to render my game engine scene to a texture.
This is the 1st way:
Create the FBO with MSAA
// create a texture object
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create a MSAA framebuffer object
// NOTE: All attachment images must have the same # of samples.
// Ohterwise, the framebuffer status will not be completed.
glGenFramebuffers(1, &fboMsaaId);
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);
// create a MSAA renderbuffer object to store color info
glGenRenderbuffers(1, &rboColorId);
glBindRenderbuffer(GL_RENDERBUFFER, rboColorId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, MSAA_level, GL_RGB8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a MSAA renderbuffer object to store depth info
// NOTE: A depth renderable image should be attached the FBO for depth test.
// If we don't attach a depth renderable image to the FBO, then
// the rendering output will be corrupted because of missing depth test.
// If you also need stencil test for your rendering, then you must
// attach additional image to the stencil attachement point, too.
glGenRenderbuffers(1, &rboDepthId);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, MSAA_level, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach msaa RBOs to FBO attachment points
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorId);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthId);
// create a normal (no MSAA) FBO to hold a render-to-texture
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach a texture to FBO color attachement point
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
// attach a rbo to FBO depth attachement point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
//## disable color buffer if you don't attach any color buffer image,
//## for example, rendering the depth buffer only to a texture.
//## Otherwise, glCheckFramebufferStatus will not be complete.
//glDrawBuffer(GL_NONE);
//glReadBuffer(GL_NONE);
// check FBO status
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return false;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And when I need to draw the scene
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);
glViewport(0, 0, width, height);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawScene();
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaaId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glBlitFramebuffer(0, 0, width, height, // src rect
0, 0, width, height, // dst rect
GL_COLOR_BUFFER_BIT, // buffer mask
GL_LINEAR); // scale filter
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, App->window->GetWidth(), App->window->GetHeight());
The 2nd way:
Create the FBO with MSAA
unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// create a multisampled color attachment texture
unsigned int textureColorBufferMultiSampled;
glGenTextures(1, &textureColorBufferMultiSampled);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, SCR_WIDTH, SCR_HEIGHT, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
// create a (also multisampled) renderbuffer object for depth and stencil attachments
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// configure second post-processing framebuffer
unsigned int intermediateFBO;
glGenFramebuffers(1, &intermediateFBO);
glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
// create a color attachment texture
unsigned int screenTexture;
glGenTextures(1, &screenTexture);
glBindTexture(GL_TEXTURE_2D, screenTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); // we only need a color buffer
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Intermediate framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And when I need to draw the scene:
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glViewport(0, 0, width, height);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawScene();
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);
glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, App->window->GetWidth(), App->window->GetHeight());
Summarizing:
1st way: Create FBO, create RBO for color and RBO for depth and use glRenderbufferStorageMultisample(...) to specify the MSAA level. Then create other FBO with texture for color and RBO for depth.
2nd way: Create FBO, create Texture for color and RBO for depth and using glTexImage2DMultisample(...) for MSAA level in the texture. Then create other FBO and a texture.
What are the differences on using one way or other? Is one better than the other?
MSAA setup in your example is actually the same in both cases. The only difference between the two methods you depicted is - different FBO attachment type. In general, you will want to attach a texture and not render buffer when you later need to use the information from that FBO for further render passes. In such a case you would plug previous render pass FBO's texture attachment into texture unit, and sample from it in the next pass shader program. Shadow mapping is one of such cases.

Use FBO's position buffer and color buffer for rendering (post-rendering 3D warp)

I want to implement post-rendering 3D warp using position data and color data stored in a FBO. How to do it efficiently in modern OpenGL? By position data, they are camera-relative XYZ coordinates.
I have two FBOs, say fbo1 and fbo2. fbo1 is for data (position+color) input, and fbo2 is for data output (the results after post-rendering 3D warp). FBO setup code for both FBOs is as following:
void setupFBO( int width, int height )
{
// Create and bind the FBO
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
// The depth buffer
glGenRenderbuffers(1, &fboDepthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, fboDepthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
// The color buffer
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &fboColorTex);
glBindTexture(GL_TEXTURE_2D, fboColorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// The position buffer
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &fboPosTex);
glBindTexture(GL_TEXTURE_2D, fboPosTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Attach the images to the framebuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepthBuf);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboColorTex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fboPosTex, 0);
// Set the targets for the fragment output variables
GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, drawBuffers);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
Assume we've data stored in fbo1, i.e. color data in fbo1.fboColorTex and position data in fbo1.fboPosTex, and we know the transformation (modelview) matrix needed for transforming the position data and also the projection matrix. How to obtain the transformed position and color data? One way to implement it could be like the following:
glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fbo1.fboColorTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fbo1.fboPosTex);
// set shader parameters for textures
// set modelview, projection matrices
// what to render here? A full-screen quad?
glBindFramebuffer(GL_FRAMEBUFFER, 0);
What do render? And what shaders should look like?
The other way I can think of is to use dynamic VBOs, but I'm not sure how to dynamically assign VBO's vertex and color data from textures (in fbo1)... The reason that I mention dynamic VBO is because fbo1's data can be changed but not frequently.

unable to read depth values from depth texture attached to FBO

I am unable to read correct depth values from depth texture using glreadpixels function. FBO status is complete. other render targets also look fine after blitting to another FBO.
code snippet:
// Create the FBO
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// Create the gbuffer textures
glGenTextures(GBUFFER_NUM_TEXTURES, m_textures);
glGenTextures(1, &m_depthTexture);
for (unsigned int i = 0 ; i < GBUFFER_NUM_TEXTURES ; i++) {
glBindTexture(GL_TEXTURE_2D, m_textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, fboWidth, fboHeight, 0, GL_RGBA, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_textures[i], 0);
}
// depth
glBindTexture(GL_TEXTURE_2D, m_depthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
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_COMPARE_MODE, GL_NONE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, fboWidth, fboHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);
GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(GBUFFER_NUM_TEXTURES, DrawBuffers);
GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (Status != GL_FRAMEBUFFER_COMPLETE) {
printf("FB error, status: 0x%x\n", Status);
return 0;
}
// drawing something with depth test enabled.
// Now i am using glreadpixels functions to read depth values from depth texture.
int w = 4, h = 1;
GLfloat windowDepth[4];
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glReadPixels(x, y, w, h, GL_DEPTH_COMPONENT, GL_FLOAT, windowDepth);
You are drawing to a depth texture. The appropriate function to call to read a texture into client memory is glGetTexImage (...).
Now, since there is no glGetTexSubImage (...), you need to allocate enough client storage to hold an entire LOD of the depth texture. Something like this will probably do the trick:
GLuint w = fboWidth, h = fboHeight;
GLfloat windowDepth [w * h];
glBindTexture (GL_TEXTURE_2D, m_depthTexture);
glGetTexImage (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, windowDepth);
Keep in mind that unlike glReadPixels (...), glGetTexImage (...) does not perform pixel transfer conversion. That is, your format and data type must be an exact match with the types used when the texture was created, the GL will not convert your data.
With that out of the way, can I ask why you are reading the depth buffer into client memory in the first place? You appear to be using deferred shading, so I can see why you need a depth texture, but it is less clear why you need a copy of the depth buffer outside of shaders. You will have a hard time achieving interactive frame rates if you copy the depth buffer each frame.