Tone mapping bright images

I am rendering the sun in an image, and the sun comes out white and is basically very unrealistic. I have tried searching for tone mapping algorithms but I'm very lost. Can someone guide me to do this? or perhaps help me with the pseudo

• You probably also want to look into a bloom effect to simulate the glare of the sun. Feb 21, 2018 at 18:28
• @NathanReed She probably mean radial blur light shafts. Feb 21, 2018 at 18:40

There are multiple different ways to do tone-mapping, including both local and global methods. I won't discuss local methods (i.e. methods based on using surrounding information as part of the tone mapping process) here. Global methods can be broken down into two components:

1. A formula for mapping from $[0,\infty)$ to $[0,1]$.
2. A method of applying this to the color.

The simplest formula out there is just $out = in / (in + 1)$, but the results are also fairly boring. A fancier curve is the filmic tone mapping function developed by John Hable, which is popular in the video gaming industry:

float hable(float x)
{
float A = 0.15;
float B = 0.50;
float C = 0.10;
float D = 0.20;
float E = 0.02;
float F = 0.30;

return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}


You can also get even fancier if you can constrain the input domain to a known, fixed peak value.

As for how to apply this function to an actual color value, there are again multiple techniques. A very simple way is to do it per-channel, i.e. color.r = map(color.r); color.g = map(color.g); color.b = map(color.b);. But this has the downside of warping the appearance of bright colors towards white (e.g. orange becomes yellowish under tone-mapping). Some in the movie industry tend to like this look, for whatever reason.

To do color-preserving tone mapping, you can perform the calculation on a single value only and then apply the same scale to all three channels. A good choice for the value to use is to choose the “brightest” of the three components:

float sig = max(color.r, max(color.g, color.b));
color.rgb *= vec3(map(sig) / sig);


However, this approach also has downsides, namely that it takes very bright objects to unnaturally saturated versions of themselves. For example, a bright sun might suddenly appear various shades of yellow and orange - which also looks unnatural on photographic content.

So ultimately, what I do is first desaturate the color based on how bright it is, and then apply tone mapping after the fact, leading to an algorithm that looks something like this:

// Calculate the desaturation coefficient based on the brightness
float sig = max(color.r, max(color.g, color.b));
float luma = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
float coeff = max(sig - 0.18, 1e-6) / max(sig, 1e-6);
coeff = pow(coeff, 20.0);

// Update the original color and signal
color.rgb = mix(color.rgb, vec3(luma), coeff);
sig = mix(sig, luma, coeff);

// Perform tone-mapping
color.rgb *= vec3(map(sig) / sig);


But YMMV.

References:

• Thank you for your lenghty and detailed response. However the sun still looks unrealistic ( I attached an image in the description above). Is this a tone mapping issue? I want my sun to look more like a glow. Feb 21, 2018 at 17:49
• It sounds like you want to be implementing a bloom filter? Feb 21, 2018 at 17:50