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


  • 1
    $\begingroup$ You probably also want to look into a bloom effect to simulate the glare of the sun. $\endgroup$ – Nathan Reed Feb 21 '18 at 18:28
  • $\begingroup$ @NathanReed She probably mean radial blur light shafts. $\endgroup$ – narthex Feb 21 '18 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);



| improve this answer | |
  • $\begingroup$ 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. $\endgroup$ – Maria Feb 21 '18 at 17:49
  • 1
    $\begingroup$ It sounds like you want to be implementing a bloom filter? $\endgroup$ – haasn Feb 21 '18 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.