Trying to implement Anti-aliasing on top of deferred shading, I'm attempting to use multi-sampled render buffers and then resolve the samples with a buffer-blit pass.
As is traditional in deferred shading, I am rendering the scene with a dedicated shader issuing 3 color outputs:
- Positions
- Normals
- Diffuse & specular
Those are then used in a lighting-computation pass resulting in the final scene texture
The scene texture is rendered to the screen on a full-screen quad using a simplistic shader
As you probably guessed, the on-screen MSAA is not applied to the content of scene texture when being rendered to the screen: to achieve anti-aliasing I thus chose to use multi-sampled render buffers in step 1) and introduced an extra step 1.1) for resolution. Of course the multi-sampling is only necessary/useful for the color map, not the other 2 maps.
My problem & question is that apparently, a frame buffer with multiple render-buffers/color attachments can only be defined for the same types of attachments ; meaning that if one attachment is multi-sampled then all others must be.
This becomes a problem for Positions and Normals buffers during resolution, because the geometry and lighting is affected as a result of anti-aliasing.
- Is my understanding valid regarding frame buffer attachments?
- Is there a way around it, in order to still have multi-sampling on the Diffuse&Specular map, but not affect the other maps?
// Create the frame buffer for deferred shading: 3 color attachments and a depth buffer
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
{
// - Position color buffer
glGenRenderbuffers(1, &gPosition);
glBindRenderbuffer(GL_RENDERBUFFER, gPosition);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gPosition);
// - Normal color buffer
glGenRenderbuffers(1, &gNormal);
glBindRenderbuffer(GL_RENDERBUFFER, gNormal);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, gNormal);
// - Color + specular color buffer
glGenRenderbuffers(1, &gColorSpec);
glBindRenderbuffer(GL_RENDERBUFFER, gColorSpec);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, gColorSpec);
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);
// - Generate the depth buffer for rendering
glGenRenderbuffers(1, &sceneDepth);
glBindRenderbuffer(GL_RENDERBUFFER, sceneDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_DEPTH_COMPONENT, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sceneDepth);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Create a frame buffer with 3 attachments for sample resolution
glGenFramebuffers(1, &gFrameRes);
glBindFramebuffer(GL_FRAMEBUFFER, gFrameRes);
{
glGenTextures(1, &gPositionRes);
glBindTexture(GL_TEXTURE_2D, gPositionRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPositionRes, 0);
glGenTextures(1, &gNormalRes);
glBindTexture(GL_TEXTURE_2D, gNormalRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormalRes, 0);
glGenTextures(1, &gColorSpecRes);
glBindTexture(GL_TEXTURE_2D, gColorSpecRes);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gColorSpecRes, 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ...
//
// Once the scene is rendered, resolve:
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gFrameRes);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glReadBuffer(GL_COLOR_ATTACHMENT1);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glReadBuffer(GL_COLOR_ATTACHMENT2);
glDrawBuffer(GL_COLOR_ATTACHMENT2);
glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
The result of the code sample above is that lit object's edges are showing artifacts of inappropriately dark/black or bright/white pixels, presumably because their position and/or normal have been altered in the process.