RGB to HSV to RGB - for Shaders
2 min read

RGB to HSV to RGB - for Shaders

RGB to HSV to RGB - for Shaders

TL;DR: Go here to get the RGB->HSV and HSV->RGB conversion functions for shaders.

The other day I tried to tint a colour in a texture (or rather all texture) with a different colour. It's quite easy to rotate things around, but, if you want to do it parametrically, you'll need something a bit more complex.

HSV

The easiest way (?!?!) I could come up with was to shift the hue off all colors using a RGB<->HSV conversion. The algorithm in a shader would be quite simple:

for pixel in texture:
    rgb = pixel.rgb
    hsv = rgb_to_hsv(rgb)
    hsv.h += parameter  # parameter is 0-1
    if hsv.h > 1:
        hsv.h -= 1
    pixel.set_color(hsv_to_rgb(hsv))

Simple, right?

Now, the two functions are:

  1. RGB -> HSV:

    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);
    }
    
  2. HSV -> RGB:

    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);
    }
    

Now, all you have to do is place them in a fragment shader and apply the above algorithm.

Vertex shader

The vertex shader is quite basic (done in the OS X's OpenGL Shader Builder):

//1
attribute vec4 a_position;
attribute vec2 a_texCoord;

void main()
{
    gl_FrontColor = gl_Color;
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}

Fragment shader

The fragment shader is like this (sand the two above methods for brevity)

uniform sampler2D tex;
uniform vec3 hue;

// Add the two methods here

void main() {
    vec4 textureColor = texture2D(tex, vTextureCoord);
    vec3 fragRGB = textureColor.rgb;
    vec3 fragHSV = rgb2hsv(fragRGB).xyz;
    fragHSV.x += hue.x;
    fragHSV.yz *= hue.yz;
    fragHSV.xyz = mod(fragHSV.xyz, 1.0);
    fragRGB = hsv2rgb(fragHSV);
    gl_FragColor = vec4(fragRGB, textureColor.w);
}

Yes, it's like the one from SO, but with the difference that values for hue are between 0 and 1 (rather than 0-360).

Now, all we have to do is to tint only some of the colours... :)