A fragment shader for converting to and from HSV. Useful for performing color effects.

The shader supplied here replaces the default image_blend functionality with something like the sprite editor's colorize function, but it can be easily be modified to do other HSV-color-space effects. (I recommend against using it for anything that doesn't require hue though, since saturation and volume can both be manipulated directly in RGB color space)

`varying vec2 v_vTexcoord;`

varying vec4 v_vColour;

#define EPSILON 0.00001

vec3 toHSV(vec3 col) {

vec3 hsv = vec3(0.0);

hsv.z = max(max(col.r, col.g), col.b); //volume

float difference = hsv.z-min(min(col.r, col.g), col.b);

hsv.y = difference / hsv.z; //saturation

//possible hue ranges

float redmax_hue = (col.g-col.b)/difference;

float greenmax_hue = 2.0+(col.b-col.r)/difference;

float bluemax_hue = 4.0+(col.r-col.g)/difference;

//hue range weights

float redweight = step(hsv.z, col.r);

float greenweight = (1.0-redweight)*step(hsv.z, col.g);

float blueweight = (1.0-redweight)*(1.0-greenweight);

hsv.x = mod((redweight*redmax_hue +

greenweight*greenmax_hue +

blueweight*bluemax_hue)

*60.0,

360.0); //hue

//div by zero handling

//for cases of unsaturated or pure black

//hue is zeroed, and saturation is also zero

//since its gray, and only volume matters,

//and we want to avoid div-by-zero fuckery

hsv.xy *= step(EPSILON, difference)*step(EPSILON, hsv.z);

return hsv;

}

//this function used to be more complicated but then it turns out it was trivial

float rerange(float x, float r) {

return (x-r)/1.0; //div by one ResidentSleeper

}

vec3 toRGB(vec3 hsv) {

//re-normalize hue to 0-6, because color hexagon reasons

hsv.x = mod(hsv.x, 360.0);

hsv.x /= 60.0;

//weights for the possible hue ranges

float ranges[6];

for (int i = 0; i < 6; i++) {

ranges[i] = step(float(i), hsv.x)*step(hsv.x, float(i+1));

}

//manual unrolling bitch

//actually I dont even know how I would do this in a proper loop

//shits going everywhere all at once

//see https://en.wikipedia.org/wiki/File:HSV-RGB-comparison.svg for an explanation

vec3 col = vec3(0.0);

col += vec3(ranges[0], ranges[0]*rerange(hsv.x, 0.0), 0.0);

col += vec3(ranges[1]*(1.0-rerange(hsv.x, 1.0)), ranges[1], 0.0);

col += vec3(0.0, ranges[2], ranges[2]*rerange(hsv.x, 2.0));

col += vec3(0.0, ranges[3]*(1.0-rerange(hsv.x, 3.0)), ranges[3]);

col += vec3(ranges[4]*rerange(hsv.x, 4.0), 0.0, ranges[4]);

col += vec3(ranges[5], 0.0, ranges[5]*(1.0-rerange(hsv.x, 5.0)));

//We have the hue, now we just have to squoosh it into its correct sat/vol

//I'm sure there's a clever one-liner to do this but I haven't found it

col.r = mix(hsv.z-hsv.y, hsv.z, col.r);

col.g = mix(hsv.z-hsv.y, hsv.z, col.g);

col.b = mix(hsv.z-hsv.y, hsv.z, col.b);

//handle fully-desaturated case

//in case of div-by-zero fuckery

col = mix(col, vec3(hsv.z), step(hsv.y, 0.0));

return col;

}

void main()

{

vec4 base_col = texture2D(gm_BaseTexture, v_vTexcoord);

vec3 hsv = toHSV(base_col.rgb);

//you can do HSV fuckery here

//Hue is 0.0-360.0 (degrees)

//Saturation/volume are 0.0-1.0

//access h, s, and v with x, y, and z

hsv.x = toHSV(v_vColour.rgb).x;

//and convert back to RGB here

gl_FragColor = vec4(toRGB(hsv), base_col.a*v_vColour.a);

}