glReadPixels() 设置 GL_INVALID_OPERATION 错误

Posted

技术标签:

【中文标题】glReadPixels() 设置 GL_INVALID_OPERATION 错误【英文标题】:glReadPixels() sets GL_INVALID_OPERATION error 【发布时间】:2012-11-27 23:51:21 【问题描述】:

我正在尝试使用 FBO 实现颜色选择。我有用于渲染场景的多重采样 FBO (fbo[0]),我有用于颜色选择的非多重采样 FBO (fbo[1])。

问题是:当我尝试从 fbo[1] 读取像素数据时,一切都很顺利,直到 glReadPixels 调用设置了 GL_INVALID_OPERATION 标志。我查了手册,找不到原因。

创建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);

我的检查器保持沉默,因此 FBO 已完成。接下来是拣货代码

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;

输出:

Sample buffers 0
OpenGL Error : Invalid Operation

有趣的事实是,如果我取消注释 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);然后不会发生错误并从屏幕读取像素(但我不需要这个)。

这里可能有什么问题?

【问题讨论】:

另一个有趣的事实。如果我将 RBO 的格式从 GL_R32UI 更改为 GL_R32F 一切正常,除了我想存储和读取整数并且不要丢失 uint32 和浮点数之间的转换精度。 (顺便说一句,片段着色器的输出是 uvec4) 问题出在帧缓冲区的格式上。仅当我尝试从具有整数格式(如 GL_RGBA8I 或 GL_RGBA8UI 或 GL_R32UI)的缓冲区读取时,glReadBuffer() 才会生成错误。从 GL_RGBA8 或 GL_R32F 读取时没有问题。如果其他人遇到问题,这可能是解决问题的方法。 【参考方案1】:

你的问题是 format 参数。对于具有单通道整数内部格式的纹理,正确的参数不是GL_RED,而是GL_RED_INTEGER

glReadPixels(pos.x(), resolution.y() - pos.y(), 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &result);

看看OpenGL documentation wiki(重点是我的):

...

格式

指定像素数据的格式。对于深度、模板或深度/模板数据的传输,您必须在适当的情况下使用 GL_DEPTH_COMPONENT、GL_STENCIL_INDEX 或 GL_DEPTH_STENCIL。对于标准化整数或浮点彩色图像数据的传输,您必须使用以下之一:GL_RED、GL_GREEN、G​​L_BLUE、GL_RG、GL_RGB、GL_BGR、GL_RGBA 和 GL_BGRA。 对于非规范化整数数据的传输,您必须使用以下之一:GL_RED_INTEGER、GL_GREEN_INTEGER、GL_BLUE_INTEGER、GL_RG_INTEGER、GL_RGB_INTEGER、GL_BGR_INTEGER、GL_RGBA_INTEGER 和 GL_BGRA_INTEGER。 即使没有进行实际的像素传输( data​为 NULL 且没有缓冲区绑定到 GL_PIXEL_UNPACK_BUFFER),您必须根据目标图像的内部格式正确设置此参数。

...


注意:official reference page 不完整/错误。

【讨论】:

感谢您的回答!这似乎是合理的,所以我会赞成这个答案。然而,现在,在最初的问题 6 年后,我无法检查它是否属实,所以我不能接受这个作为答案。 没关系,我知道回答这么老的问题很奇怪,但我遇到了同样的问题,而且我在任何地方都找不到解决方案。我希望避免其他人浪费一天时间寻找解决方案。遗憾的是,6 年后官方参考页面仍然不完整。【参考方案2】:

鉴于如果您取消注释该代码行,它是“固定的”,我想知道您的驱动程序是否对您撒谎说 GL_SAMPLE_BUFFERS 为 0。来自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.

【讨论】:

这个 FBO 的 RBO 存储空间是使用 glRenderbufferStorage() 创建的。这个 FBO 被多重采样的原因是什么? 我不知道。考虑到“无效操作”的可能性有限,这只是一种理论。【参考方案3】:

如果您在 Linux 上使用 NVIDIA 的二进制驱动程序并已切换到非图形虚拟控制台(例如 CTRL+ALT+F1) 那么对glReadPixels() 的任何尝试都将返回 GL_INVALID_OPERATION (0x502)。

解决方法:切换回图形控制台(通常是CTRL+ALT+F7)。

【讨论】:

以上是关于glReadPixels() 设置 GL_INVALID_OPERATION 错误的主要内容,如果未能解决你的问题,请参考以下文章

不能 glreadPixels 超过 3844x1065

如何在不同的线程上调用 glReadPixels?

glReadPixels 不更新值

glReadPixels 移动坐标与缩放

为啥 glreadpixels 仅在某些情况下有效?

从 glReadPixels 创建 HBITMAP