I am making a planet generator, and when I go too close to the planet, the smoothly colored surface has striations. Very visible striations, as seen in the attached image. The color transition is supposed to be completely smooth, set with a double precision floating point (which, AFAIK, is actually overkill), but no matter how I generate it, the striations appear. I understand that there are only so many nuances of grey or other colors to go around, but if I place a grey shade of 140 (in a 0-255 range) next to one with 141 in any graphics program (e.g. GIMP), they are indistinguishable, meaning there should be no striation, as they would 'melt together'. I checked polygon topology, that's not it (if I set the color nuances to rotate, the striations do not follow polygons at all). I've tried upping the color depth to 32 bit, with no change. I even tried manually casting the resulting values as 'float' for the glColor3f assignment! Nothing works, the striations remain! Is there a limit on the nuances OpenGL can give to colors? Can I remove that? Or is there another explanation?
These are called Mach bands, they’re an optical illusion, commonly referred to as banding.
A simple technique to avoid banding is to use dithering (spatial or temporal, or both).
You can try with simple white noise dithering first: in your fragment shader, before outputting the final colour, add a small random number such as
(rand() - 0.5) / 255 to each of its components. If you do not have a random function available, you may try this one (just pass
gl_FragCoord.xy as its argument).
Your image goes from RGB 0|0|0 to RGB 97|97|97, with the specific region you have highlighted going from RGB 19|19|19 to RGB 22|22|22 in increments of 1.
In other words, what you are seeing as banding is actually consecutive greyscale colours in increments of 1, but at the darker end of the range.
Measuring 140 next to 141 in an image program is not representative of the colour range you are using; you need to measure darker colours, in the 10s, 20s or 30s, to be able to do an equal comparison.
What you're seeing is just colour banding, caused by the fact that (and irrespective of how much precision you use internally) the final destination framebuffer is an 8|8|8|8 RGBX or RGBA format.
This is nothing to do with OpenGL and everything to do with your hardware.