How do I render with HDR and antialiasing at the same time ?

Question


«I'm writing a 3D application using Direct3D9 and I've one of those new GeForce 8800 that supports multisampling with floating point targets. So I know how to render with anti-aliasing and a regular ARGB8 target. I know how to render with HDR with a floating point target. But I can't figure how to combine the two. What are the steps ?»

Answer


It's not that much more complicated..

Direct3D 9

Here's your pseudo d3d code (all targets listed here have same dimensions !):

// Init
CreateDevice(backbuffer and frontbuffer both called targetA, 
    multisampletype_none, absolutely no automatic depth stencil !).

CreateDepthStencilBuffer(targetZ, Z24X8/Z24S8, 
    multisampletype_2x_4x_8x, quality level 0-7); // <- no Z16 please

CreateRendertarget(targetB, A16B16G16R16F, 
    multisampletype_2x_4x_8x, quality level 0-7 (they must match above !)) 
    // <- this is not a texture !

CreateTexture(textureC, Rendertarget, pool_default, 
    A16B16G16R16F, multisampletype_none); // <- important, see below

// Render frame 0

BeginScene();

// First draw the multisampled scene

SetRenderTarget(targetB);
SetDepthStencilSurface(targetZ);
Clear(both);
RenderScene(); // <- hdr scene as you do usually

// Then downsample the multisampled to the normal buffer with a blit

StretchRect(From targetB to texture C); 
    // <- no rectangle, matching dimensions please

// Then do the tone mapping pass

SetRenderTarget(targetA);
SetDepthStencilSurface(NULL); // <- important

Clear(color); // optional since you're stomping it just below

DrawFullScreenquad(with texture C); // tone mapping pass with a dedicated shader. 
    // This should be more complicated if you have bloom or other effects obviously.

RenderInterface(); <- don't tonemap or multisample your interface.

EndScene();

Present();

// Goto Render Frame [...]

// After we're done we release everything.

OpenGL

The method is almost the same for OpenGL, though it is somehow complexified by the heavy use of OpenGL extensions (so double check those extensions are available on your graphics card).

// Init
GLenum format = GL_RGBA16F_ARB;
int samples = 4; // for example

GLuint multisampledFloatFBuffer;
glGenFramebuffersEXT(1, &multisampledFloatFBuffer);
glBindFrameBufferEXT(GL_FRAMEBUFFER_EXT, multisampledFloatFBuffer);
GLuint multisampledRenderBuffer;
glGenRenderbuffersEXT(1, &multisampledRenderBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, multisampledRenderBuffer);
glRenderbufferStorageMultisampleEXT(
    GL_RENDERBUFFER_EXT, samples, format, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
    GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, multisampledRenderBuffer);

GLuint multisampledDepthBuffer
glBindFrameBufferEXT(GL_FRAMEBUFFER_EXT, multisampledFloatFBuffer);
glGenRenderbuffersEXT(1, &multisampledDepthBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, multisampledDepthBuffer);
glRenderbufferStorageMultisampleEXT(
    GL_RENDERBUFFER_EXT, samples, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
    GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, multisampledDepthBuffer);

GLuint normalFloatFBuffer;
glGenFramebuffersEXT(1, &normalFloatFBuffer);
glBindFrameBufferEXT(GL_FRAMEBUFFER_EXT, normalFloatFBuffer);
GLuint textureFloat;
glGenTextures(1, &textureFloat);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, textureFloat);
glTexImage2D(
    GL_TEXTURE_RECTANGLE_NV, 0, format, width, height, 0, 
    GL_RGBA, GL_INT, NULL);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
    GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_NV, textureFloat, 0);

// Render frame 0

// First draw the multisampled scene

glBindFrameBufferEXT(GL_FRAMEBUFFER_EXT, multisampledFloatFBuffer);
Clear(both);
RenderScene(); // <- hdr scene as you do usually

// Then downsample the multisampled to the normal buffer with a blit

glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, multisampledFloatFBuffer); // source
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, normalFloatFBuffer); // dest
glBlitFramebufferEXT(
    0, 0, width, height, 0, 0, width, height,
    GL_COLOR_BUFFER_BIT, GL_LINEAR);// <- matching dimensions please

// Then do the tone mapping pass
glBindFrameBufferEXT(
    GL_FRAMEBUFFER_EXT, 0); // unbind all (render to back buffer)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, textureFloat);

DrawFullScreenquad(); // tone mapping pass with a dedicated fragment program. 
    // This should be more complicated if you have bloom or other effects obviously.

RenderInterface(); <- don't tonemap or multisample your interface.

Present(); // swap buffers 

// Goto Render Frame [...]

// After we're done we release everything.

As you can see the idea is pretty similar. Note that the code has been simplified, so ALWAYS check with your extension specification to make sure you understand every step.

Direct3D10

The situation is slightly different for Direct3D 10. Direct3D 10 allows you to specify a multisample type for textures. That means that you have the choice between calling ResolveSubResource() to explicitely copy from a multisampled resource to a non multisampled resource similar to what was done for Direct3D 9 and OGL before, or to do a custom resolve pass by reading directly the individual samples in a pixel shader via the load() instruction on a multisampled texture.

There may be more to say about the quality of anti-aliasing that you will be able to get and what tone mapping operator to use. But that should answer your question for now.


If you want to learn more..




Partner websites : LEGREG | GRAPHICS | GRAPHISME | PHOTOGRAPHY | OUT OF MY MIND | ANIMATION MENTOR | GREEN LIVING | VOXEL | RAY TRACING | DEALS