<p>First, a quick correction:</p>

<blockquote>
  <p>storing color values as one byte integers I can have a combination of 255^3</p>
</blockquote>

<p>You mean <span class="math-container">$256^3$</span>. There are 256 eight-bit integers (including zero), not 255. But here comes the more fundamental thing you need to understand.</p>

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

<p>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 <em>what</em> to store, because everything will end up as 8 bit colour channels.</p>

<p>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.</p>

<p>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 <span class="math-container">$(0, 0, 0)$</span> to <span class="math-container">$(1, 1, 1)$</span> in whatever representation you like (floats or normalized integers) and show it in place of your real computation. If you see <a href="https://en.wikipedia.org/wiki/Mach_bands" rel="nofollow noreferrer">Mach bands</a> 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 <em>is</em> 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.</p>

<p>However, there are techniques to make the differences invisible most of the time. Check out <a href="https://stackoverflow.com/a/12894053/967945">my answer explaining what sRGB is for</a> on SO for some examples.</p>

CC BY-SA 4.0
2019-03-19T01:40:40.540Z
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.
Zebrafish
CC BY-SA 4.0
2019-03-19T08:25:12.927Z
@Zebrafish That's exactly right.
Dan Hulme
CC BY-SA 4.0
2019-03-20T15:55:05.453Z
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.
ratchet freak
CC BY-SA 4.0
2019-03-20T16:50:39.487Z
@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"
Dan Hulme
CC BY-SA 4.0
2019-03-18T19:14:20.567Z
2019-03-18T19:14:20.567Z
Dan Hulme
<p>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.</p>

<p>I have a couple of question about this.</p>

<p>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?</p>

<p>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?  </p>

CC BY-SA 4.0
2019-03-18T14:17:14.597Z
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.
gallickgunner
CC BY-SA 4.0
2019-03-18T14:19:43.733Z
Also found an interesting site https://www.strollswithmydog.com/just-noticeable-difference-color/
gallickgunner
CC BY-SA 4.0
2019-03-18T16:03:51.523Z
Why only 255^3 and not 256^3 combinations?
Simon F
CC BY-SA 4.0
2019-03-18T16:07:48.740Z
RE your last paragraph - are you aware of the differences between non-linear, e.g. sRGB encoded (https://en.wikipedia.org/wiki/SRGB),  and linear colour? It might make a difference to your fade-out choices
Simon F
CC BY-SA 4.0
2019-03-19T09:52:16.367Z
@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.
Zebrafish
<color>
Some questions about working in 0-255 integers instead of single precision floating point colors
Zebrafish
# Some questions about working in 0-255 integers instead of single precision floating point colors

(Asked by: username_0 on 2019-03-18)

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?

**Tags:** color

> **Comments:**
> **username_1**: 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.  
> **username_1**: Also found an interesting site https://www.strollswithmydog.com/just-noticeable-difference-color/  
> **username_2**: Why only 255^3 and not 256^3 combinations?  
> **username_2**: RE your last paragraph - are you aware of the differences between non-linear, e.g. sRGB encoded (https://en.wikipedia.org/wiki/SRGB),  and linear colour? It might make a difference to your fade-out choices  
> **username_0**: @username_2 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.  

---

## 1 answers

### Answer by username_3 on 2019-03-18. Score: 4

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](https://en.wikipedia.org/wiki/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](https://stackoverflow.com/a/12894053/967945) on SO for some examples.

> **Comments:**
> **username_0**: 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.  
> **username_3**: @username_0 That's exactly right.  
> **username_4**: 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.  
> **username_3**: @username_4 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"  

---

