DEPTH_COMPONENT 上的 glReadPixels 抛出 GL_INVALID_OPERATION

glReadPixels on DEPTH_COMPONENT throws GL_INVALID_OPERATION

我在读取离屏帧缓冲区渲染通道的深度缓冲区时遇到问题。使用 OpenGL 4.5 时,它按预期工作,但在 OpenGL ES 2.0(角度)上,我的 glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depthBuffer) 调用出现错误。错误是 0x502,这是一个 GL_INVALID_OPERATION 错误代码。

更多背景。我在 Qt 环境中工作,它有一个主要的渲染例程,现在我正在做一些离屏渲染。通常我们使用 opengl 桌面实现,但在某些机器上,我们遇到了一些 opengl 版本错误的问题。因此,我目前正在努力使整个设置更加健壮。我做的一件事是改用角度。所以我只是想获得工作的角度,这应该对应于使用 OpenGL ES 2.0。

这里是帧缓冲区创建:

glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glGenRenderbuffers(1, &cboId);
glBindRenderbuffer(GL_RENDERBUFFER, cboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);    
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cboId);    
glGenRenderbuffers(1, &dboId);
glBindRenderbuffer(GL_RENDERBUFFER, dboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dboId);

帧缓冲区已完成,未抛出任何错误。上一段和下一段之间的代码也不会抛出任何错误。

glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, colorData);
//No error up to this point
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depthData));
//This call throws 0x502 (GL_INVALID_OPERATION)

通常我使用 GL_DEPTH_COMPONENT24,但由于某些原因它不起作用。所以我改用 GL_DEPTH_COMPONENT16 。也许这暗示出了什么问题。仅供参考,主帧缓冲区使用 24 位深度和 8 位模板。 (我也尝试使用 GL_DEPTH24_STENCIL8 格式,但在 glReadPixels 调用中没有成功)。

改用纹理或 pbuffer 不起作用,因为此变通方法(glGetTexImage(...)glMapBuffer(...))所需的函数未在我坚持使用的 GL 版本中实现。

根据Khronos specification

format Specifies the format of the pixel data. The following symbolic values are accepted: GL_ALPHA, GL_RGB, and GL_RGBA.

type Specifies the data type of the pixel data. Must be one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4, or GL_UNSIGNED_SHORT_5_5_5_1.

GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_5_6_5 and format is not GL_RGB.

GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_4_4_4_4 or GL_UNSIGNED_SHORT_5_5_5_1 and format is not GL_RGBA.

GL_INVALID_OPERATION is generated if format and type are neither GL_RGBA and GL_UNSIGNED_BYTE, respectively, nor the format/type pair returned by querying GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE.

您的代码中的 formattype 都不符合此要求。

正如@xeed 已经指出的那样,@Reaper 已经回答了问题的“为什么”部分。至于在OpenGL ES中“如何”获取深度信息,经过网上大量搜索,以及与一些同行的交流,我只看到提出了两种方法:

  1. (仅限 ES 3+)将深度纹理附加到渲染场景的帧缓冲区。然后渲染“full-screen”四边形,在片段着色器中采样深度纹理这个四边形,并将深度纹理值输出为您的颜色值,例如在你的红色频道。最后,对该颜色信息使用 glReadPixels 以取回深度值。参见:

  2. 获取 gl_FragCoord.z 的值。参见:

我已经尝试了这两个选项,并且似乎都有效,虽然我仍然有一个 depth-inversion 问题,但第二个问题虽然很容易修复(只需从 1.0f 中减去该值),但我试图破译的原因。此外,第二个选项的详细信息取决于您何时需要这些值,以及原始场景需要哪些颜色信息。例如。在我的例子中,我可以将 gl_FragCoord.z 作为我的 alpha 通道值,因为我实际上并没有显示我的渲染结果。但是,如果您确实需要原始渲染的 alpha 通道完好无损,也许多个渲染目标(其中之一获得 gl_FragCoord.z 值)将是解决方案吗?