I have many objects that store their color values. At the moment I'm storing them as vec4 values, that is four 4-byte values for RGBA, mainly because this is how the shader reads them. I was thinking of storing these colors as a vector or 4 unsigned single-byte integers. Anyway, with storing color values as one byte integers I can have a combination of 255^3, which is 16,581,375.

I have a couple of question about this.

If I store the red color as 128, this ends up being a floating point value of about 0.502, and a color of 129 ends up being a floating point value of about 0.506. My question is whether there is any discernible difference to speak of, either consciously or unconsciously between the intensity of a color being 0.502 or 0.506, or shades in between. Does this depend on the limitations of your graphics adaptor or video display? Or is there simply no discernible difference that one could tell with the eye for a value in between those two?

Further, I have a similar inquiry about things like color changes over time. If for example we use a 0 to 255 value to do a fade to black over a significant period of time, would one integer jumps on the 0-255 scale in terms of brightness, which would result in about 0.004 in floating point on each change, be detectable as opposed to calculating the change in floating point to begin with?

  • $\begingroup$ For Question 1 you should try reading up on Weber-Fechner Law and Just Noticeable Difference. In short Afaik there is no set value for that. It depends on our perception. and we might be more sensitive to intensity shifts in some colors more than others. As for (2) I don't understand, 1 unit change in integer equals 0.004 change in the floating point. So whatever you do the change is same. If you are trying to ask how big of a change would be detectable then again that depends on the perception I guess. $\endgroup$ – gallickgunner Mar 18 at 14:17
  • $\begingroup$ Also found an interesting site strollswithmydog.com/just-noticeable-difference-color $\endgroup$ – gallickgunner Mar 18 at 14:19
  • $\begingroup$ Why only 255^3 and not 256^3 combinations? $\endgroup$ – Simon F Mar 18 at 16:03
  • $\begingroup$ RE your last paragraph - are you aware of the differences between non-linear, e.g. sRGB encoded (en.wikipedia.org/wiki/SRGB), and linear colour? It might make a difference to your fade-out choices $\endgroup$ – Simon F Mar 18 at 16:07
  • $\begingroup$ @SimonF Yes, I do, well sorta. Many times I've understood it and many times I've forgotten exactly which way it goes. I know that the gist behind it is that that our eyes perceive intensity in a non linear way, so twice the physical intensity isn't a twofold perception in brightness. So I think the point is to spread out the color space so more values are available within the lower end of the intensity (or was it higher). I definitely messed up when I loaded a normal map in sRGB instead of linear, so instead of the normal pointing straight out, 128, 128, 255, it went in a wrong direction. $\endgroup$ – Zebrafish Mar 19 at 9:52

First, a quick correction:

storing color values as one byte integers I can have a combination of 255^3

You mean $256^3$. There are 256 eight-bit integers (including zero), not 255. But here comes the more fundamental thing you need to understand.

If I store the red color as 128, this ends up being a floating point value of about 0.502

It doesn't end up being a floating-point value at all: it ends up as an 8 bit (or possibly 10 bit) integer on your display. Whatever extra precision you had in the floats to start with is thrown away when the final colour goes into the framebuffer. So the rest of what I'm going to say shouldn't influence your choice of what to store, because everything will end up as 8 bit colour channels.

Now that doesn't mean there's never any value in using a higher-precision number earlier on. More precision in the intermediate data means you'll get the full precision in your output format. e.g. if you start out with these normalized integers, and you multiply them all by 2 in the shader, you'll only end up with the even-numbered integers in your output: you lost half of your precision.

As for the question you really asked, about visible differences, it's easy to test that in live conditions. Just make a grey colour gradient from $(0, 0, 0)$ to $(1, 1, 1)$ in whatever representation you like (floats or normalized integers) and show it in place of your real computation. If you see Mach bands on your gradient, then yes, you can tell the difference between adjacent intensities. In general, for adults with normal sight and 8 bit-per-channel RGB in common lighting conditions, there is a visible difference in a grey gradient. That doesn't mean there's a visible difference between every adjacent pair: the eye is less sensitive to contrast in red and blue than in green, and less sensitive to changes in hue, so it strongly depends on the colours involved.

However, there are techniques to make the differences invisible most of the time. Check out my answer explaining what sRGB is for on SO for some examples.

  • $\begingroup$ I see. What I meant by ends up as floating point was that's how it ends up in the shader. But what you're saying is two things basically as I understand it. The framebuffer, when written to, stores values as integers in most cases anyway, so any extra precision from a 0-1 floating point in the shader is rounded off, but in any case at the end of the day your display (screen) displays intensities based on integer values anyway, correct? Edit: what I mean by integer values is discrete values of say 2^8 or 2^10 if it's 10 bit, as you said. $\endgroup$ – Zebrafish Mar 19 at 1:40
  • $\begingroup$ @Zebrafish That's exactly right. $\endgroup$ – Dan Hulme Mar 19 at 8:25
  • $\begingroup$ There are high dynamic range monitors that will use more than 24 bits per pixel, but it is debatable how much that is really needed. $\endgroup$ – ratchet freak Mar 20 at 15:55
  • $\begingroup$ @ratchetfreak Well, you can see more than 256 shades of grey, so it's needed if you want to display every visible colour. Also, if your monitor actually has more dynamic range, it needs more precision too; e.g. imagine only having 256 brightness values between "no light at all" and "as bright as the sun" $\endgroup$ – Dan Hulme Mar 20 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.