<p>I think you misunderstood the use of <code>glBlitFrameBuffer</code>. When using this to blit Color Buffers, according to the <a href="https://www.khronos.org/opengl/wiki/Framebuffer#Blitting" rel="nofollow noreferrer">docs</a>,</p>
<blockquote>
<p>One thing to keep in mind is this: when using GL_COLOR_BUFFER_BIT​,
the only colors read will come from the <em>read color buffer</em> in the read
FBO, <em>specified by glReadBuffer</em>. The colors written will only go to the
draw color buffers in the write FBO, specified by glDrawBuffers. If
multiple draw buffers are specified, then multiple color buffers are
updated with the same data.</p>
</blockquote>
<p>So simply speaking, when blitting color buffers, you need to specify what <code>ReadBuffer</code> by using <a href="https://www.khronos.org/opengl/wiki/Framebuffer#Read_color_buffer" rel="nofollow noreferrer">glReadBuffer</a> which takes in color attachment number when dealing with FBOs.</p>
<p>So in short, you can't expect to copy both the attachments or textures with a single blit call.</p>
<p>Also, can't you just render the anti-aliased scene once, blit this single texture to get a working copy of it usable in shaders and then extract the second texture which is essentially just the environment subtracted from it?</p>

CC BY-SA 4.0
2021-06-01T17:22:27.823Z
2021-06-01T17:22:27.823Z
gallickgunner
<h3>The setup</h3>
<p>I have a working bloom shader implemented which creates a fbo with two textures attached to it.</p>
<ol>
<li>The first texture holds the entire scene</li>
<li>The other only displays objects which are supposed to be bloomed in a postprocessing (ping pong) step</li>
</ol>
<p>At the and the end both textures are added.</p>
<p>Here is the code which creates the according fbo:</p>
<pre class="lang-cpp prettyprint-override"><code>    void CreateHdrBloomFBO(GLuint &amp;hdr_fbo, GLuint *color_buffers, GLuint *pingpong_fbo, GLuint *pingpong_color_buffers, GLuint &amp;rbo_depth, float width, float height)
    {
        glGenFramebuffers(1, &amp;hdr_fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo);
        glGenTextures(2, color_buffers);

        for (unsigned int i = 0; i &lt; 2; i++)
        {
            glBindTexture(GL_TEXTURE_2D, color_buffers[i]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, color_buffers[i], 0);
        }

        glGenRenderbuffers(1, &amp;rbo_depth);
        glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth);
        GLuint attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, attachments);

        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
            std::cout &lt;&lt; &quot;CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating HDR/BLOOM buffers.&quot; &lt;&lt; std::endl;
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        glGenFramebuffers(2, pingpong_fbo);
        glGenTextures(2, pingpong_color_buffers);
        for (unsigned int i = 0; i &lt; 2; i++)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[i]);
            glBindTexture(GL_TEXTURE_2D, pingpong_color_buffers[i]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpong_color_buffers[i], 0);

            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
                std::cout &lt;&lt; &quot;CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating HDR/BLOOM-pingpong buffers.&quot; &lt;&lt; std::endl;
        }
    }
</code></pre>
<p>So my renderloop looks something like this.</p>
<pre class="lang-cpp prettyprint-override"><code>// 1. Bind fbo
// 2. Render scene 
// 3. Blur cube and earth fragments with two-pass Gaussian Blur
bool horizontal = true, first_iteration = true;
unsigned int amount = 10;
blur_shader.use();
for (unsigned int i = 0; i &lt; amount; i++)
{
    glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[horizontal]);
    blur_shader.set_int(&quot;horizontal&quot;, horizontal);
    // Note on the first iteration I take the second texture from above, which holds the objects which are supposed to get bloomed.
    glBindTexture(GL_TEXTURE_2D, first_iteration ? color_buffers[1] : pingpong_color_buffers[!horizontal]);
    core::RenderQuad(&amp;quad_geometry);
    horizontal = !horizontal;
    if (first_iteration)
        first_iteration = false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// 4. Combine normal and bloom textures
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
bloom_final_shader.use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, color_buffers[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpong_color_buffers[!horizontal]);
bloom_final_shader.set_int(&quot;bloom&quot;, use_bloom);
bloom_final_shader.set_float(&quot;exposure&quot;, exposure);
core::RenderQuad(&amp;quad_geometry);
</code></pre>
<p>This is what the output of the scene render looks like. The are the scene and the blooming targets:</p>
<p><a href="https://i.sstatic.net/KScSk.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/KScSk.png" alt="enter image description here" /></a></p>
<p>In the first blooming step the correct image is passed and &quot;ping ponged&quot; afterwards.</p>
<p><a href="https://i.sstatic.net/h9ic3.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/h9ic3.png" alt="enter image description here" /></a></p>
<p>The final image:</p>
<p><a href="https://i.sstatic.net/FS3nH.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/FS3nH.jpg" alt="enter image description here" /></a></p>
<h3>The problem</h3>
<p>I want to implement antialiasing on top of the given process. Therfore I create a multisample fbo which also holds two textures:</p>
<pre class="lang-cpp prettyprint-override"><code>void CreateMsaaFBO(GLuint &amp;target_fbo, GLuint *target_fbo_textures, GLuint &amp;rbo_depth_stencil, float width, float height)
{
    glGenFramebuffers(1, &amp;target_fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, target_fbo);
    glGenTextures(2, target_fbo_textures);
    for (unsigned int i = 0; i &lt; 2; i++)
    {
        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, target_fbo_textures[i]);
        glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, width, height, GL_TRUE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D_MULTISAMPLE,target_fbo_textures[i], 0);
    }
    glGenRenderbuffers(1, &amp;rbo_depth_stencil);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth_stencil);
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,rbo_depth_stencil);
    GLuint attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
    glDrawBuffers(2, attachments);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout &lt;&lt; &quot;CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating MSAA buffer.&quot; &lt;&lt; std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER_COMPLETE, 0);
}
</code></pre>
<p>So my Plan is to draw the scene as above this time using my msaa_fbo and use <code>glBlitFramebuffer</code> to copy antialiased textures to my original fbo:</p>
<pre class="lang-cpp prettyprint-override"><code>// 1. Bind msaa_fbo
// 2. Render scene 
    glBindFramebuffer(GL_READ_FRAMEBUFFER, msaa_fbo);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hdr_fbo);
    glBlitFramebuffer(0, 0, viewport_width, viewport_height, 0, 0, viewport_width, viewport_height,GL_COLOR_BUFFER_BIT, GL_NEAREST);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 3. blur bright fragments with two-pass Gaussian Blur as above
// ...
</code></pre>
<p>It seems that only the first texture gets copied to the <code>hdr_fbo</code> and therfore the entire image is bloomed.</p>
<p>The output of the <code>msaa_fbo</code> looks correct:</p>
<p><a href="https://i.sstatic.net/P9MK1.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/P9MK1.png" alt="enter image description here" /></a></p>
<p>This is the <code>glBindFramebuffer</code> in renderdoc which seems to target only one texture (the scene):</p>
<p><a href="https://i.sstatic.net/p1cwW.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/p1cwW.png" alt="enter image description here" /></a></p>
<p>That is why the input of the pingpong loop is the entire scene:</p>
<p><a href="https://i.sstatic.net/DjS5T.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/DjS5T.png" alt="enter image description here" /></a></p>
<p>As far as I get it should copy both textures (scene + bloom) to my target <code>hdr_fbo</code> buffer. Why doesn't it? I started learning those concept, so I am not ruling out that I am missing something fundamental.</p>
<p>Also note that a lot of the code is taken from <a href="https://learnopengl.com/" rel="nofollow noreferrer">learnopengl.com</a>.</p>

<opengl><antialiasing><frame-buffer>
glBlitFramebuffer does not copy all textures
チーズパン
# glBlitFramebuffer does not copy all textures

(Asked by: username_0 on 2021-05-28)

### The setup

I have a working bloom shader implemented which creates a fbo with two textures attached to it.

1. The first texture holds the entire scene
2. The other only displays objects which are supposed to be bloomed in a postprocessing (ping pong) step

At the and the end both textures are added.

Here is the code which creates the according fbo:

```cpp
    void CreateHdrBloomFBO(GLuint &hdr_fbo, GLuint *color_buffers, GLuint *pingpong_fbo, GLuint *pingpong_color_buffers, GLuint &rbo_depth, float width, float height)
    {
        glGenFramebuffers(1, &hdr_fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo);
        glGenTextures(2, color_buffers);

        for (unsigned int i = 0; i < 2; i++)
        {
            glBindTexture(GL_TEXTURE_2D, color_buffers[i]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, color_buffers[i], 0);
        }

        glGenRenderbuffers(1, &rbo_depth);
        glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth);
        GLuint attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, attachments);

        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
            std::cout << "CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating HDR/BLOOM buffers." << std::endl;
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        glGenFramebuffers(2, pingpong_fbo);
        glGenTextures(2, pingpong_color_buffers);
        for (unsigned int i = 0; i < 2; i++)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[i]);
            glBindTexture(GL_TEXTURE_2D, pingpong_color_buffers[i]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpong_color_buffers[i], 0);

            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
                std::cout << "CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating HDR/BLOOM-pingpong buffers." << std::endl;
        }
    }

```

So my renderloop looks something like this.

```cpp
// 1. Bind fbo
// 2. Render scene 
// 3. Blur cube and earth fragments with two-pass Gaussian Blur
bool horizontal = true, first_iteration = true;
unsigned int amount = 10;
blur_shader.use();
for (unsigned int i = 0; i < amount; i++)
{
    glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[horizontal]);
    blur_shader.set_int("horizontal", horizontal);
    // Note on the first iteration I take the second texture from above, which holds the objects which are supposed to get bloomed.
    glBindTexture(GL_TEXTURE_2D, first_iteration ? color_buffers[1] : pingpong_color_buffers[!horizontal]);
    core::RenderQuad(&quad_geometry);
    horizontal = !horizontal;
    if (first_iteration)
        first_iteration = false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// 4. Combine normal and bloom textures
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
bloom_final_shader.use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, color_buffers[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpong_color_buffers[!horizontal]);
bloom_final_shader.set_int("bloom", use_bloom);
bloom_final_shader.set_float("exposure", exposure);
core::RenderQuad(&quad_geometry);

```

This is what the output of the scene render looks like. The are the scene and the blooming targets:

[![enter image description here](https://i.sstatic.net/KScSk.png)](https://i.sstatic.net/KScSk.png)

In the first blooming step the correct image is passed and "ping ponged" afterwards.

[![enter image description here](https://i.sstatic.net/h9ic3.png)](https://i.sstatic.net/h9ic3.png)

The final image:

[![enter image description here](https://i.sstatic.net/FS3nH.jpg)](https://i.sstatic.net/FS3nH.jpg)

### The problem

I want to implement antialiasing on top of the given process. Therfore I create a multisample fbo which also holds two textures:

```cpp
void CreateMsaaFBO(GLuint &target_fbo, GLuint *target_fbo_textures, GLuint &rbo_depth_stencil, float width, float height)
{
    glGenFramebuffers(1, &target_fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, target_fbo);
    glGenTextures(2, target_fbo_textures);
    for (unsigned int i = 0; i < 2; i++)
    {
        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, target_fbo_textures[i]);
        glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, width, height, GL_TRUE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D_MULTISAMPLE,target_fbo_textures[i], 0);
    }
    glGenRenderbuffers(1, &rbo_depth_stencil);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth_stencil);
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,rbo_depth_stencil);
    GLuint attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
    glDrawBuffers(2, attachments);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "CORE::CREATE_FRAMEBUFFER_FAIL - Failed creating MSAA buffer." << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER_COMPLETE, 0);
}

```

So my Plan is to draw the scene as above this time using my msaa\_fbo and use `glBlitFramebuffer` to copy antialiased textures to my original fbo:

```cpp
// 1. Bind msaa_fbo
// 2. Render scene 
    glBindFramebuffer(GL_READ_FRAMEBUFFER, msaa_fbo);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hdr_fbo);
    glBlitFramebuffer(0, 0, viewport_width, viewport_height, 0, 0, viewport_width, viewport_height,GL_COLOR_BUFFER_BIT, GL_NEAREST);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 3. blur bright fragments with two-pass Gaussian Blur as above
// ...

```

It seems that only the first texture gets copied to the `hdr_fbo` and therfore the entire image is bloomed.

The output of the `msaa_fbo` looks correct:

[![enter image description here](https://i.sstatic.net/P9MK1.png)](https://i.sstatic.net/P9MK1.png)

This is the `glBindFramebuffer` in renderdoc which seems to target only one texture (the scene):

[![enter image description here](https://i.sstatic.net/p1cwW.png)](https://i.sstatic.net/p1cwW.png)

That is why the input of the pingpong loop is the entire scene:

[![enter image description here](https://i.sstatic.net/DjS5T.png)](https://i.sstatic.net/DjS5T.png)

As far as I get it should copy both textures (scene + bloom) to my target `hdr_fbo` buffer. Why doesn't it? I started learning those concept, so I am not ruling out that I am missing something fundamental.

Also note that a lot of the code is taken from [learnopengl.com](https://learnopengl.com/).

**Tags:** opengl, antialiasing, frame-buffer


---

## 1 answers

### Answer by username_1 on 2021-06-01. Score: 4 | Accepted answer

I think you misunderstood the use of `glBlitFrameBuffer`. When using this to blit Color Buffers, according to the [docs](https://www.khronos.org/opengl/wiki/Framebuffer#Blitting),

> One thing to keep in mind is this: when using GL\_COLOR\_BUFFER\_BIT​,
> the only colors read will come from the *read color buffer* in the read
> FBO, *specified by glReadBuffer*. The colors written will only go to the
> draw color buffers in the write FBO, specified by glDrawBuffers. If
> multiple draw buffers are specified, then multiple color buffers are
> updated with the same data.

So simply speaking, when blitting color buffers, you need to specify what `ReadBuffer` by using [glReadBuffer](https://www.khronos.org/opengl/wiki/Framebuffer#Read_color_buffer) which takes in color attachment number when dealing with FBOs.

So in short, you can't expect to copy both the attachments or textures with a single blit call.

Also, can't you just render the anti-aliased scene once, blit this single texture to get a working copy of it usable in shaders and then extract the second texture which is essentially just the environment subtracted from it?


---

