FBO Blitting is not working - opengl

I'm trying to render a multisampled scene to texture, here is the code I'm using. I'm getting a black screen. I check the fbo completeness at the end of init, and they report that both fbo's are complete.
void init_rendered_FBO() {
glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenTextures(1,&fbo_tex);
glBindTexture(GL_TEXTURE_2D, fbo_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
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_RGBA8, width_screen, height_screen, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width_screen, height_screen);
glBindTexture (GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex, 0);
int objectType;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,&objectType);
ASSERT(objectType == GL_TEXTURE);
int objectName;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,&objectName);
ASSERT(glIsTexture(objectName) == GL_TRUE);
int wid, hei, fmt;
glBindTexture(GL_TEXTURE_2D, objectName);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &wid);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &hei);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT,
&fmt);
glBindTexture(GL_TEXTURE_2D, 0);
std::string format = convertInternalFormatToString(fmt);
std::cout << "Color attachment 0: " << objectName << " " << wid << "x" << hei << ", " << format << std::endl;
ASSERT(checkFramebufferStatus());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// this is the init function that gets called.
void init_rendered_multisample_FBO() {
init_rendered_FBO();
// now I'm going to set up the additional component necessary to perform multisampling which is a new fbo
// that has a multisampled color buffer attached. I won't need a multisample depth buffer.
glGenFramebuffers(1,&multi_fbo);
glBindFramebuffer(GL_FRAMEBUFFER,multi_fbo);
glGenRenderbuffers(1,&renderbuffer_multi);
glBindRenderbuffer(GL_RENDERBUFFER,renderbuffer_multi);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA, width_screen,height_screen);
glBindRenderbuffer(GL_RENDERBUFFER,0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer_multi);
int objectType;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,&objectType);
ASSERT(objectType == GL_RENDERBUFFER);
int objectName;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,&objectName);
ASSERT(glIsRenderbuffer(objectName) == GL_TRUE);
glBindRenderbuffer(GL_RENDERBUFFER,objectName);
int wid,hei,fmt,sam;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_WIDTH,&wid);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_HEIGHT,&hei);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_INTERNAL_FORMAT,&fmt);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_SAMPLES,&sam);
glBindRenderbuffer(GL_RENDERBUFFER,0);
printf("Renderbuffer: %dx%d, fmt=%d, samples=%d\n",wid,hei,fmt,sam);
ASSERT(checkFramebufferStatus());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// this is called after rendering to multi_fbo
void resolve_multisample_FBO() {
glBindFramebuffer(GL_READ_FRAMEBUFFER, multi_fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
GLenum drawbuf = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1,&drawbuf);
glBlitFramebuffer(0,0,width_screen,height_screen,0,0,width_screen,height_screen,GL_COLOR_BUFFER_BIT,GL_NEAREST);
}
Can you spot anything I left out? I think the issue may be with the glFramebufferRenderbuffer call. I tried switching the first arg to GL_READ_FRAMEBUFFER but it did not fix.
I check glGetError and there are no errors. If I setup something wrong, surely something will fail and give me INVALID_ENUM or INVALID_OPERATION to help me narrow down the issue.
The way I use this code is that in order to enable multisampling all I have to change is to bind multi_fbo when drawing, and then call the resolve function which will bind and blit. After that, my fbo texture (which works fine when I render directly to it) should now contain the multisampled render. But it's just black. I also did call glEnable(GL_MULTISAMPLE) in another part of initialization code.
I'm now going to attempt to blit a non multisampled texture to another texture to make sure that works. Hopefully that will help me narrow down where I screwed up.
Update: So it turns out copying a regular FBO to another identical FBO (both have textures attached to color attachment 0) produces the same black screen. The blit simply doesn't work.
What is also strange is that once I attempt to blit, then draw the texture of the SOURCE fbo, it's still all black. It's like attempting to blit just ruins everything.
Does anybody know of or have any FBO blitting code? I can't find any but I know people have gotten multisampled FBO's working.
Here's how you can help: If you have code which calls glBlitFramebuffer at any point, I would like to see the other calls for setting that operation up. I can't seem to get anything but black buffers and textures once I invoke it.
Update: Blit to backbuffer works, even with multisampling! This of course requires absolutely no setup since I just bind the framebuffer 0. So the problem seems to be my setting up of the FBO which has a texture attachment which is supposed to be blitted to.

Well turns out I didn't do anything wrong other than fail to reset the framebuffer binding.
At the end of void resolve_multisample_FBO() I simply needed a glBindFramebuffer(GL_FRAMEBUFFER, 0);
A terrible way to waste 3 hours, I assure you. But multisampling looks great and more than makes up for it.

Related

How to sample a pixel from a framebuffer texture in OpenGL?

I am creating a color picker OpenGL application for images with ImGUI. I have managed to load an image by loading the image into a glTexImage2D and using ImGUI::Image().
Now I would like to implement a method, which can determine the color of the pixel in case of a left mouse click.
Here is the method I loading the texture, then assigning it to a framebuffer:
bool LoadTextureFromFile(const char *filename, GLuint *out_texture, int *out_width, int *out_height,ImVec2 mousePosition ) {
// Reading the image into a GL_TEXTURE_2D
int image_width = 0;
int image_height = 0;
unsigned char *image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
if (image_data == NULL)
return false;
GLuint image_texture;
glGenTextures(1, &image_texture);
glBindTexture(GL_TEXTURE_2D, image_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(image_data);
glBindTexture(GL_TEXTURE_2D, 0);
*out_texture = image_texture;
*out_width = image_width;
*out_height = image_height;
// Assigning texture to Frame Buffer
unsigned int fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, image_texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, image_texture, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE){
std::cout<< "Frame buffer is done."<< std::endl;
}
return true;
}
Unfortunately, the above code results in a completely blank screen. I guess, there is something I missed during setting the framebuffer.
Here is the method, where I would like to sample the framebuffer texture by using the mouse coordinates:
void readPixelFromImage(ImVec2 mousePosition) {
unsigned char pixels[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(GLint(mousePosition.x), GLint(mousePosition.y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
std::cout << "r: " << static_cast<int>(pixels[0]) << '\n';
std::cout << "g: " << static_cast<int>(pixels[1]) << '\n';
std::cout << "b: " << static_cast<int>(pixels[2]) << '\n';
std::cout << "a: " << static_cast<int>(pixels[3]) << '\n' << std::endl;
}
Any help is appreciated!
There is indeed something missing in your code:
You set up a new Framebuffer that contains just a single texture buffer. This is okay, so the glCheckFramebufferStatus equals GL_FRAMEBUFFER_COMPLETE. But there is no render buffer attached to your framebuffer. If you want your image rendered on screen, you should use the default framebuffer. This framebuffer is created from your GL context.
However, the documentation says: Default framebuffers cannot change their buffer attachments, [...] https://www.khronos.org/opengl/wiki/Framebuffer. So attaching a texture or renderbuffer to the default FB is certainly not possible. You could, however, generate a new FB as you did, render to it, and finally render the outcome (or blit the buffers) to your default FB. Maybe a good starting point for this technique is https://learnopengl.com/Advanced-Lighting/Deferred-Shading
Moreover, if you intend to just read back rendered values from your GPU, it is more performant to use a renderbuffer instead of a texture. You can even have multiple renderbuffers attached to your framebuffer (as in deferred shading). Example: you could use a second renderbuffer to render an object/instance id (so, the renderbuffer will be single channel integer), and your first renderbuffer will be used for normal drawing. Reading the second renderbuffer with glReadPixels you can directly read which instance was drawn at e.g. the mouse position. This way, you can enable mouse picking very efficiently.

GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT from the shared second context

I need to share the color/depth/stencil buffers between two OpenGL contexts. 2D textures are attached to framebuffer object for the buffers. The code looks like below:
// === Start of context 1
//
Create _hDC1, _hRC1;
wglMakeCurrent(hDC1, hRC1);
glGenFramebuffers(1, &_FBO1);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO1);
// color buffer
glGenTextures(1, &_color1);
glBindTexture(GL_TEXTURE2D, _color1);
glTexParameteri(GL_TEXTURE_2D, ..., ); // GL_TEXTURE_WRAP_S, ..., etc.
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F, 512, 512);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _color1, 0);
// depth buffer
glGenTextures(1, &_depth1);
glBindTexture(GL_TEXTURE2D, _depth1);
glTexParameteri(GL_TEXTURE_2D, ..., ); // GL_TEXTURE_WRAP_S, ..., etc.
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, 512, 512);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depth1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, _depth1, 0);
// check completeness
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
assert(status == GL_FRAMEBUFFER_COMPLETE); // *pass fine here*
// detach _FBO1
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glReadBuffer(GL_NONE);
glDrawBuffer(GL_NONE);
//
// === End of context 1
// === Start of context 2
//
Create _hDC2, _hRC2; // pixel format is same as _hDC1
wglShareLists(_hRC2, hRC1); // share objects between _hRC1 and _hRC2
wglMakeCurrent(_hDC2, _hRC2);
// Create another frame buffer, since it is not sharable
glGenFramebuffers(1, &_FBO2);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO2);
// Use the shared textures for color/depth/stencil buffers
glBindTexture(GL_TEXTURE2D, _color1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _color1, 0);
glBindTexture(GL_TEXTURE2D, _depth1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depth1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, _depth1, 0);
// check completeness
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
assert(status == GL_FRAMEBUFFER_COMPLETE); // *fail here, status is GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT*
//
// === End of context 2
The second completeness check always fails.
Tried move the second completeness check right after attaching the color buffer, and I got the same failure.
Also tried to unbind _color and _depth before creating the second context, but, it did not help.
glGetError() was put after each OpenGL call and no error was detected.
From https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml
Not all framebuffer attachment points are framebuffer attachment complete. This means that at least one attachment point with a renderbuffer or texture attached has its attached object no longer in existence or has an attached image with a width or height of zero, or the color attachment point has a non-color-renderable image attached, or the depth attachment point has a non-depth-renderable image attached, or the stencil attachment point has a non-stencil-renderable image attached.
Can not figure out what's get missed. Checked several posts online, but none of them is the same use case as mine.
Aha, I messed up with the arguments order when calling wglShareLists(), which should be:
wglShareLists(_hRC1, hRC2);
Everything works now. I should have put an error check around it.

OpenGL how can I attach a depth buffer to a framebuffer using a multisampled 2d texture

How can I attach a depth-buffer to my framebufferobject when I use GL_TEXTURE_2D_MULTISAMPLE. glCheckFramebufferStatus(msaa_fbo) from the code below returns 0. From the documentation this seems to mean that msaa_fba is not a framebuffer, but it is created from glGenFramebuffers(1, &msaa_fbo);.
Additionally, if an error occurs, zero is returned.
GL_INVALID_ENUM is generated if target is not GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER or GL_FRAMEBUFFER.
The error is 1280, which I think means GL_INVALID_ENUM.
If i remove the depth buffer attachment the program runs and renders (although without depth testing). The error is still present when it runs then. With the depth attachment included there is an error (1286) after every frame, which is INVALID_FRAMEBUFFER. I don't know how to continue from here. Some examples I've looked at do somewhat the same but seem to work.
glGenTextures(1, &render_target_texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, render_target_texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, NUM_SAMPLES, GL_RGBA8, width, height, false);
glGenFramebuffers(1, &msaa_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, render_target_texture, 0);
glGenRenderbuffers(1, &depth_render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, depth_render_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, NUM_SAMPLES, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_render_buffer);
GLenum status = glCheckFramebufferStatus(msaa_fbo);
Most of the code is from this.
EDIT
The status check was wrong, it should've been GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);. Now there is no error when I don't include the depth. When I include depth I get this error now: GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE.
EDIT 2
Documentation claims that this happens when GL_TEXTURE_SAMPLES and GL_RENDERBUFFER:SAMPLES don't match.
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.
But they do!
I've tested them like this:
std::cout << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" << std::endl;
GLsizei gts, grs;
glGetTexLevelParameteriv(GL_TEXTURE_2D_MULTISAMPLE, 0, GL_TEXTURE_SAMPLES, &gts);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &grs);
std::cout << "GL_TEXTURE_SAMPLES: " << gts << std::endl;
std::cout << "GL_RENDERBUFFER_SAMPLES: " << grs << std::endl;
Output is:
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
GL_TEXTURE_SAMPLES: 8
GL_RENDERBUFFER_SAMPLES: 8
EDIT 3
Worked around this by using two textures instead of a texture and a renderbuffer like this:
glGenFramebuffers(1, &msaa_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa_fbo);
glGenTextures(1, &render_texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, render_texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, NUM_SAMPLES, GL_RGBA8, width, height, false);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, render_texture, 0);
glGenTextures(1, &depth_texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, depth_texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, NUM_SAMPLES, GL_DEPTH_COMPONENT, width, height, false);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depth_texture, 0);
I'm am still interested in what was wrong with the original implementation, so question is still standing.
You need to used fixed sample locations for the texture if you mix it with renderbuffers. From the spec, in section "Framebuffer Completeness":
The value of TEXTURE_FIXED_SAMPLE_LOCATIONS is the same for all attached textures; and, if the attached images are a mix of renderbuffers and textures, the value of TEXTURE_FIXED_SAMPLE_LOCATIONS must be TRUE for all attached textures.
{FRAMEBUFFER_INCOMPLETE_MULTISAMPLE}
To avoid this error condition, you the call for setting up the texture storage needs to be changed to:
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
NUM_SAMPLES, GL_RGBA8, width, height, GL_TRUE);

Can I use OpenGL for off-screen rendering? [duplicate]

This question already has answers here:
How to use GLUT/OpenGL to render to a file?
(6 answers)
Closed 9 years ago.
I want to try to make a simple program that takes a 3D model and renders it into an image. Is there any way I can use OpenGL to render an image and put it into a variable that holds an image rather than displaying an image? I don't want to see what I'm rendering I just want to save it. Is there any way to do this with OpenGL?
I'm assuming that you know how to draw stuff to the screen with OpenGL, and you wrote a function such as drawStuff to do so.
First of all you have to decide how big you want your final render to be; I'm choosing a square here, with size 512x512. You can also use sizes that are not power of two, but to keep things simple let's stick to this format for now. Sometimes OpenGL gets picky about this issue.
const int width = 512;
const int height = 512;
Then you need three objects in order to create an offscreen drawing area; this is called a frame buffer object as user1118321 said.
GLuint color;
GLuint depth;
GLuint fbo;
The FBO stores a color buffer and a depth buffer; also you screen rendering area has these two buffers, but you don't want to use them because you don't want to draw to the screen. To create the FBO, you need to do something like the following only one time for instance at startup:
glGenTextures(1, &color);
glBindTexture(GL_TEXTURE_2D, color);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &depth);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
First you create a memory area to store pixel color, than one to store pixel depth (which in computer graphics is used to remove hidden surfaces), and finally you connect them to the FBO, which basically holds a reference to both. Consider as an example the first block, with 6 calls:
glGenTextures creates a name for a texture; a name in OpenGL is simply an integer, because a string would be too inefficient.
glBindTexture binds the texture to a target, namely GL_TEXTURE_2D; subsequent calls that specify that same target will operate on that texture.
The 3rd, 4th and 5th call are specific to the target being manipulated, and you should refer to the OpenGL documentation for further information.
The last call to glBindTexture unbinds the texture from the target. Since at some point you will hand control to your drawStuff function, which in turn will make its whole lot of OpenGL calls, you need to clear you workspace now, to avoid interference with the object that you have created.
To switch from screen rendering to offscreen rendering you could use a boolean variable somewhere in your program:
if (offscreen)
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
else
glBindFramebuffer(GL_FRAMEBUFFER, 0);
drawStuff();
if (offscreen)
saveToFile();
So, if offscreen is true you actually want drawStuff to interfere with fbo, because you want it to render the scene on it.
Function saveToFile is responsible for loading the result of the rendering and converting it to file. This is heavily dependent on the OS and language that you are using. As an example, on Mac OS X with C it would be something like the following:
void saveImage()
{
void *imageData = malloc(width * height * 4);
glBindTexture(GL_TEXTURE_2D, color);
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, imageData);
CGContextRef contextRef = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedLast);
CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
CFURLRef urlRef = (CFURLRef)[NSURL fileURLWithPath:#"/Users/JohnDoe/Documents/Output.png"];
CGImageDestinationRef destRef = CGImageDestinationCreateWithURL(urlRef, kUTTypePNG, 1, NULL);
CGImageDestinationAddImage(destRef, imageRef, nil);
CFRelease(destRef);
glBindTexture(GL_TEXTURE_2D, 0);
free(imageData);
}
Yes, you can do that. What you want to do is create a frame buffer object (FBO) backed by a texture. Once you create one and draw to it, you can download the texture to main memory and save it just like you would any bitmap.

glReadPixels() sets GL_INVALID_OPERATION error

I'm trying to implement color picking with FBO. I have multisampled FBO (fbo[0]) which I use to render the scene and I have non multisampled FBO (fbo[1]) which I use for color picking.
The problem is: when I try to read pixel data from fbo[1] everything goes well until glReadPixels call which sets GL_INVALID_OPERATION flag. I've checked the manual and can't find the reason why.
The code to create FBO:
glBindRenderbuffer(GL_RENDERBUFFER, rbo[0]);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_RGBA8, resolution[0], resolution[1]);
glBindRenderbuffer(GL_RENDERBUFFER, rbo[1]);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL_DEPTH24_STENCIL8, resolution[0], resolution[1]);
glBindRenderbuffer(GL_RENDERBUFFER, rbo[2]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, resolution[0], resolution[1]);
glBindRenderbuffer(GL_RENDERBUFFER, rbo[3]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, resolution[0], resolution[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo[3]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[2]);
OGLChecker::checkFBO(GL_DRAW_FRAMEBUFFER);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[0]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo[1]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[0]);
OGLChecker::checkFBO(GL_DRAW_FRAMEBUFFER);
My checker stays silent so the FBOs are complete. Next the picking code
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// bla, bla, bla
// do the rendering
unsigned int result;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
int sb;
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glGetIntegerv(GL_SAMPLE_BUFFERS, &sb);
// glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
OGLChecker::getGlError();
std::cerr << "Sample buffers " << sb << std::endl;
glReadPixels(pos.x(), resolution.y() - pos.y(), 1, 1, GL_RED, GL_UNSIGNED_INT, &result);
OGLChecker::getGlError();
return result;
the output:
Sample buffers 0
OpenGL Error : Invalid Operation
The interesting fact that if I uncomment glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); then no error happens and pixels are read from screen (but I don't need this).
What may be wrong here?
Your problem is the format parameter. For a texture that has a one-channel integer internal format the correct parameter isn't GL_RED, but GL_RED_INTEGER:
glReadPixels(pos.x(), resolution.y() - pos.y(), 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &result);
Look at the OpenGL documentation wiki (emphasis mine):
...
format
Specifies the format of the pixel data. For transfers of depth, stencil, or depth/stencil data, you must use GL_DEPTH_COMPONENT, GL_STENCIL_INDEX, or GL_DEPTH_STENCIL, where appropriate. For transfers of normalized integer or floating-point color image data, you must use one of the following: GL_RED, GL_GREEN, GL_BLUE, GL_RG, GL_RGB, GL_BGR, GL_RGBA, and GL_BGRA. For transfers of non-normalized integer data, you must use one of the following: GL_RED_INTEGER, GL_GREEN_INTEGER, GL_BLUE_INTEGER, GL_RG_INTEGER, GL_RGB_INTEGER, GL_BGR_INTEGER, GL_RGBA_INTEGER, and GL_BGRA_INTEGER. Even if no actual pixel transfer is made (data​ is NULL and no buffer is bound to GL_PIXEL_UNPACK_BUFFER), you must set this parameter correctly for the internal format of the destination image.
...
Note: the official reference page is incomplete/wrong.
Given that it's "fixed" if you uncomment that line of code, I wonder if your driver is lying to you about GL_SAMPLE_BUFFERS being 0. From http://www.opengl.org/sdk/docs/man/xhtml/glReadPixels.xml:
GL_INVALID_OPERATION is generated if GL_READ_FRAMEBUFFER_BINDING is non-zero, the read framebuffer is complete, and the value of GL_SAMPLE_BUFFERS for the read framebuffer is greater than zero.
If you're using NVIDIA's binary driver on Linux and have switched to a non-graphical virtual console (e.g. CTRL+ALT+F1) then any attempt to glReadPixels() will return GL_INVALID_OPERATION (0x502).
Solution: Switch back to the graphical console (usually CTRL+ALT+F7).