About Gamma

not yet written

Gamma Support in POV 3.1

According to the POV official reference manual, §4.10.3,
Scenes that do not have an assumed_gamma global setting will not have any gamma correction performed on them, for compatibility reasons. If you are creating new scenes or rendering old scenes, it is strongly recommended that you put in an appropriate assumed_gamma global setting. For new scenes, you should use an assumed gamma value of 1.0 as this models how light appears in the real world more realistically.

So, the assumed_gamma in the scene file is supposed to be 1.0, not 2.2 like they are in all the examples including BASICVUE.POV. This is used for backwards compatability with old files, and as such is actually a bad example!

The scene file should contain an assumed_gamma of 1.0 so that the light behaves like it should, with respect to adding lighting effects together.

So that you can view the scene in the preview window, and generate an output file that can be viewed directly on a CRT, POV will gamma-correct the value after it is done tracing the ray. This value is what gets output and displayed. Specifically, each red, green, and blue component of each pixel is run through the formula output= pow ( original, assumed_gamma/display_gamma );, where pow is the C function that raises a value to a power. In other words, output = original γad.


A line like

should be added to the master POVRAY.INI file. This is set to your monitor's gamma. This value will normally be 2.2 for PC's, 1.8 for Mac's without gamma correction in the control panel, or 1.0 for a calebrated monitor or viewing program. I've found that my Trinatron monitor at work is too dark though, and gamma determination gives funny results.

The new scene file should contain, in part,

global_settings {
   assumed_gamma 1.0  /* "For new scenes, you should use an
      assumed gamma value of 1.0 as this models 
      how light appears in the real world more realistically." */

What it does

Here is a file that produces a ray-tracing of a γ-calebration plaque. This should be traced without anti-aliasing, at the largest size you can fit on the screen. Then view it from a large distance away, such as 20 feet, so that the bottom half looks like a solid color rather than a checkerboard (image available, traced with a Display_Gamma of 2.2).

You can customize the image in several ways.

It's interesting to note that the bar corresponding to a gamma of 1.0 (third from the left in this example trace) matches the checker pattern's overall brightness. This means two important things:
  1. POV did its calculations in a "natural" linear color space.
  2. The image has been gamma corrected to match this monitor.
This is exactly what we need.

What happened to the colors?

As soon as you trace a scene with assumed_gamma=1.0, you'll see a big difference! Here is a treatment of what happens and why and what you need to do about it.

POV Internals

There is a function to do gamma correction, and it performs value= pow (value,assumed_gamma/Display_Gamma);. The γ-calebration plaque image shows that this is indeed what happens to each pixel. When assumed_gamma is 1, this pre-un-adjusts the image data so when it's displayed on a monitor whose response curve has a gamma of Display_Gamma, it cancels out and the displayed image matches what POV "thought" when it traced it.

The comments in the code state that this transformation must be done exactly once for each pixel output (saved or displayed). However, what the code actually does when super-sampling (due to anti-aliasing or focal blur), is to gamma-correct each value before averaging them together. This does not make sense mathematically, since it means that an anti-aliased pixel that is post-processed to a different gamma will contain a different value than performing the average using the different Display_Gamma in the first place. This is contrary to the whole concept of this system. Either I'm missing something important here, or this is a bug.

Why bother?

In the old days, you would pick color values, finishes, and lights to make the scene look the way you wanted. This had overall gamma correction of the colors built in, but produced inaccurate results for summing light values such as when combining reflected and refracted rays with the primary pigment. But, you would just tweek the texture's values to get the effect you wanted. Reflection too light? Just increase the amount of reflection! Works to some degree, but light colors' reflections and dark colors' reflections would be adjusted by different amounts.

But all in all, nothing is measured scientifically (how many of you render a gray card or a light meter into the scene?), and when it looks right, it's done. The meaning of "accuracy" is not a concern.

I took this interest in getting the gamma done correctly because I want my image to be printed out on a high-end IRIS fine-art printer. On my home color printer, I just use an adjustment layer in PhotoShop to wash-out the colors before printing, to pre-compensate for the fact that the stuff prints darker than it looks on the screen. But when the printer is not on site, and when it costs hundreds of dollars to make a print, and the high-end system is calebrated, I intend to do it right.

Post-trace Gamma Correction

I can figure out my monitor's gamma here and use that for tracing, but the funny monitor at work needs a different value. And the printer needs a different value yet. I suppose I could trace it several times, but when it takes days to do a trace, that is not a good option. Also, I'm not going to trace a variety of values to suit everyone's needs. Instead, I'll trace it once and then post-process the image to change the gamma for each need, and let anyone else viewing it do the same.

With only 256 color intensities, and poorly chosen ones at that (too far between in the dark end, and uselessly clustered together on the bright end), transforming the image will lose something. That bothers me.

I was delighted to find that POV now supports 16-bit planes! Instead of each component being output as a value from 0 to 255, you can get 0 through 65535. This means that remapping the values will not cause noticeable quantization errors, even in extreme cases (if done correctly).

Realistically, 10 or 12 bits is probably enough, but I've not experimented. Fewer bits will result in smaller files. For my current work, I'm just using 16.

Output Format

The file format of choice is PNG. This is supposedly "the future", designed for lossless storage, and is perfect for our needs here because these needs are among those that PNG was designed to address. And it appears that POV has embraced PNG.

The option +FN16 will tell POV to output a 16-bit-per-channel PNG file.

POV will store the Display_Gamma in the output PNG file. This means that people reading the file will know how it was rendered, and viewers are supposed to adjust to the local gamma conditions when displaying the file. Because you used more than 8 bits, this adjustment will not degrade the image. With properly implemented viewers with quality color mapping, a PNG file will look the same to everyone! No too dark/too light problems! And the service place that makes the print will do the same, since they know what the printer likes.

PNG, regardless of the number of bits, is a good choice for POV's output, as long as you don't use tools that can't handle it. It contains the same information (if not more) than BMP or TGA, but is compressed. Adding +FN16 to my POVRAY.INI file makes this the default output. This gamma calebration plaque is 2.3 meg in the original 24-bit Windows BMP file, and less than 0.2 meg as a 48-bit PNG. The native compression offsets the increase in bits per pixel, in this case, by an extreme amount. But it gets better.

POV does not write a very efficient PNG file. Using my PNGstat tool, I saw that the file has 77 individual IDATA chunks, totalling 191,147 bytes of image data. It only puts a little bit in each chunk, which incurs 76 extra chunk headers and checksums (77*12 bytes = 924 bytes), but doesn't use optimal encoding parameters. Loading and resaving in another program should produce a tighter file.

Thumbs Plus happily did so, producing a file that was a mere 21.3K, or 1/3 the size of the GIF! However, a closer look bears out the fact that it cheated. Without warning, it reduced the color depth from 16 bits/sample down to 8, throwing away half the data. PhotoShop 5, even though it supports 16-bit channels, did not actually read 16 bits/sample from a PNG file! Isn't that kind of missing the point?

Chromataphore will do a reasonable job at this, as it's part of the reason I'm writing it. But it's not anywhere near ready for prime time yet. The champion to date is a program called pngcrush, whose sole purpose is to reduce the size of a PNG file. The resulting file is reduced 23% from what POV wrote, or 147K. PNGstat shows that it contains 146,749 data bytes. Not bad for a file that losslessly stores a full 48 bit per pixel precision!