8

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?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Oleg Titov
  • 1,100
  • 1
  • 8
  • 13
  • Another interesting fact. If I change RBO's format from GL_R32UI to GL_R32F everything works fine, except I want to store and read integers and don't loose precision on convertion between uint32 and float. (BTW, the output from fragment shader is uvec4) – Oleg Titov Nov 28 '12 at 00:29
  • 1
    Well the problem was with format of framebuffer. glReadBuffer() generates error only when I try to read from buffer with integer formats (like GL_RGBA8I or GL_RGBA8UI or GL_R32UI). No problems when reading from GL_RGBA8 or GL_R32F. That may be the fix for the problem if anyone else encounters it. – Oleg Titov Nov 28 '12 at 22:46

3 Answers3

5

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.

Tarquiscani
  • 649
  • 7
  • 20
  • Thank you for your answer! This seems plausible so I'll upvote this answer. However, now, 6 years after the initial question, I'm not able to check if it is true, so I cannot accept this as an answer. – Oleg Titov Mar 13 '19 at 16:20
  • Never mind, I know it's odd to answer such an old question, but I had the same identical problem and I couldn't find the solution anywhere. I hope to avoid other people to waste one day looking for the solution. Sadly after 6 years the official reference page is still incomplete. – Tarquiscani Mar 13 '19 at 17:46
1

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.
Jim Buck
  • 20,482
  • 11
  • 57
  • 74
  • Well RBOs storage spaces for this FBO were created with glRenderbufferStorage(). Whats the reason for this FBO to be multisampled? – Oleg Titov Nov 28 '12 at 00:04
  • I've no idea. It was just a theory given the limited possibilities for "invalid operation". – Jim Buck Nov 28 '12 at 08:52
1

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).

Nathan Kidd
  • 2,919
  • 21
  • 22