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 ReedFeb 21, 2018 at 18:28
-
$\begingroup$ @NathanReed She probably mean radial blur light shafts. $\endgroup$– mdkdyFeb 21, 2018 at 18:40
1 Answer
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:
- A formula for mapping from $[0,\infty)$ to $[0,1]$.
- 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:
- https://github.com/haasn/libplacebo/blob/master/src/shaders/colorspace.c#L664
- You can also poke at ITU-R Report BT.2390-2 for some more ideas and guidelines.
-
$\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$– MariaFeb 21, 2018 at 17:49
-
2$\begingroup$ It sounds like you want to be implementing a bloom filter? $\endgroup$– haasnFeb 21, 2018 at 17:50