FBO and multisampling

how do one do multisampling on a FBO?

GL_EXT_framebuffer_multisample

although I am not sure how many cards support it (geforce8 for sure)

my card supports “GL_EXT_framebuffer_multisample”… ive read the documentation but i dont rly understand how to use it…?

It’s just like you create renderbuffers in GL_EXT_framebuffer_object, but for mutisampling you use
glRenderbufferStorageMultisampleEXT(…)
instead of
glRenderbufferStorageEXT(…)

N.

thats it? i dont have to render the fbo multiple times for each sample etc…?

Yes, that’s it :slight_smile:

That was only the first half, the second half is that you have to put glEnable(GL_MULTISAMPLE_ARB) in your code before rendering :wink:

N.

hmmmm, I wonder if leaving that out would have the same effect as it does in D3D10 allowing you to use that stencil routing algo for translucancy…

or http://http.download.nvidia.com/develope…tProcessing.zip

…and the third half is that you can not use the multisample fbo for RTT directly. you have to use GL_framebuffer_blit to blit the contents from the multisample FBO to a normal FBO to use the attached textures…

for example, like this:


        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo_ms_draw_id);

        // draw some stuff

        // blit contents into textures
        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, _fbo_ms_draw_id);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo_id);
        glBlitFramebufferEXT(0, 0, width, height,
                             0, 0, width, height,
                             GL_COLOR_BUFFER_BIT |
                             GL_DEPTH_BUFFER_BIT,
                             GL_NEAREST);

        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);

the first FBO is the multisample fbo with the multisample renderbuffers as color0 and depth attachment. the second fbo, target of the blit operation, is a nornal FBO with textures bound to color0 and depth attachment points.

Re. the “third 50%” :), wouldn’t that actually make it both more straightforward and faster to just render to back FB and copy that to a texture (old-school “RTT”)? I mean, a blit is a blit is a blit.

yes this would still work, but with the multisample fbo you have a common way to do AA without the WGL, GLX, AGL platform stuff and without limiting yourself to the framebuffer dimensions and pixel types. this way multisample MRTs and multisample floating point buffers are also on the menu.

another advantage is, that you have the new nVidia coverage AA modes available which can not be used otherways.

ok… ive made a try… but the result is all gray…


#ifndef _FBOMULTISAMPLE
#define _FBOMULTISAMPLE

#include "stdafx.h"

#include <boost/shared_ptr.hpp>

namespace nbase{	
namespace graphics{
namespace opengl{

class ShadowMap;

class FBOMultiSample
{
	friend class ShadowMap;

	public:

		inline FBOMultiSample(){}

		inline FBOMultiSample(int _width, int _height, GLint  type, bool mipmap = false)
		{			
			Create(_width, _height,  type, mipmap);
		}

		inline void Create(int _width, int _height, GLint  type, bool mipmap = false)
		{
			mipmap_ = mipmap;
			width_ = _width;
			height_ = _height;
			type_ = type;

			// Create First normal FBO

			glActiveTexture(GL_TEXTURE15);

			glGenFramebuffersEXT(1, &fbo);
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

			glGenRenderbuffersEXT(1, &depthBuffer);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);

			glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width_, height_);

			glGenTextures(1, &img);
			glBindTexture(GL_TEXTURE_2D, img);

			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, img, 0);

			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthBuffer);

			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

			GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);			

			glTexImage2D(GL_TEXTURE_2D, 0, type,  _width, height_, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

			if (mipmap_)
			{
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);		
				glGenerateMipmapEXT(GL_TEXTURE_2D);
			}
			else
			{
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		
			}

			glTexParameterf(GL_TEXTURE_2D, 0x84FE, 8); // ANISTROPY

			// Create second multismaple FBO

			glActiveTexture(GL_TEXTURE16);

			glGenFramebuffersEXT(1, &fbo);
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, draw_fbo);

			glGenRenderbuffersEXT(1, &draw_depthBuffer);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, draw_depthBuffer);

			glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 8, GL_DEPTH_COMPONENT, width_, height_);

			glGenTextures(1, &draw_img);
			glBindTexture(GL_TEXTURE_2D, draw_img);

			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, draw_img, 0);

			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, draw_depthBuffer);

			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

			

		}

		inline ~FBOMultiSample()
		{
			if (img != NULL)
				glDeleteTextures(1, &img);

			if (!fbo != NULL)
				glDeleteFramebuffersEXT(1, &fbo);

			if (!depthBuffer != NULL)
				glDeleteRenderbuffersEXT(1, &depthBuffer);
		}


		inline void Begin(GLenum texture)
		{

			glDisable(GL_ALPHA_TEST);
			glDisable(GL_BLEND);
			
			glActiveTexture(texture);
			glBindTexture(GL_TEXTURE_2D, draw_img);

			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, draw_fbo);
				
			glPushAttrib(GL_VIEWPORT_BIT);
			glViewport(0,0,width_,height_);

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);				

			glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
		}

		inline void End()
		{
			if (mipmap_)
				glGenerateMipmapEXT(GL_TEXTURE_2D);

			glPopAttrib();

			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, draw_fbo);
			glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo);
			glBlitFramebufferEXT(0, 0, width_, height_,
								 0, 0, width_, height_,
								 GL_COLOR_BUFFER_BIT |
								 GL_DEPTH_BUFFER_BIT,
								 GL_NEAREST);

			glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
			glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);

			glEnable(GL_ALPHA_TEST);
			glEnable(GL_BLEND);			
		}

		inline void Use(GLenum texture)
		{
			glActiveTexture(texture);
			glBindTexture(GL_TEXTURE_2D, img);
		}

		inline void Bind(GLenum texture)
		{
			glActiveTexture(texture);
			glBindTexture(GL_TEXTURE_2D, img);

			if (mipmap_)
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);	
		}

		inline int GetHeight() { return height_; }
		inline int GetWidth() { return width_; }
		inline GLenum GetType() { return type_; }

	private:		
		GLuint fbo;
		GLuint depthBuffer;
		GLuint img;

		GLuint draw_fbo;
		GLuint draw_depthBuffer;
		GLuint draw_img;

		int width_;
		int height_;
		GLint type_;

		bool mipmap_;
};

typedef boost::shared_ptr<FBOMultiSample> FBOMultiSamplePtr;



}
}
}

#endif

I did not test your code, but some remarks:

  • you create a texture object and bind it to the fbo. try initializing the texture before binding it (glTexImage, glTexParameteri…).
  • you get the fbo status but never ask if everything went ok. like this for example:

unsigned fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
    std::cout << "error creating fbo, framebufferstatus is not complete:" << std::endl;
    std::string error;
	
    switch (fbo_status) {
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT");break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:  error.assign("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT");break;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT");break;
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:             error.assign("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT");break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT");break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT");break;
        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:                    error.assign("GL_FRAMEBUFFER_UNSUPPORTED_EXT");break;
    }
    std::cout << "error: " << error << std::endl;
    return (false);
}

  • then you create the first fbo to the variable ‘fbo’ with the seconf one you overwrite the same variable, so you can not bind the first ever again…
  • you create and bind a texture for the second fbo. a multisample fbo can not have texture attachments only plain renderbuffers…

i attach my testcode for this kind of thing, i hope this helps you:


bool init_geometry_fbo()
{
    if (!init_geometry_textures())
        return (false);

    // create framebuffer object
    glGenFramebuffersEXT(1, &_fbo_id);
    if (_fbo_id == 0) {
        std::cout << "error generating fbo id" << std::endl;
        return (false);
    }
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo_id);

    // attach depth texture
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, _fbo_depth_id, 0);

    // attach color texture
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, _fbo_color_id, 0);

    unsigned fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
        std::cout << "error creating fbo, framebufferstatus is not complete:" << std::endl;
        std::string error;

        switch (fbo_status) {
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:  error.assign("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:             error.assign("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT");break;
            case GL_FRAMEBUFFER_UNSUPPORTED_EXT:                    error.assign("GL_FRAMEBUFFER_UNSUPPORTED_EXT");break;
        }
        std::cout << "error: " << error << std::endl;
        return (false);
    }

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    // create framebuffer object
    glGenFramebuffersEXT(1, &_fbo_ms_draw_id);

    glGenRenderbuffersEXT(1, &_fbo_ms_draw_depth_id);
    glGenRenderbuffersEXT(1, &_fbo_ms_draw_color_id);

    if (   _fbo_ms_draw_id       == 0
        || _fbo_ms_draw_depth_id == 0
        || _fbo_ms_draw_color_id == 0) {
        std::cout << "error generating fbo ms draw id" << std::endl;
        return (false);
    }


    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _fbo_ms_draw_depth_id);
    //glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_DEPTH_COMPONENT24,   _gl_widget->width(), _gl_widget->height());
    glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, 16, 8, GL_DEPTH_COMPONENT24,   _gl_widget->width(), _gl_widget->height());

    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _fbo_ms_draw_color_id);
    //glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_RGBA,                _gl_widget->width(), _gl_widget->height());
    glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, 16, 8, GL_RGBA,                _gl_widget->width(), _gl_widget->height());

    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo_ms_draw_id);

    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                 GL_DEPTH_ATTACHMENT_EXT,
                                 GL_RENDERBUFFER_EXT, _fbo_ms_draw_depth_id);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                 GL_COLOR_ATTACHMENT0_EXT,
                                 GL_RENDERBUFFER_EXT, _fbo_ms_draw_color_id);

    fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
        std::cout << "error creating fbo ms draw, framebufferstatus is not complete:" << std::endl;
        std::string error;

        switch (fbo_status) {
            case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:  error.assign("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:          error.assign("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:             error.assign("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT");break;
            case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:         error.assign("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT");break;
            case GL_FRAMEBUFFER_UNSUPPORTED_EXT:                    error.assign("GL_FRAMEBUFFER_UNSUPPORTED_EXT");break;
        }
        std::cout << "error: " << error << std::endl;
        return (false);
    }

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    return (true);
}

void use()
{
        // geometry pass
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo_ms_draw_id);
        {
            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

            render_geometry();
        }
        //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

        // blit contents into textures
        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, _fbo_ms_draw_id);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo_id);
        glBlitFramebufferEXT(0, 0, _gl_widget->width(), _gl_widget->height(),
                             0, 0, _gl_widget->width(), _gl_widget->height(),
                             GL_COLOR_BUFFER_BIT |
                             GL_DEPTH_BUFFER_BIT,
                             GL_NEAREST);

        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
}

but careful this code is ripped completely out of context and is from my testbed application.