Iluvatar's picture

Multisampling Problem

Hi there, ihave a problem with multisampling.

In the beginning everything was well, that was when i was rendering the Scene to the default Framebuffer directly. Nice FSAA :) - yay....
But now i have some troubles, and I think i know why:

I implemented SSAO to my program and therefore i changed the rendering from something like that:

    ClearFrame();
    RenderAllShapes();

To Something like that:

    BindFrameBuffer();
    ///Set FrameBuffer rendering to two targets... (Color and ViewSpace-Normals)
    RenderAllShapesToFrameBuffer();
 
    BindAnotherFrameBuffer();
    CalculateAO();
 
    BindAnotherFrameBuffer();
    BilateralBlurAOTexture();
 
    MultiplyTexturesToScreen(); ///Using the Color-Texture from the begining and the Blurred AO-Texture from the last Pass

Since I render to a Texture multisampling is disabled by default - right?
My FrameBuffer used to render the first pass is initialized as follows:

        private static void InitializeFrameBufferDepthColorNormal(out int frameBufferID, out Texture shadowMap, out Texture colorMap, out Texture normalMap, Size sizeToUse)
        {
            ///First initialize the FrameBuffer Object
            GL.GenFramebuffers(1, out frameBufferID);
            ///Bind it to set it actual
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, frameBufferID);
 
            ///Initialize the ShadowMap and attach it to the FrameBuffer
            shadowMap = new Texture(PixelInternalFormat.DepthComponent32, PixelFormat.DepthComponent, sizeToUse.Width, sizeToUse.Height, "DepthMap");
            ///Attach the Texture to the FrameBuffer
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, shadowMap.ID, 0);
 
            ///Initialize the ColorTexture and attach it to the FrameBuffer
            colorMap = new Texture(PixelInternalFormat.Rgba, PixelFormat.Rgba, sizeToUse.Width, sizeToUse.Height, "ColorMap");
            ///Attach the Texture to the FrameBuffer
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, colorMap.ID, 0);
 
            ///Initialize the ColorTexture and attach it to the FrameBuffer
            normalMap = new Texture(PixelInternalFormat.Rgba, PixelFormat.Rgba, sizeToUse.Width, sizeToUse.Height, "NormalMap");
            ///Attach the Texture to the FrameBuffer
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment1, normalMap.ID, 0);
 
            ///Check the FrameBuffer for Completeness and Log ErrorMessage if it is not Complete
            FramebufferErrorCode code = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
            if (code != FramebufferErrorCode.FramebufferComplete)
            {
                Logger.SendToLog("FrameBuffer Creation with Depth, Color and Normal Attachment not Successful, Error: " + code.ToString(), LogType.Errors);
            }
 
            ///Unbind the Framebuffer again
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
        }

The Texture Constructor simply generates a Texture with the specified Dimensions and sets some TextureParameters...

Is it possible somehow to enable multisampling again? Maybe only for the colorMap in the Example?
I tried creating a MultiSample-Texture, but i always get an "FRAMEBUFFER_INCOMPLETE_ATTACHMENT" Error...
When i checked the creation of the Multisample-Texture there was no Error at all... and i also checked the IsTexture()-Query...

Is such a thing even possible?

Thank you!
Iluvatar


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

The way I handle this is by rendering to a multisampled renderbuffer and resolving that to a texture via GL.BlitFramebuffer.

I think that newer hardware supports rendering to multisample textures, but I've never tried this myself.

Iluvatar's picture

Wow Fiddler, thank you for your fast answer!!

But how can I do that in my situation? Replace the colorMap with a Renderbuffer?
And how can i blit that to a Texture afterwards?

Sorry, but i never used a renderbuffer :(

Thx :)

Iluvatar's picture

Ahhh, I can't mix my simple Textures (depth and normal) with a multisample Renderbuffer?
I see.... i start moving to the result I need :)

Thx

Edit:
Now i get an "FramebufferIncompleteAttachment", but when i only attach the Renderbuffer the Framebuffer is complete....

the Fiddler's picture

Creating a renderbuffer is really easy:

GL.GenRenderbuffers(1, out id);
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, id);
if (samples == 0)
{
    GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Rgba8, width, height);
}
else
{
    GL.RenderbufferStorageMultisample(RenderbufferTarget.Renderbuffer, samples, RenderbufferStorage.Rgba8, width, height);
}

You can now attach this to the framebuffer in place of a color texture. (You can also create depth and stencil renderbuffers by modifying the RenderbufferStorage parameter).

The limitation is that you cannot read from a renderbuffer directly, i.e. you cannot attach a renderbuffer to a sampler. What you need to do is blit the renderbuffer to a texture and attach that texture to your shader:

// you need two framebuffers
// the renderbuffer is attached to the first one
// the texture is attached to the second
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, source_fbo);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, dest_fbo);
GL.BlitFramebuffer(
    source.Left, source.Top, source.Right, source.Bottom,
    target.Left, target.Top, target.Right, target.Bottom,
    ClearBufferMask.ColorBufferBit, 
    BlitFramebufferFilter.Linear);

Check the FBO documentation for more information.

Edit: I don't know if you can render to attachments with different AA parameters. Try using renderbuffers for everything.

Iluvatar's picture

Well i changed everything to Renderbuffers but there is one thing I still don't understand....
How is it possible to Blit every Attachment of the Framebuffers??

I have 3 Attachments at the time, Depth and two Color Attachments, or isn't it possible at all?
So i have to use multiple FBO's and need to render to the different Attachments in seperate Passes?

Sorry, but I don't know how i can get the multisampled Renderbuffer-Data into the Textures!

Color Multisampling works :)

Thank you :)

Edit:

So i got it working thank you!
I needed to set the GL.ReadBuffer and GL.DrawBuffer explicitely for the different ColorAttachments :)
But the Performance is really slow now :( - but anyway Thank you very much - it was not so hard to do with your assistance! :)

the Fiddler's picture

You're welcome!

Iluvatar's picture

Do you have any Idea, why the Renderbuffer Version is slower so much?
I think the Render-part is slow, not the Blitting.... :(
Got any ideas? ;)

another edit:
It's not that bad after all.... should work... :)

the Fiddler's picture

Maybe the multisampling part is slowing you down?

Iluvatar's picture

Yes maybe, but as i tested now, the performance is absoluteley ok :)
Thank you. One Picture of the 16-sample version:
http://picasaweb.google.com/lh/photo/FmUnjSfgnLZTkvyHN9c8GwhKI7Z66J9z4Ad...

sgsrules's picture

Nice screen shot. I've been screwing around with multisampling quite a bit and if you're using multiple render targets i would highly recommend using Texture2DMultisample instead. I've found it to be faster than blitting and you can also do custom resolves. This way you can multisample your color buffer but leave your normal buffer alone. Anyhow here's some code:

Setup:

            colorBuffer = new int[1];
            dataBuffer = new int[1];
            depthBuffer = new int[1];
 
            // Generate multisampled textures
            GL.GenTextures(1, colorBuffer);
            GL.BindTexture(TextureTarget.Texture2DMultisample, colorBuffer[0]);
            GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Samples, PixelInternalFormat.Rgba16f, ScreenWidth, ScreenHeight, false);
 
            GL.GenTextures(1, dataBuffer);
            GL.BindTexture(TextureTarget.Texture2DMultisample, dataBuffer[0]);
            GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Samples, PixelInternalFormat.Rgba16f, ScreenWidth, ScreenHeight, false);
 
            GL.GenTextures(1, depthBuffer);
            GL.BindTexture(TextureTarget.Texture2DMultisample, depthBuffer[0]);
            GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, Samples, PixelInternalFormat.DepthComponent24, ScreenWidth, ScreenHeight, false);
 
            //create fbo
            GL.GenFramebuffers(1, MSAAFBO);
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, MSAAFBO[0]);
 
            // attach
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, colorBuffer[0], 0);
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment1, dataBuffer[0], 0);
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, depthBuffer[0], 0);
            FramebufferErrorCode stanEnum = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
 
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);

Usage:

 GL.BindFramebuffer(FramebufferTarget.Framebuffer, MSAAFBO[0]);
 GL.DrawBuffers(2, new DrawBuffersEnum[] { DrawBuffersEnum.ColorAttachment0, DrawBuffersEnum.ColorAttachment1 });
// draw pretty stuff
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);

Now you have to resolve the multisampled buffer. You do this with a shader:

#version 330
 
uniform sampler2DMS color;
uniform sampler2DMS data;
out vec4 fragColor;
out vec4 fragData;
in vec2 texcoordIn;
uniform int samples;
float div= 1.0/samples;
 
void main()
{
 fragColor = vec4(0.0);
 ivec2 texcoord = ivec2(textureSize(color) * texcoordIn); // used to fetch msaa texel location
 for (int i=0;i<samples;i++)
 {
  fragColor += texelFetch(color, texcoord, i);  // add  color samples together
 }
 
 fragColor*= div; //devide by num of samples to get color avg.
 
 fragData = texelFetch(data, texcoord, 0);  //I don't wan't to multisample my data buffer so i just pass the first center sample in.
}

just start your shader bind your buffers to the textures draw fullscreen quad and you're done.

Hope this helps. It would have made my life a hell of a lot easier if i would've been able to find this without doing some serious digging. All the info online is a bit dated and still uses the older msaa method.