An Efficient and User Friendly Tone Mapping Operator
An Efficient and User Friendly Tone Mapping Operator
with the final value clamped to the interval [0,1] (a convention well assume throughout). Importantly, though, we wish to be able to give a shoulder to the curve shape, causing it to bulge upwards so that its steep at the beginning and shallow at the end, and forcing the high input luminances to bunch together near the output value 1. Indeed in the absence of a finite white point, wed like it to be asymptotic to the output value 1. A simple way to achieve this is to use a hyperbola with vertical and horizontal asymptotes lets say the function , where
This appears to have 3 degrees of freedom, but we also have two constraints, namely that the curve should pass through the points and . By applying these constraints we can eliminate any two of . If we choose to eliminate and we we can express in terms of the single parameter :
with
. The parameter controls the amount of blending between a straight-line curve and a
purely asymptotic curve, with 0 and 1 yielding the two extremes, and intermediate values providing varying amounts of shoulder to the curve. Well refer to as the shoulder strength parameter. Figure 1 shows a plot of function for a selection of values of ranging from 0 (the straight line) to almost 1.
0.8
0.6
0.4
0.2
10
Figure 1: the effect of varying the shoulder strength Note that the form of the function is just one linear polynomial divided by another, and remains so even with a change of origin. This is not intended to be an empirically correct function, merely one which gives the desired aesthetic qualities at little computational expense. To achieve a filmic look, as per [Hable 10], we need our curve to have not only a shoulder but also a toe a small region of opposite curvature at the bottom end. One simple way we can achieve this at little expense is by composing our curve piecewise out of two such hyperbolic sections. In this case, the key to the toes parameterization is to observe what happens to the shoulder curve when the shoulder strength is allowed to become negative. Figure 2 shows the function plotted for a range of shoulder strengths from zero downwards. The one with highest curvature has a shoulder strength of -300, and it becomes clear that the full range of curves is covered by the interval . If we wish our toe strength parameter call it to operate in the same manner as the shoulder strength , well need to remap the one interval to the other.
0.8
0.6
0.4
0.2
10
Figure 2: negative shoulder strength values can be remapped to toe strength values
we obtain
Note that is really the same function (or family of functions) as parameter instead of the shoulder strength one.
Now we wish to piece the toe and shoulder together into a single curve. Wed like to have control over where along the horizontal axis the toe gives way to the shoulder, so well introduce a new parameter , the cross-over point. If well choose the toe, and if well choose the shoulder. At we can pick either one, assuming weve joined the curves correctly. Since some work will be required to formulate new toe and shoulder curves to satisfy the new requirements, this is a good time to introduce our final parameter, the black point, which well denote by . This plays the complementary role to the white point: input values at or below are mapped to zero. The black and white points are closely analogous to the near and far depth values used in mapping camera-space depth to [0,1] depth buffer values. The introduction of comes at no additional run-time cost, since it doesnt change the functional form of the curves merely the coefficients. Lets therefore assume that our final toe and shoulder functions, which well name and respectively, will each take the form of one linear polynomial divided by another, with coefficients to be determined in terms of the parameters . The functions must satisfy the following constraints: the toe must pass through the black point; the shoulder must pass through the white point; the toe and shoulder must coincide at the cross-over point.
A simple procedure for satisfying these constraints is to appropriately scale and translate the functions and from earlier for example requires its input range to be remapped from [0,w] to [b,c], and its output range to be remapped from [0,1] to [0, ]. There is one remaining unknown to be found, namely the value of both functions where they join, say . In order to find the value of we impose one additional constraint: the toe and shoulder must join with tangent continuity.
The algebra is left for the reader, but the resulting value is
and the resulting expressions for the toe and shoulder functions are
Figure 3 shows a sample curve, with . In practice the black point and cross-over point would typically be set considerably lower; the values were chosen here to show the salient features of the curve.
0.8
0.6
0.4
0.2
10
Expressing this curve in a pixel shader is a simple matter of using a remapping function such as the following:
float Remap(float x) { float4 coeffs = (x < cross_over_point) ? toe_coeffs : shoulder_coeffs; float2 fraction = coeffs.xy * x + coeffs.zw; return fraction.x / fraction.y; }
The division by average luminance, needed as a first step to normalize all pixel values, can be made costfree by baking it into the constants in the Remap() function shown above.
Notes: 1. Depending on taste, it might be preferable to apply a non-linear mapping upfront to the and intervals, to counteract their rather sluggish onset. 2. If better filmic accuracy is desired, a linear section of the curve can be spliced between the toe and shoulder, requiring the use of a second cross-over point. The calculations are left to the reader.
Bibliography [Hable 10] Uncharted 2: HDR Lighting, John Hable, GDC 2010 presentation. [Reinhard 02] Photographic Tone Reproduction for Digital Images, Erik Reinhard et al, Proc. SIGGRAPH 2002.