使用带有多个颜色纹理附件的FrameBufferObject

时间:2015-05-14 14:31:21

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

我在我的程序中实现了高斯模糊效果。为了完成这项工作,我需要在特定纹理中渲染第一个模糊信息(Y轴上的一个)(让我们称之为tex_1)并使用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?

2 个答案:

答案 0 :(得分:7)

我可以想到至少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绑定到draw framebuffer对象,则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()次调用即可在它们之间切换。绑定不同的对象通常被认为比修改现有对象便宜,这是第二种方法所需要的。

答案 1 :(得分:0)

  

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

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

修改

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