GL_EXT_framebuffer_object with Multisampling

I’ve been using OpenGL Framebuffer Objects (FBOs) a lot lately. One day I was trying to render smoother, anti-aliased edges by using multisampling in combination with a FBO. After iterating through all possible options of OpenGL and the window context I was only confused… my FBO refused to multisample.

My buddy Google who’s usually most helpful proved to be really bitchy in this matter. But after long arguments with Google I finally got some interesting sources.

This is nothing new, this is nothing revolutionary… this is meant for people with a similar problem as the one I had and help them to find their way to the solution a little quicker than I did.

The standard example for using a Framebuffer Object to render to a texture (for more details and explanations ckeck the FBO 101):

// depth buffer
glGenRenderbuffersEXT(1, &depthBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);

// create fbo and attach depth buffer
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthBuffer);

// create texture
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// set your texture parameters here if required ...

// attach texture
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);

...

// rendering procedure
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, width, height);
// ... draw ...
glPopAttrib();
// you have to unbind all fbos before you can render to the main window
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

Now we extend the previous example a little bit by using GL_EXT_framebuffer_multisample and GL_EXT_framebuffer_blit in addition to the GL_EXT_framebuffer_object extension so that we can use multisampling for anti-aliasing. The only tricky part is that we now use two FBOs. One is a normal FBO which also the texture is attached to. Only the second FBO is multisampled. While rendering, the multisampled FBO’s content is blitted to the normal FBO.

// multi sampled color buffer
glGenRenderbuffersEXT(1, &colorBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorBuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, GL_RGBA8, width, height);

// multi sampled depth buffer
glGenRenderbuffersEXT(1, &depthBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, GL_DEPTH_COMPONENT, width, height);

// create fbo for multi sampled content and attach depth and color buffers to it
glGenFramebuffersEXT(1, &mfbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mfbo);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, colorBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthBuffer);

// create texture
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// set your texture parameters here if required ...

// create final fbo and attach texture to it
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);

...

// rendering procedure
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mfbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, width, height);
// ... draw ...
glPopAttrib();
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, mfbo);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo);
glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// you have to unbind all fbos before you can render to the main window
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
...

// cleaning up
glDeleteRenderbuffersEXT(1, &depthBuffer);
glDeleteRenderbuffersEXT(1, &colorBuffer);
glDeleteFramebuffersEXT(1, &mfbo);
glDeleteFramebuffersEXT(1, &fbo);

Make sure that your graphics device supports the GL_EXT_framebuffer_multisample and GL_EXT_framebuffer_blit extensions because usually if an extension is not available the compiler won’t complain and no error message will be displayed. Instead your application will simply crash (at least this is what happens when you access the OpenGL extensions through glew). If you’re using glew you can simply do such a check by calling glewIsSupported(…). It took me a while to figure out that it was my laptop’s ATI Mobility Radeon X1400 inability to anti-alias a FBO and not my inattention that caused the crash :-( .

One last hint: If you want to render your FBO onto a non-power-of-two sized texture you might be better off using the GL_ARB_texture_rectangle extension. Replace GL_TEXTURE_2D with GL_TEXTURE_RECTANGLE_ARB… that’s it.

Enough extensions, happy hacking!

Update:
Also see http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample

This entry was posted in Coding, OpenGL. Bookmark the permalink.

13 Responses to GL_EXT_framebuffer_object with Multisampling

  1. mudlord says:

    Thank you for this!

    I can’t thank you enough! I had similar issues as you had with finding decent code samples in how to do this…I’m glad now all my searching is at a end..

  2. Stefan says:

    You’re welcome!

    Wow, I didn’t expect the first reply only 8 hours after posting this, I’m happy to help. Let me now if you encounter any problems with the sample code.

  3. mudlord says:

    Hi again,

    I ran into some issues with the implementation of multisampled FBOs.

    I tried to mod the FBO 101 sample, but ti didn’t work for me.

    I know there is a syntax issue in there with colorBuffer, but fixing that, all I get is a blank screen.

    Here is my code:
    http://pastebin.ca/1191339

  4. Stefan says:

    hey, i’m pretty busy these days… but i’ll have a look at your code for sure as soon as i’ll get a possibility

  5. Stefan says:

    hey,

    i found the 2 bugs in your code and submitted a working example here: http://pastebin.ca/1192017
    i ‘only’ have linux here right now, so i switched over to glew instead of glee and changed some of the header, but it should be working for you as well with little changes.
    the mistakes were:
    glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 16, GL_DEPTH_COMPONENT, width, height);
    instead of
    glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 16, width, height); (nasty one… )
    and that you have to unbind the currently attached fbo before you can draw to the main window framebuffer. i’ll actually add this to my example code… you might not be the last one to fall into this trap!! thanks ;)

    and you might actually also want to add GLUT_DEPTH to the initDisplay call, otherwise it looks strange

    I hope I could help, gl :)

  6. mudlord says:

    Thanks for your help!

    Now my code works perfect. :)

  7. Darek Pryczek says:

    Why would I be better off using rectangle textures? All harware supporting framebuffer extensions also supports non-power-of-two textures. Are you suggesting that rectangle textures are much faster? They outght to be in order to justify the hassle with non normalized texture coordinates…

  8. Stefan says:

    You’re right, the texture rectangle extension has some cons (no mipmapping, non normalized coords (can also be a pro tho), no borders, less wrap modes… ). I wasn’t

    aware of the GL_ARB_texture_non_power_of_two when i wrote this article.
    But actually not all hardware that supports framebuffer exts also supports NPOTs (non-power-of-two textures), for example my ATI Mobility Radeon X1400 actually

    doesn’t support it. The weird thing is, that it’s still able to render NPOTs, but slower. I tried this example with a NPOT:

    http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=06 It crashes on Linux (using grappy ati fglrx drivers) and crashes also on Windows. I heard some people

    telling that it should be possible on their ATI cards even though GL_ARB_texture_non_power_of_two is not supported to get NPOTS running (but with a remarkable performance penalty).
    I guess that in those cases it is actually not the hardware that supports NPOTs but only a slow software implementation (that was provided in the driver to keep

    things OpenGL 2.0 compliant) what would explain the loss of fps. But anyway, my laptop just crashes :-( it says it’s OpenGL 2.0 compatible… but thanks to you I just found out that it’s not… OpenGL 2.0 is supposed to support this!

    Thanks for your remark, good one!

  9. onar3d says:

    Dextha,

    Indeed out of all examples on the internet for how to achieve this, the only one that actually worked was yours!

    Thank you for publishing this post!

  10. Tim says:

    I’ve got a question for you — why do you have fbo as well as mfbo? Why don’t you render from the multisampled buffer straight to the screen?

  11. Stefan says:

    Hey Tim,

    Thanks for your question, it’s a good one. I didn’t really think of that when I wrote this post (I was just copying and pasting code together to get a working example). In general you’re absolutely correct: It would make sense to directly downsample from the mfbo to the OpenGL backbuffer without first binding another FBO.

    I got kinda confused since all examples I can find on the internet still do it by using the intermediate FBO. If you read the last section (More info…) of http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample you’ll find a short explanation, the pixel ownership test is explained here: http://www.opentk.com/doc/chapter/2/opengl/fragment-ops/pixel-ownership-test.
    So one example I can think of that might go wrong if the intermediate FBO is not used: Let’s say one corner of the OpenGL window is covered by another window. Now when you blit onto the backbuffer from the mbfo the covered area is not written. Now a problem will arise if you try to read the backbuffer into a texture and display it on another area of the OpenGL window that is not covered. The part of the texture that corresponds to the covered are will contain old/undefined data.
    (But if you wanted to render to a texture it should be faster with the intermediate FBO anyway.)

    I hope this was helpful.

    (I’m surprised that people still read this, maybe I should revise the whole post, add some more detailed explanations and source for a complete running example.)

  12. Tim says:

    Once I’ve got data in the fbo and texture, how do I draw the texture to the screen? I mean, yeah, full screen quad, but texture isn’t a texture, it’s a part of the framebuffer fbo.

  13. Stefan says:

    But you can still use that texture like any other texture.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>