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?
-
\$\begingroup\$ It's tough to tell from that image - are you setting the color per polygon or per vertex? \$\endgroup\$– PikalekCommented May 25, 2017 at 14:02
-
\$\begingroup\$ @Pikalek - copy off the image to an image editor, zoom in on it - it's clearly banding. Adjust brightness/contrast to see it better if required, but it should be beyond doubt. \$\endgroup\$– Maximus MinimusCommented May 25, 2017 at 15:35
-
\$\begingroup\$ No need to copy to an image editor, I think, you can just right-click and show image. But yeah, it seems to be banding. And contrast might help. Also, it's a soft gradient between vertices, so I guess 'per vertex'..? \$\endgroup\$– Henry StoneCommented May 25, 2017 at 20:45
-
\$\begingroup\$ At the time I wasn't able to check w/ an editor. I did click in, but wanted to verify. It looks like classic mach banding to me as well. \$\endgroup\$– PikalekCommented May 25, 2017 at 23:09
2 Answers
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).
-
\$\begingroup\$ Could the same correcting effect be achieved with a soft white noise texture? I haven't really gotten into fragment shaders yet :( \$\endgroup\$ Commented May 25, 2017 at 20:42
-
1\$\begingroup\$ Overlaying a soft noise texture should probably work, but depending on how you generate &/or apply the texture, you'll need to make sure it doesn't create seams. Alternatively, you could just add a bit of noise to the vertex values when you assign them. Should work just as well & probably a bit easier if you're already setting the color via vertices. \$\endgroup\$– PikalekCommented May 25, 2017 at 23:13
-
2\$\begingroup\$ @HenryStone yes, a white noise texture may achieve the same result, but make sure it is not filtered (also, you should absolutely learn shaders before you do anything else in OpenGL; if you don’t use them, you are lagging 15 years behind!) \$\endgroup\$ Commented May 26, 2017 at 11:17
-
\$\begingroup\$ @sam, I know shaders, but I'm not too hot with them yet. I'm going bareback for the time being. But by filtered, you mean what exactly? \$\endgroup\$ Commented May 26, 2017 at 19:29
-
1\$\begingroup\$ @HenryStone noise in the vertex colours will not work; your image will slightly flicker and you will get the banding effect on any screenshot. By filtering I mean this: make sure you use
GL_NEAREST
. \$\endgroup\$ Commented May 27, 2017 at 13:01
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.
-
\$\begingroup\$ Does that mean other hardware will do more or less of it? Is there a (standard) way to predict this, to optimize the individual hardware's performance? \$\endgroup\$ Commented May 25, 2017 at 20:41
-
1\$\begingroup\$ @HenryStone - no, I mean that the framebuffer format is 8|8|8|8. If your GPU and monitor support it, you may be able to create different framebuffer formats, but you cannot rely on them being available on other people's PCs. \$\endgroup\$ Commented May 25, 2017 at 20:48
-
\$\begingroup\$ Ah, good, I was mainly worried that the bands would show up differently on different hardware, forcing me to do hardware detection and multiple solutions to fix it. Thanks for setting my nerves at ease!! \$\endgroup\$ Commented May 26, 2017 at 19:34