DX11 DirectCompute Global Operator Photographic Tonemapping
I wrote a DX11 DirectCompute implementation of the famous global operator photographic tonemapping algorithm developed by Erik Reinhard, Mike Stark, Peter Shirley and Jim Ferwerda. My code is based on the original code provided by the authors at http://www.cs.utah.edu/~reinhard/cdrom/. As in the sample source, my implementation performs tonemapping on luminance values in the Yxy color space. The DirectCompute portion of my source code is heavily based on the HDRToneMappingCS11 sample provided in the DirectX SDK.
Eric Rheinhard’s homepage for the photographic tonemapping algorithm. The homepage contains links for the paper “Photographic Tone Reproduction for Digital Images”, sample source code and HDR images in .hdr format.
The free DirectX10 programming book “Programming Vertex, Geometry, and Pixel Shaders” hosted by gamedev.net contains a chapter on HDR rendering that includes an explanation and implementation of Rheinhard tonemapping. The chapter is written by ex-Rockstar Games graphics programmer and GPU Pro (used to be called ShaderX) book series editor Wolfgang Engel (http://diaryofagraphicsprogrammer.blogspot.com/) and it’s really good.
gamedev.net forum discussion on implementing the Rheinhard tonemapping operator.
t-pot.com’s DX9 implementation of the Rheinhard global tonemapping operator. Tonemapping calculations are performed on luminance values in YCrCb color space.
HDR Cornell Box
The left image is an HDR Cornell box render image without tonemapping. The right image is the same image with tonemapping enabled. Without tonemapping, the HDR values all get cut off at 1, resulting in bright washed-out areas. With Rheinhard tonemapping enabled, the HDR image gets re-mapped better to an LDR range and colors in the bright HDR sections of the image are visible.
LDR Cornell Box
The above is a test of what happens when you tonemap an LDR (max luminance less than 1) image. The right image has the maximum luminance burnout control disabled. The left image has the maximum luminance burnout control enabled. Notice that the left image is brighter with greater contrast. When the maximum luminance burnout control is enabled, the resulting tonemapped values are not forced to burnout into 1 (they can exceed 1 and simply get clamped to LDR during the framebuffer write) and a stronger contrast can be brought out.
HDR Outdoors Scene
The left image is an HDR outdoors scene without tonemapping. The middle image is the same image with tonemapping enabled. The right image is the same image with tonemapping enabled but burnout control disabled. Unlike with LDR images, the effect of the burnout control is not very strong in HDR images when the maximum luminance is used for the value.
The above HDR images were obtained from http://www.cs.utah.edu/~reinhard/cdrom/hdr.html.
1) Compute Shaders Log Average Luminance & Max Luminance Calc Step
The above is the equation for calculating the log average luminance of the scene. In Rheinhard’s tonemapping paper, it’s regarded as an approximation to the scene’s key. In DX9, this value is calculated in the pixel shader by downsampling into a 1×1 texture. In my DX11 implementation, the log average luminance is calculated in the compute shader. In addition to the log average luminance, the maximum luminance of the scene is calculated as well. In my demo, this computation is performed in the same fashion as the HDRToneMappingCS11 sample provided in the DirectX SDK. Each thread group in the ReduceTo1DCS.hlsl compute shader outputs its log average luminance and the maximum luminance into a single texel in a 1D texture. Then this 1D texture is processed multiple times in a different compute shader at 128 thread threadgroup chunks until a single log average luminance and the maximum luminance value remains.
2) Pixel Shader Tonemapping Step
In the tonemapping pixel shader, using the above equation, luminance values are adjusted so that the log average luminance is mapped to the middle-grey value.
Then this adjusted luminance is scaled to a LDR displayable range using the above equation. This last equation has the characteristic that high luminance values are compressed more than the lower luminance values. value is set to the maximum luminance calculated in the compute shader. The tonemapping paper says that using this maximum luminance adjustment increases contrast for LDR (max luminance < 1) images (look at the Figure 6 graph plots in the paper to be convinced.)
Input HDR images are assumed to be in linear space and all tonemapping calculations are done in linear space. No pixel shader gamma correction is performed during write because an SRGB framebuffer is used.
Source Code & Binary
The HDR images included in the demo were obtained from http://www.cs.utah.edu/~reinhard/cdrom/hdr.html. Since D3DX11CreateTextureFromFile is unable to handle .hdr files (looks like the DX9 equivalent handled .hdr file types), I used the DirectX Texture Tool included in the DirectX SDK to convert the .hdr files into .dds files. The DirectX Texture Tool was unable to load some of the sample .hdr files. For those files, I opened and re-save them as .hdr files using the nice, free http://www.hdrlabs.com/picturenaut/. After they are re-saved using Picturenaut, I was able to load the .hdr files into the DirectX Texture Tool.
Man, spent a lot of my weekend writing this blog post. Think I’m going to work on the Japanese translation during the weekday. Going to finishing my weekend by grabbing dinner and playing a little bit more of Battlefield Bad Company 2 (which has been a lot of fun so far.)