6

I'm using the following shader to render a skydome to simulate a night sky. My issue is the clearly visible transitions between colours.

What causes these harsh gradient transitions?

enter image description here

Fragment shader:

#version 330
in vec3 worldPosition;
layout(location = 0) out vec4 outputColor;

void main()
{
    float height = 0.007*(abs(worldPosition.y)-200);    
    vec4 apexColor = vec4(0,0,0,1);
    vec4 centerColor = vec4(0.159, 0.132, 0.1, 1);

    outputColor = mix(centerColor, apexColor, height);
}

Fbo pixel format:

GL.TexImage2D(
    TextureTarget.Texture2D,
    0,
    PixelInternalFormat.Rgb32f,
    WindowWidth,
    WindowHeight,
    0,
    PixelFormat.Rgb,
    PixelType.Float,
    IntPtr.Zero )
genpfault
  • 51,148
  • 11
  • 85
  • 139
livin_amuk
  • 1,285
  • 12
  • 26
  • 4
    How does the `RGB32F` format relate to all that? I'd say you don't see the banding in that texture, you just see it when you finally render (or blit) it to the final framebuffer, which typically just has 8 bits of precision. – derhass Jul 17 '18 at 16:17
  • @derhass I included the pixel format information incase it was related to the problem, seemed plausible given what I knew at the time. – livin_amuk Jul 18 '18 at 06:44

2 Answers2

8

As Ripi2 explained, 24 bit color is unable to perfectly represent a gradient and discontinuities between representable colours become jarringly visible on gradients of a single color.

To hide the color banding I implemented a simple form of ordered dithering with an 8x8 texture generated using this bayer matrix algorithm.

enter image description here

vec4 dither = vec4(texture2D(MyTexture0, gl_FragCoord.xy / 8.0).r / 32.0 - (1.0 / 128.0));
colourOut += dither;

enter image description here

livin_amuk
  • 1,285
  • 12
  • 26
  • It wil be much more better if you edit borh question and answer and use some image which is not near black, which is perhaps the most difficult to see glitches. – Ripi2 Jul 18 '18 at 18:25
2

Normally monitors have 8 bits per channel of resolution. For example, the red intensity varies from 0 to 255.

If your window horizontal size is 768 pixels and you want a full gradient on red channel, then each color step takes 768/256 = 3 pixels. Depending on your eye health you may see bands.

How to do smooth gradient on those 3 pixels? Use sub-pixel rendering.
Basically you "expand" the color step among the neighbour pixels: Add small amounts of other channels to neighbours, and reduce a bit the central pixel amount.

Ripi2
  • 7,031
  • 1
  • 17
  • 33
  • 1
    Sub-pixel rendering is for anti-aliasing. I think you mean [dithering](https://en.wikipedia.org/wiki/Dither). – derhass Jul 17 '18 at 18:23
  • 1
    @derhass Well, [Floyd–Steinberg dithering](https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering) would be used here. The point is using neighboring pixels to smooth the gradient. – Ripi2 Jul 17 '18 at 19:31
  • 1
    Floyd-steinberg is hard to implement in a parallel fashio. simple ordered dithering (bayer) works very well on GPUs, and is easy to implement. – derhass Jul 17 '18 at 20:06