【问题标题】:Using a FrameBufferObject with several Color Texture attachments使用带有多个颜色纹理附件的 FrameBufferObject
【发布时间】:2015-07-26 05:25:31
【问题描述】:

我正在我的程序中实现高斯模糊效果。为了完成这项工作,我需要在特定纹理(我们称之为 tex_1)中渲染第一个模糊信息(Y 轴上的那个),并使用 tex_1 中包含的相同信息作为第二个渲染通道的输入信息(对于 X 轴) 来填充包含最终高斯模糊结果的其他纹理(我们称之为 tex_2)。

一个好的做法应该是创建 2 个帧缓冲区 (FBO),每个缓冲区都附加一个纹理,并将两者都链接到 GL_COLOR_ATTACHMENT0(例如)。但我只想知道一件事:

是否可以使用相同的 FBO 填充这 2 个纹理?

所以我必须启用 GL_COLOR_ATTACHMENT0 和 GL_COLOR_ATTACHMENT1 并将所需的纹理绑定到正确的渲染通道,如下所示:

伪代码:

FrameBuffer->Bind()
{
     FrameBuffer->GetTexture(GL_COLOR_ATTACHMENT0)->Bind(); //tex_1
     {
          //BIND external texture to blur
          //DRAW code (Y axis blur pass) here...
            //-> Write the result in texture COLOR_ATTACHEMENT0 (tex_1)
     }
     FrameBuffer->GetTexture(GL_COLOR_ATTACHMENT1)->Bind(); //tex_2
     {
          //BIND here first texture (tex_1) filled above in the first render pass
          //Draw code (X axis blur pass) here...
            //-> Use this texture in FS to compute the final result
            //within COLOR_ATTACHEMENT1 (tex_2) -> The final result
     }
}
FrameBuffer->Unbind()

但在我看来有一个问题,因为我需要为每个渲染通道绑定一个外部纹理作为我的片段着色器中的输入。因此,纹理的第一个绑定(color_attachment)丢失了!

那么是否存在使用一个 FBO 解决我的问题的方法,还是我需要使用 2 个单独的 FBO?

【问题讨论】:

    标签: opengl-es opengl-es-2.0 shader fbo


    【解决方案1】:

    因此,纹理的第一个绑定(color_attachment) 迷路了!

    不,不是。也许您的帧缓冲区类以这种方式工作,但是那样的话,这将是一个非常糟糕的抽象。 GL 不会仅仅因为您将此纹理绑定到某个纹理单元而从 FBO 中分离纹理。如果您创建一个反馈循环(渲染到您正在读取的纹理),您可能会得到一些未定义的结果。

    编辑

    但是,正如@Reto Koradi 在他的出色回答中指出的那样(以及他对此的评论),您不能简单地在未扩展的 GLES1/2 中渲染为单一颜色的附件,并且需要在 GLES3 中使用一些技巧。因此,我在这里指出的事实仍然是正确的,但对于您要实现的最终目标并没有真正的帮助。

    【讨论】:

    • 那么,要将一部分信息呈现到 ATTACHEMENT0 中,另一部分呈现到 ATTACHEMENT1 中,我需要分别绑定它们吗?因此,对于第一部分,我需要绑定纹理 (tex_1) COLOR_ATTACHMENT 并执行渲染通道,而对于其余部分,我需要为第二个渲染通道绑定纹理 COLOR_ATTACHMENT1。那正确吗 ?选择我需要填充的纹理是否正确?感谢您的帮助!
    • 你这里不是很清楚。你到底什么意思?您将纹理绑定到纹理单元,以便能够从它进行采样。您将纹理附加 到FBO 以将其渲染到。当您有多个颜色附件时,您可以使用glDrawBuffer() 或glDrawBuffers 选择要渲染到哪个(或多个)。
    • @derhass 在 OpenGL ES 中,您不能只渲染到 ATTACHMENT1。请参阅我的回答中的规范报价。当我在规范中发现该限制时,我已经开始向 GPU 供应商提交错误。
    • @RetoKoradi:你是对的。我更新了这个问题,明确表示这不是答案,而只是解决了一个小问题。
    【解决方案2】:

    我可以想到至少 3 个不同的选项来做到这一点。第 3 个实际上不在 OpenGL ES 中工作,但我还是会解释它,因为您可能会尝试以其他方式尝试它,并且它在桌面 OpenGL 中受支持。

    我还将使用伪代码来减少打字并提高可读性。

    2 个 FBO,每个 1 个附件

    这是最直接的方法。您为每个纹理使用单独的 FBO。在设置过程中,您将:

    attach(fbo1, ATTACHMENT0, tex1)
    attach(fbo2, ATTACHMENT0, tex2)
    

    然后进行渲染:

    bindFbo(fbo1)
    render pass 1
    bindFbo(fbo2)
    bindTexture(tex1)
    render pass 2
    

    1 个 FBO,1 个附件

    在这种方法中,您使用一个 FBO,并且每次都附加您想要渲染的纹理。在设置过程中,您只创建 FBO,还没有附加任何东西。

    然后进行渲染:

    bindFbo(fbo1)
    attach(fbo1, ATTACHMENT0, tex1)
    render pass 1
    attach(fbo1, ATTACHMENT0, tex2)
    bindTexture(tex1)
    render pass 2
    

    1 个 FBO,2 个附件

    这似乎是您的想法。您有一个 FBO,并将两种纹理附加到此 FBO 的不同连接点。设置期间:

    attach(fbo1, ATTACHMENT0, tex1)
    attach(fbo1, ATTACHMENT1, tex2)
    

    然后进行渲染:

    bindFbo(fbo1)
    drawBuffer(ATTACHMENT0)
    render pass 1
    drawBuffer(ATTACHMENT1)
    bindTexture(tex1)
    render pass 2
    

    这会在第 2 步中渲染到 tex2,因为它附加到 ATTACHMENT1,我们将绘制缓冲区设置为 ATTACHMENT1。

    主要的警告是这不适用于 OpenGL ES。在 ES 2.0(不使用扩展)中,它不是一个初学者,因为它只支持一个颜色缓冲区。

    在 ES 3.0/3.1 中,有一个更微妙的限制:它们没有来自完整 OpenGL 的 glDrawBuffer() 调用,只有 glDrawBuffers()。你会尝试的电话是:

    GLenum bufs[1] = {GL_COLOR_ATTACHMENT1};
    glDrawBuffers(bufs, 1);
    

    这在完整的 OpenGL 中完全有效,但在 ES 3.0/3.1 中会产生错误,因为它违反了规范中的以下约束:

    如果 GL 绑定到绘图帧缓冲区对象,则 bufs 中列出的第 i 个缓冲区必须是 COLOR_ATTACHMENTi 或 NONE。

    换句话说,渲染到GL_COLOR_ATTACHMENT1 的唯一方法是至少有两个绘制缓冲区。以下调用有效:

    GLenum bufs[2] = {GL_NONE, GL_COLOR_ATTACHMENT1};
    glDrawBuffers(bufs, 2);
    

    但要让这真正发挥作用,您需要一个片段着色器来产生两个输出,其中第一个不会被使用。到目前为止,您希望您同意这种方法对 OpenGL ES 没有吸引力。

    结论

    对于 OpenGL ES,上面的前两种方法都可以使用,并且都绝对可以使用。我不认为有非常充分的理由选择其中一个。不过,我会推荐第一种方法。

    您可能认为仅使用一个 FBO 会节省资源。但请记住,FBO 是仅包含状态的对象,因此它们使用的内存非常少。创建一个额外的 FBO 是微不足道的。

    大多数人可能更喜欢第一种方法。想法是您可以在设置期间配置两个 FBO,然后只需要 glBindFramebuffer() 调用即可在它们之间切换。绑定不同的对象通常被认为比修改现有对象更便宜,第二种方法需要这样做。

    【讨论】:

    • 非常感谢这个完整的课程!实际上我没有正确理解函数 glDrawBuffers 的用法(我认为它只能在 FBO 初始化期间使用,因此不能在渲染期间使用)。事实上,这个函数 'glDrawBuffers' 与函数 'glBindTexture' 具有或多或少相同的行为,但主要区别在于第一个函数可以同时针对多个纹理(颜色缓冲区(!OpenGL ES))(但它可以当然只能与 FBO 一起使用)。非常感谢您的帮助,我现在更清楚了!
    • 这两个功能不一样。 glDrawBuffers 控制您渲染到哪个纹理,而glBindTexture 控制您从哪个纹理采样。
    • 该引用来自 ES 3.0 规范第 4.2.1 节“选择写入缓冲区”,第 185 页,第 5 段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-28
    • 2021-03-05
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    相关资源
    最近更新 更多