2

I'm implementing in my program the gaussian blur effect. To do the job I need to render the first blur information (the one on Y axis) in a specific texture (let's call it tex_1) and use this same information contained in tex_1 as input information for a second render pass (for the X axis) to fill an other texture (let's call it tex_2) containing the final gaussian blur result.

A good practice should be to create 2 frame buffers (FBOs) with a texture attached for each of them and linked both to GL_COLOR_ATTACHMENT0 (for example). But I just wonder one thing:

Is it possible to fill these 2 textures using the same FBO ?

So I will have to enable GL_COLOR_ATTACHMENT0 and GL_COLOR_ATTACHMENT1 and bind the desired texture to the correct render pass as follow :

Pseudo code:

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()

But in my mind there is a problem because I need for each render pass to bind an external texture as an input in my fragment shader. Consequently, the first binding of the texture (the color_attachment) is lost!

So does it exist a way to solve my problem using one FBO or do I need to use 2 separate FBOs ?

genpfault
  • 51,148
  • 11
  • 85
  • 139
user1364743
  • 5,283
  • 6
  • 51
  • 90

2 Answers2

9

I can think of at least 3 distinct options to do this. Where the 3rd one will actually not work in OpenGL ES, but I'll explain it anyway because you might be tempted to try it otherwise, and it is supported in desktop OpenGL.

I'm going to use pseudo-code as well to cut down on typing and improve readability.

2 FBOs, 1 attachment each

This is the most straightforward approach. You use a separate FBO for each texture. During setup, you would have:

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

Then for rendering:

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

1 FBO, 1 attachment

In this approach, you use one FBO, and attach the texture you want to render to each time. During setup, you only create the FBO, without attaching anything yet.

Then for rendering:

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

1 FBO, 2 attachments

This seems to be what you had in mind. You have one FBO, and attach both textures to different attachment points of this FBO. During setup:

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

Then for rendering:

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

This renders to tex2 in pass 2 because it is attached to ATTACHMENT1, and we set the draw buffer to ATTACHMENT1.

The major caveat is that this does not work with OpenGL ES. In ES 2.0 (without using extensions) it's a non-starter because it only supports a single color buffer.

In ES 3.0/3.1, there is a more subtle restriction: They do not have the glDrawBuffer() call from full OpenGL, only glDrawBuffers(). The call you would try is:

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

This is totally valid in full OpenGL, but will produce an error in ES 3.0/3.1 because it violates the following constraint from the spec:

If the GL is bound to a draw framebuffer object, the ith buffer listed in bufs must be COLOR_ATTACHMENTi or NONE.

In other words, the only way to render to GL_COLOR_ATTACHMENT1 is to have at least two draw buffers. The following call is valid:

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

But to make this actually work, you'll need a fragment shader that produces two outputs, where the first one will not be used. By now, you hopefully agree that this approach is not appealing for OpenGL ES.

Conclusion

For OpenGL ES, the first two approaches above will work, and are both absolutely fine to use. I don't think there's a very strong reason to choose one over the other. I would recommend the first approach, though.

You might think that using only one FBO would save resources. But keep in mind that FBOs are objects that contain only state, so they use very little memory. Creating an additional FBO is insignificant.

Most people would probably prefer the first approach. The thinking is that you can configure both FBOs during setup, and then only need glBindFramebuffer() calls to switch between them. Binding a different object is generally considered cheaper than modifying an existing object, which you need for the second approach.

neevek
  • 11,760
  • 8
  • 55
  • 73
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • Thanks so much for this complete course! In fact I did not understand correctly the usage of the function glDrawBuffers (I believed it could be used only during FBO initialization and so could not be used during the rendering). In fact this function 'glDrawBuffers' has more or less the same behaviour than the function 'glBindTexture' but the main difference is with the first one it's possible to target several textures (color buffers (!OpenGL ES)) at once (but it can be used only with FBO of course). Thanks a lot for your help, it's more clear for me now! – user1364743 May 15 '15 at 09:44
  • Those two functions are not the same. `glDrawBuffers` controls which texture you **render to**, while `glBindTexture` controls which texture you **sample from**. – Reto Koradi May 15 '15 at 16:00
  • That quote is from the ES 3.0 spec section 4.2.1 "Selecting Buffers for Writing", page 185, paragraph 5. – genpfault Dec 07 '18 at 17:01
0

Consequently, the first binding of the texture (the color_attachment) is lost!

No, it isn't. Maybe your framebuffer class works that way, but then, it would be a very bad abstraction. The GL won't detach a texture from an FBO just because you bind this texture to some texture unit. You might get some undefined results if you create a feedback loop (rendering to a texture you are reading from).

EDIT

However, as @Reto Koradi pointed out in his excellent answer, (and his comment to this one), you can't simply render to a single color attachment in unextended GLES1/2, and need some tricks in GLES3. As a result, The fact I'm pointing out here is still true, but not really helpful for the ultimate goal you are trying to achieve.

derhass
  • 43,833
  • 2
  • 57
  • 78
  • So, to render a part of the information into ATTACHEMENT0 and an other part into ATTACHEMENT1 I need to bind each of them separatly? So, for the first part I need to bind texture (tex_1) COLOR_ATTACHMENT and execute the render pass and for the rest I need to bind texture COLOR_ATTACHMENT1 for the second render pass. Is that right ? Is it the right way to choose which texture I need to fill ? Thanks for your help! – user1364743 May 14 '15 at 15:56
  • You are not very clear here. What exactly do you mean? You _bind_ a texture to a texture unit to be able to sample _from_ it. You _attach_ a texture to a FBO to render _into_ it. When you have multiple color attachments, you can use `glDrawBuffer()` or `glDrawBuffers` to select which one(s) you want to render into. – derhass May 14 '15 at 16:35
  • 1
    @derhass In OpenGL ES, you cannot render only to ATTACHMENT1. See the spec quote in my answer. I had already started filing a bug against a GPU vendor when I found that restriction in the spec. – Reto Koradi May 15 '15 at 04:24
  • @RetoKoradi: You are right. I updated the question to make it clear that it is not the answer, but only addresses a minor side issue. – derhass May 15 '15 at 11:43