“Gamma Correction” and “Gamma Correction”


Hi everybody. It’s been a while since my last post, mainly because I’ve left Naughty Dog and started my own company. Suffice it to say, I’ve been very busy and feverishly coding. Don’t have anything to say about it right now other than it’s going to be cool and it’s not going to be a game. Btw, U3 is going to be crazy. And yes, I need to fix that image on the right.

Back on topic, one issue that always confused people is the terminology around gamma correction. Sometimes, when I’m talking about the issue, someone will say “It doesn’t matter because people have poorly calibrated TVs?” The answer is “Wrong Gamma Correction”. This kind of confusion happens all the time because we have two completely separate problems in CG/Video Games and they are both called “Gamma Correction”.

Problem 1: Gamma Correction (Linear Space Lighting)
This problem is making sure that all your lighting calculations are done in the correct space internally. You fix this problem by “Gamma Correcting” your textures in your shader. I.e. convert from sRGB to linear. And then at the end of your lighting calculations, you have to convert from linear back to sRGB, which is what we usually assume your output color profile is.

This problem is HUGE. If your goal is photoreal-ish visuals and you do not solve this problem then you are screwed. You MUST deal with this problem. In this case, you can actually think of Gamma Correction as a compression problem. If we stored data in linear-space, we would need 16 bits per channel instead of 8 for simple diffuse textures.

Problem 2: Gamma Correction (Monitor Calibration)
Most people have horribly calibrated TVs. They are getting better, but we took a big step backwards when the top-of-the-line TVs went from CRTs to LCDs. Most LCDs are way too contrasty. So, there is this problem of “Gamma Correction” where we are trying to calibrate the TV to match the color profile that we expect, which is usually sRGB. That’s what the “Gamma Correction” slider in a game generally does.

This problem includes issues like the NTSC vs. PAL standards, the horrible Xbox 360 PWL gamma curve, etc. But for most end users it’s not a huge deal and there really isn’t a good solution. If you let this one slide, I don’t blame you.

So those are the two problems. Problem 1 is SUPER DUPER IMPORTANT. When I talk about gamma, that’s usually what I’m referring to. It doesn’t matter how bad your end-user’s TV is. You STILL have to do your lighting in linear space which means gamma correcting your textures/framebuffer on read and write.

Problem 2, well, do the best you can but don’t beat yourself up over it.

9 Responses to ““Gamma Correction” and “Gamma Correction””

  1. “If your goal is photoreal-ish visuals and you do not solve this problem then you are screwed. You MUST deal with this problem.”

    I’d go farther, and say you care even in the case of NPR if you are performing any lighting calculations whatsoever. Even if you’re going for a non-photo real look, lack of gamma-correct lighting is almost certainly not it :)


  2. Sorry I didn’t get this part: “If we stored data in linear-space, we would need 16 bits per channel instead of 8 for simple diffuse textures”.

    Why is 16 bits per channel in linear space? Could you be more specific please?


  3. Nick P: In general I’d agree with you. I’m just feeling generous today. (-:

    MacPhisto: The consensus I’ve heard is that if you want to store colors in a way that the human eye can’t perceive banding, you would need 9 bits in sRGB space but 14 bits in linear space.

    This happens because the human eye has much more precision in the dark areas than the light areas. Since the Gamma 2.2 curve has more codes in the blacks and fewer in the whites, you can get away with fewer bits. But if you store data as linear, you need much more overall precision to make the blacks look non-banded.

    Btw, those numbers are estimates and often argued about. Some color purists would argue that 16 bits is not actually enough if you store the data in linear.


  4. The steepest part of the sRGB curve has a slope of 12.92, so when you transform the smallest non-zero gamma value to linear space, you’re left with (1/255)/12.92. So on an interval with fixed precision you’d need values ranging from 0 to ceil(255*12.92) which is less than 4 bits of additional precision. I argue you could get fairly equivalent results with only 4 more bits, hence a 12-bit linear encoding. In fact, I have a lookup table that maps from linear values to sRGB values, and it has to be 4096 entries long (assuming powers of two) in order to fully map onto every sRGB value.


  5. James:

    I wish I could find the link for why sRGB is 9 and linear is 14. It may have been that Gamma 2.2 is 9 bits, and that sRGB is actually 10. For sRGB, 8 is definitely not enough. On most monitors you will see banding in the dark end.

    The reason why I said linear would require 16 is because with the formats that we currently have on graphics cards, that’s the next value that you could go to. There is no 12-bit format, unless I’m missing something. Of course, you would never do that, because if you needed slightly more precision, you should probably go 7e3, unless you are on PS3, in which case you would have to go fp16/int16.

    As a side note, most SLRs these days store RAW files in linear data with 12 or 14 bits. They get converted to 16 in software. Of course, I wasn’t planning on going that deep into bit-depth when I was originally writing the post. (-:


  6. Not directly related to the post but I’ll ask anyway :) : Have you tried to use premultiplied alpha with Gamma ? I guess the problem is not impossible to solve, but is still tricky, and I was wondering if you tried to solve thi problem as well !

    Thanks, and congrats for your new job !


  7. Hi Ben. Don’t think I’ve ever tried to store pre-multiplied alpha in gamma-space. When using input textures for blending, I don’t think I’ve ever pre-multiplied them. And when accumulating the results I’m a big fan of an fp16 framebuffer.


  8. I’m not disputing that you probably need more bits to completely eliminate banding. My comment was mostly to illustrate that when compressing from linear down to sRGB you need to start with about 4 more bits of precision. On the PS3 we usually set our display buffer to be 8-bit (sRGB), and when working with linear HDR values beforehand, you must use a 12-bit lookup table to completely map onto all 256 potential values. Obviously you don’t need a lookup table–you could do the math explicitly–but it was my other evidence for needing 4 more bits. I wish we could just leave our colors in linear FP16 and let the hardware transform to the display’s color space. Perhaps next gen?


  9. Gotcha. Good point on the lookup table. That’s how we actually did the conversion from linear to 8-bit in U2. We had a good old table of 4096 entries.


Leave a Reply