They are really good looking games. Another thing they have in common is that they care about screen and texture gamma and that is part of the secret to why they are looking so good.
Before starting here's a short disclaimer:
- I have no ambitions to explain things in a physically accurate way I try to give an overview of how things works, check the references for deeper information on the subject.
- There is already much written about this topic on the internet but my impression is that there are still too many people out there disregarding gamma so there is room for another post.
- In this post I'm mainly going to use monochrome values as opposed to colors. I also use normalized intensities between 0 and 1. If you are more used to think of intensities as between 0 and 255, you have to divide by 255 before applying anything of what is written in this post.
What is Gamma?
On a CRT screen the relationship between the pixel intensity and the output on the screen looks like this:Where:
is the intensity emitted by the screen.
is the intensity value stored in the framebuffer.
is a constant, it's read out as gamma.
This means there is a non linear relationship between what's in the framebuffer and what's being shown on the screen. The most common gamma used is 2.2 but it varies roughly between 1.5 and 2.5. Gamma is also present in flat screens for backward compatibility.
How does this affect the typical developer?
If you write a color with intensity .5 in the framebuffer of the graphics card, the relative intensity of what's being shown is actually proportional to .5γ. For gamma 2.2 this means the actual output intensity value is somewhere close to 0.218! Does this sound a bit scary? The actual intensity you see is less than half of what you just wrote in the framebuffer!So why doesn't this mean there are all kinds of strange non linear effects in for instance color ramps created in Photoshop or similar tools?
The secret lies in how the human eye responds to light. Somewhat simplified it can be described like this:
Where:
is the percieved intensity of the pixel
is the actual intensity.
is a constant roughly around .5 which happens to be close to 2.2-1. Some people prefers to use a different name for this constant than gamma since it's not related to non linearity in CRT screens.
This means that the eye responds to lighting roughly inverse of how a CRT emits light. The more intuitive explanation to this is that we see darker colors as brighter than they are or that we tend to see more details in darker colors. The human vision system basically compensates the non linearity of the screen. A linear ramp in the non linear gamma space looks linear when looked at. A linear ramp in intensity space looks like it's non linear. Look at the images below to see the difference.

Linear ramp in gamma space

Linear ramp in intensity space
Note how much faster the ramp in intensity space goes bright, darker intensities are percieved as brighter by humans.
How does this affect the rendered results?
First notice that all images below appears to be brighter when taking gamma in consideration. Artists tends to need to compensate for this manually and often end up with more ambient lighting and fill lighting than necessary when working in an environment where gamma is disregarded.
Gamma not considered

Gamma considered
Note how the shading transition on the ball looks sharper when taking gamma correction in consideration, the shadow border is also harsher. These transitions very much match the differences in the ramps showed above. Check out John Hable's presentation in the references for a real life example of that this is the correct behavior.

Gamma not considered

Gamma considered
Note how the region where the two light sources overlap looks too bright in the rendering where gamma wasn't considered.

Gamma not considered

Gamma considered
Note how the shadows are much more detailed and appears brighter in the rendering where we considered gamma.
How does this affect a game studio?
Let's say you create an image using a paint program or choose a color with a color picking tool, then you actually see what you are doing and the content will be authored for the particular gamma curve of the screen. The image and selections are made using the gamma encoded colors and the image shows correctly on your screen. It's when you start doing calculations on colors things will go wrong.
Calculations should be done on real intensities rather than on intensities authored for a screen with a gamma ramp. We often refer to colors without the gamma encoding as linear colors since in that color space the math is linear. Multiplication of gamma encoded colors is fine:
Addition is not okay though:
This means that you are introducing errors every time you do addition on colors selected by looking at the screen unless you take gamma in consideration!
A reasonable question is why we don't always store colors in linear space if it gets the math right out of the box. Current hardware should be able to compensate for the non linearity of CRT screens?
The reason is that you want to make the best use of the bits in the image. Remember what I wrote about the human eye and how it's more sensitive to differences in darker colors? This is why you want to use an encoding with gamma in the image. It means you get better precision in the darker intensities which mean you are less likely to get banding if you use gamma encoding for your colors. You can always go for a pixel format with more bits per pixel if you prefer everything to be linear, but it's a luxury most games can't afford.
What actual benefits do I get from caring about Gamma?
The main problem with not taking gamma in consideration is that your lighting will be less correct. It means artists needs to work more and do more tweaking to get good results. You will also get colors shifting, too dark shadows and generally a less realistic look. Realism is not a goal by itself, but this is an error in the interpretation of colors. If you want a non realistic stylish look there are better tools to accomplish it than introducing a fundamental flaw in how you interpret and compute colors.How do I fix my Content and Rendering Pipelines?
- Take control of what encoding your images, colors and framebuffers have.
- When doing math on colors, typically inside a shader, make sure you convert all colors to the linear color space and do your math there using high precision numbers.
- When storing your colors to the framebuffer or some other lower bitrate device, gamma encode your colors in order to make sure it's shown correctly on the screen and you save the precision in the darker part of the colors.
Let's look at these different steps in some more detail.
What encoding does my colors and images have?
Most likely something very local for your computer. A good guess is gamma 2.2 which most screens tend to stay around. sRGB is another standard that is commonly used. It's close to gamma 2.2 with some special treatment of the lowest part of the curve.Modern graphics hardware has support for doing gamma to linear conversions directly in the texture fetch. Some uses sRGB, others simpler approximations. In order to take advantage of the hardware support it is a good idea to configure your tools to use the same curve as the hardware. If you don't have hardware support for gamma to linear conversion, it's popular to encode the images with gamma 2.0 since it means you can convert your data to linear with a multiplication instead of a power computation (x2 = x*x).
Adobe Photoshop has support for custom gamma ramps and encoding them in images. However be aware that not all viewers respect this kind of information and may interpret the images incorrectly. It may be better to work in sRGB and introduce the custom gamma coding as a cooking step between the artist and the game content.
For colors selected with color pickers, a good guess is to expect the user selected colors have gamma 2.2 or sRGB. An important thing is to try to separate color from intensities when working with colors that can exceed 1. This is something you should do regardless if you use gamma or not. As an example most color selection controls show an RGB color of (10.5, 1.1, 1.1) as completely white even if it's obviously red. The problem is that the color selection control clamp everything above 1 when showing the color. The other reason is that gamma is not defined outside the range 0-1. Doing gamma to linear conversion of the color mentioned above would cause some strange and extreme results. The right thing to do is let the artist select the color and assume it's in sRGB or gamma 2.2. When doing the gamma to linear conversion you first convert the color to linear and then multiply with the intensity to avoid worrying about components above 1. This approach will save the color tint selected and give it correct intensity.
How do I make sure I have linear colors when making computations?
If you use hardware gamma to linear for texture fetches, this part is pretty straight forward, just use the colors directly.If you don't have hardware support for this or use a different ramp, you will have to convert your fetched data manually. You should be aware of that bilinear and trilinear filtering is done before you can touch the data, i.e. in gamma space. Since filtering includes math that goes wrong in gamma space it may introduce artifacts, in particular in high contrast borders. The actual conversion is often as easy as raising each component to the power of the gamma you use or squaring the color components if you happen to use gamma 2 instead of something more complicated.
How do I know that I store the right thing in the framebuffer?
First of all, values above 1 should generally not be gamma encoded directly. When it comes to storing colors in a framebuffer it's no big deal though. Values will be clamped when writing them to the framebuffer anyway and values above 1 will stay above 1 and values below 1 will stay below 1 when gamma encoding them.If your hardware supports gamma encoding when writing, you are done. Just store your linear value and it will be gamma encoded automatically.
If it doesn't you need to do the gamma encoding yourself. Using a gamma of 2.2 or sRGB are probably the best bets of gamma choice unless you know more about the target monitor. The back conversion is done by a component wise power to gamma-1.
Final Thoughts
Gamma applies to all stages stages involving math on colors in a game. Some other examples are Mipmap generation the Post Process Chain.Check out the references for deeper and more platform specific details on gamma correction.
Further Reading
Picture Perfect Gamma through the Rendering Pipeline, Steve Smith, Microsoft. This presentation goes through the subject in detail and also has some best practices for XBOX360 and DirectX.Uncharted 2, HDR Lighting, John Hable, Naughty Dog. Another presentation of the subject with lots of good images.
Gamma Correction, Technical Memo, Alvy Ray Smith. An old explanation from a graphics programming veteran.
Frequently Asked Questions about Gamma, Charles Poynton. A more theoretical document on the subject.
Wikipedias page on Gamma Correction, As always, Wikipedia gives useful information.
0 comments:
Post a Comment