0

I am rendering a quad with up to 4 different vertex colours.

My vertex shader is super simple:

#version 330

layout(location=0) in vec2 inVertexPosition;
layout(location=1) in vec4 inColor;

out vec4 color;

void main()
{
    gl_Position = vec4(inVertexPosition.x,-inVertexPosition.y, 0.0, 1.0);
    color = inColor;
}

The fragment shader:

#version 330

layout(location=0) out vec4 frag_colour;

in vec4 color;

void main()
{
    frag_colour = color;
}

also super simple. The results are a smooth gradient from corner to corner. However, I would like to produce an effect similar to the background of the text in this image:

enter image description here

where there is a limited palette so there's intentional banding in the gradient. My attempt to create this same style is a combination of Gradient with fixed number of levels and From RGB to HSV in OpenGL GLSL which has given me a fragment shader like this:

#version 330

layout(location=0) out vec4 frag_colour;

in vec4 color;

uniform bool uBand = false;
uniform float uBandingSteps;

vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

void main()
{
    if(uBand)
    {
        vec3 hsv = rgb2hsv(color.rgb);
        float h = floor(hsv.x * (uBandingSteps*3+1) + 0.5) / (uBandingSteps*3+1);
        float s = floor(hsv.y * (uBandingSteps*3+1) + 0.5) / (uBandingSteps*3+1);
        float v = floor(hsv.z * (uBandingSteps*3+1) + 0.5) / (uBandingSteps*3+1);
        frag_colour = vec4(hsv2rgb(vec3(h,s,v)),color.a);
    }
    else
        frag_colour = color;
}

This works to a degree, however, for some reason I need to multiply uBandingSteps to generate the right number of colours. The above works "okay", but the problem is that the banding seems fairly arbitrary:

enter image description here

You can see that the banding doesn't give a bevelled sort of look, but instead the first colour is narrow, then large, then narrow, then large etc. Rather than being gradually larger until it gets to the middle colour that's the widest part of the gradient, and then steadily retreats.

How can I modify what I have in order to produce the intended effect? (sort of bevelled, smooth, stepped gradient).

NeomerArcana
  • 1,978
  • 3
  • 23
  • 50
  • Then you probably need to calculate the color directly in the fragment shader since all GPU can do for you is a linear interpolation (which you apparently don't want). Instead of having colors at the vertices, give them a parameter value (0 at the top, 1 at the bottom), and calculate whatever gradient you want using that parameter. – Nico Schertler Dec 30 '19 at 00:24
  • @NicoSchertler can you give an example? How would I calculate the colour on a gradient? – NeomerArcana Dec 30 '19 at 03:16
  • E.g. something like `(1-t^2) * color1 + t^2 * color2`, where `t` is the parameter. Then apply your quantization to that color. – Nico Schertler Dec 30 '19 at 04:02
  • looks like color banding, eg: http://glslsandbox.com/e#60028.0 – nabr Dec 30 '19 at 12:16

1 Answers1

1

Possibly the answer is super simple, too. Just round the color channels.
e.g. If you want to allow 8 different gradients for each color channel, then based on the fragment shader of the question:

#version 330

layout(location=0) out vec4 frag_colour;

in vec4 color;

void main()
{
    vec3 color8 = round(color.rgb * 8.0) / 8.0;
    frag_colour = vec4(color8, color.a);
}
Rabbid76
  • 202,892
  • 27
  • 131
  • 174