Dave Scherer explained to me why my grayscale calculations gave very
dark scenes. I've put his gamma-correction formula (the second one given
below) into the grayscale.py program found in the Contributed Programs
at http://vpython.py. Here are his comments:
-----------------------------------------
The problem is monitor gamma. I had forgotten that we had not
already addressed this issue.
For silly historical reasons, PC monitors have a "gamma" of
approximately 2.5. This means that framebuffer intensity values are not
proportional to the luminance of the monitor. For a color (r,g,b), the
monitor is actually emitting light energy approximately proportional to
(r**2.5, g**2.5, b**2.5). This means that (1,1,1) is not just twice as
bright as (0.5, 0.5, 0.5), but more than 5 times brighter!
This is not a huge problem for many of the things people do with
computers, because color values are chosen by hand anyway (so all that
matters is that everyone uses the same color space, which is very
roughly true across PCs. Historically, however, Macs have used a gamma
of 1.8 instead.) It is a big problem for 3D graphics, because it makes
*all* lighting and blending calculations nonphysical unless they are
specifically gamma corrected, which is expensive and can't be done in
hardware (except after the framebuffer, see below).
To apply this to your calculation, the color (1,0,0) is really being
rendered as (.009, 0,0) instead of (.15, 0, 0). That is indeed very
dark! It is possible to correct for this problem as far as your
specific calculation goes:
GAMMA = 2.5
s = ( .15 * r**GAMMA + .55 * g**GAMMA + .30 * b**GAMMA ) ** (1/GAMMA)
Or, with a different set of coefficients (the above is rather odd: blue
is definitely darker than red):
s = ( .299 * r**GAMMA + .587 * g**GAMMA + .114 * b**GAMMA ) ** (1/GAMMA)
I think you will agree that this last doesn't change the brightness of a
scene much. In red/blue (as opposed to red/cyan) mode, the brightness
*will* change a lot because the green channel, which is the most
important for luminance, is being lost. Unfortunately there is no way
to precisely compensate for this.
The above calculation doesn't solve all of the problems of nonlinear
gamma, which makes the vertex lighting, interpolation, antialiasing,
alpha blending (if we had any), and other color calculations inaccurate
in the same way. The correct thing to do is to correct the monitor
gamma by changing the color tables used by the video card to convert
framebuffer data to analog monitor signals. This method is used by
almost all full-screen 3D applications. The problem is that there is
(last I checked) no widely supported way of changing the gamma in a
single window, and changing the system gamma makes everything else on
the screen look washed out. This is probably why we haven't dealt with
this before.
If you want to experiment with gamma correction, there is probably a
slider in your video card properties that allows you to play with it.
You should also change scene.ambient, which defaults too high for gamma
correct rendering.
|