【问题标题】:Quickly read values from the depth buffer从深度缓冲区快速读取值
【发布时间】:2013-08-03 02:22:13
【问题描述】:

我正在使用 GLSL 在 OpenGL (3.X) 中实现“景深”。 它工作得很好,着色器(纹理(tex1,vec2(0.5)))占据了中心深度以进行自动对焦。但是使用这样的实现焦点会立即发生。他应该喜欢眼睛——逐渐适应。为此,我需要 CPU 内存一侧中间的深度。 我知道如何使用 PBO,使用它来读取像素/像素颜色工作得非常快(异步运行),但是将 GL_RGB 更改为 GL_DEPTH_COMPONENT 性能会急剧下降。你知道有什么方法可以比较快地读出中心深度是什么颜色吗?

    glReadPixels(a/2, b/2, 1, 1, GL_RGB , GL_BYTE, 0); // WORKS FINE - 30 ns
    glReadPixels(a/2, b/2, 1, 1, GL_DEPTH_COMPONENT, GL_BYTE, 0); // WORKS SLOOOW - around 3500 microseconds
    glReadPixels(a/2, b/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, 0); // WORKS SLOOOW- around 3500 microseconds

@编辑: 创建 FBO 纹理:

glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT32F , sett.framebuffer_width,  sett.framebuffer_height, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);

下载深度:

index = (index + 1) % 2;// for ping-pong PBO
nextIndex = (index + 1) % 2;// for ping-pong PBO
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]);
glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT , GL_FLOAT, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);

将深度复制到内存:

GLfloat tabc[10];
glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]);
GLfloat* srccccc = (GLfloat*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
tabc[0]=*srccccc;
std::cout<<"Depth center: "<<tabc[0]<<std::endl;//only to check is that correct
glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);     

现在可以运行了,速度要快得多(从 3500 微秒提高到 357 微秒)。 现在还好吗?所有格式?

【问题讨论】:

  • 为什么改变景深速度需要CPU干预? CPU 应该提供清晰聚焦的深度。所以这应该只是 CPU 为焦距设置动画的问题。
  • CPU 需要几个先前帧的中心深度才能更改模糊渐变。如何告诉 CPU 一个物体在屏幕中间有多远? (已知最简单的方法,我需要快速完成)要知道如何对焦点进行分级。然后是如何在着色器中模糊。
  • "如何告诉 CPU 一个物体在屏幕中间有多远?:让 GPU/CPU 同步真的值得那个 答案准确吗?我只是根据场景中心附近的物体进行猜测。
  • 但是如何在 cpu 上猜到呢?

标签: performance opengl glsl depth


【解决方案1】:
glReadPixels(a/2, b/2, 1, 1, GL_DEPTH_COMPONENT, GL_BYTE, 0); // WORKS SLOOOW - around 3500 ns
glReadPixels(a/2, b/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, 0); // WORKS SLOOOW- around 3500 ns

当然它们很慢。一般来说,深度缓冲区既不是 8 位字节也不是 32 位浮点值。您快速回读颜色的原因是因为您的 pixel transfer format and type 与您的帧缓冲区匹配。而且由于您在回读中非常明智地使用了 PBO,因此必须对值进行基于 CPU 的转换会降低您的性能。

所以你需要在这里做同样的事情:将像素传输参数与帧缓冲区图像匹配。

如果您从 FBO 读取数据,那么您应该知道图像的确切位深度(您正在使用sized internal formats,对吗?)。所以简单地匹配:

GL_DEPTH_COMPONENT16  -> GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT
GL_DEPTH_COMPONENT32  -> GL_DEPTH_COMPONENT, GL_UNSIGNED_INT
GL_DEPTH_COMPONENT32F -> GL_DEPTH_COMPONENT, GL_FLOAT

还有另一种纯深度缓冲区格式:GL_DEPTH_COMPONENT24。你或许可以只使用GL_UNSIGNED_INT,但很有可能OpenGL在写入时必须将数据扩展为32位。

这就是您应该改用GL_DEPTH24_STENCIL8 的原因之一。对于那个,你可以这样做:

GL_DEPTH24_STENCIL8 -> GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8

当您需要深度值时,您必须将每个 32 位值右移 8 位并屏蔽前 8 位。但它应该可以工作。

如果您从default framebuffer 阅读,那就有点棘手了。很有可能,当您设置渲染上下文时,您告诉 OpenGL 您想要在帧缓冲区中使用什么位深度。但是你可能没有得到它们,所以你需要问。

为此,您需要employ glGetFramebufferAttachmentParameter。这样,您可以从GL_DEPTH 附件中查询GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE​,并从GL_STENCIL 附件中查询GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE​。测试 both 很重要;如果您同时具有深度和模板位,则需要使用深度/模板读取。

一旦你有了比特深度,就假装它们是上述内部格式之一并照常进行。

【讨论】:

  • 这是我创建的深度纹理:glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT,settings.framebuffer_width, settings.framebuffer_height, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
  • @Skides:停止使用未调整大小的内部格式。告诉 OpenGL 你想要什么;不要猜测。
  • 主帖已更新 - 是否正确(所有格式)? 377 微秒 - 从 3500 下降,所以它好多了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-13
  • 2011-02-05
  • 2018-06-19
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多