OpenGL - How to draw to a multisample framebuffer and then use the result as a normal texture?

后端 未结 1 780
小蘑菇
小蘑菇 2021-01-14 16:48

I\'m developing a little gamedev library. One of the elements of this library is a Canvas (offscreen drawing area), which is implemented through an OpenGL framebuffer. So fa

相关标签:
1条回答
  • 2021-01-14 17:19

    Just to make sure that there's no confusion. You can't just create a texture that is x times bigger and then wish that filters will do the magic. Because GL_LINEAR, etc. only averages the four texels closest to the center of the pixel being textured.

    To create a multisample texture you'd use glTexImage2DMultisample() (available in core since 3.2). You'd set it up like this.

    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA8, width, height, false);
    

    It should be self-explanatory as samples being the amount of samples in the multisample texture. Also change the internalformat as you please.

    To attach the texture to a framebuffer you equally use glFramebufferTexture2D(). But instead of setting the textarget as GL_TEXTURE_2D you set it to GL_TEXTURE_2D_MULTISAMPLE.

    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0);
    

    Remember to check the status of your framebuffer.

    In your shader you'll also have to use sampler2DMS to be able to access the multisample texture. However note that multisample textures work quite differently compared to regular textures. If you want to read from the texture you'll have to use texelFetch().

    So if you want to sample from a multisample texture then you can't use texture() but would have to utilize texelFetch() with something like.

    uniform int texSamples;
    uniform sampler2DMS tex;
    
    vec4 textureMultisample(sampler2DMS sampler, ivec2 coord)
    {
        vec4 color = vec4(0.0);
    
        for (int i = 0; i < texSamples; i++)
            color += texelFetch(sampler, coord, i);
    
        color /= float(texSamples);
    
        return color;
    }
    

    Note that texelFetch() doesn't take normalized coordinates, you can circumvent this with something like:

    vec2 uv = vec2(0.5, 0.5); // normalized coordinates
    ivec2 texSize = textureSize(tex, 0);
    ivec2 texCoord = ivec2(uv * texSize);
    vec4 color = textureMultisample(tex, texCoord);
    

    At the end of the day if you want display the crisp anti-aliased result, you'll have to blit it to the screen.

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
    glDrawBuffer(GL_BACK);
    glBlitFramebuffer(0, 0, src_width, src_height, 0, 0, dst_width, dst_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    

    If you need a multisample depth buffer then look into glRenderbufferStorageMultisample().

    Also make sure that glEnable(GL_MULTISAMPLE). However today most drivers enable it by default.

    Last but not least, here's a few other Stack Overflow/Exchange question related to multisampling that you might find interesting.

    • Filtering
    • texelFetch() vs texture()
    • Multisample Window Buffer
    0 讨论(0)
提交回复
热议问题