#version 440
layout(location = 0) in vec2 v_tex_coords;
layout(location = 0) out vec4 color;
layout(std140, set = 0, binding = 0) uniform InputUniform {
mat4 matrix;
vec2 size;
float hue;
float contrast;
float brightness;
float saturation;
uint grayscale;
uint invert;
};
layout(set = 1, binding = 0) uniform texture2D t_diffuse;
layout(set = 1, binding = 1) uniform sampler s_diffuse;
const float PI = 3.141592653589793238462643383279502884197169399375105820974944;
const float max_value = 255;
// https://gist.github.com/ciembor/1494530
vec3 rgb2hsl(vec3 rgb) {
rgb = clamp(rgb, 0, 1);
vec3 result;
float r = rgb.r;
float g = rgb.g;
float b = rgb.b;
float max = max(max(r, g), b);
float min = min(min(r, g), b);
result.x = result.y = result.z = (max + min) / 2;
if(max == min) {
result.x = result.y = 0; // achromatic
} else {
float d = max - min;
result.y = (result.z > 0.5) ? d / (2 - max - min) : d / (max + min);
if(max == r) {
result.x = (g - b) / d + (g < b ? 6 : 0);
} else if(max == g) {
result.x = (b - r) / d + 2;
} else if(max == b) {
result.x = (r - g) / d + 4;
}
result.x /= 6;
}
return result;
}
vec3 hsl2rgb(vec3 hsl) {
hsl = clamp(hsl, 0, 1);
vec3 rgb = clamp(abs(mod(hsl.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
return hsl.z + hsl.y * (rgb - 0.5) * (1.0 - abs(2.0 * hsl.z - 1.0));
}
// Converts a color from linear light gamma to sRGB gamma
vec4 fromLinear(vec4 linearRGB) {
bvec4 cutoff = lessThan(linearRGB, vec4(0.0031308));
vec4 higher = vec4(1.055)*pow(linearRGB, vec4(1.0/2.4)) - vec4(0.055);
vec4 lower = linearRGB * vec4(12.92);
return mix(higher, lower, cutoff);
}
// Converts a color from sRGB gamma to linear light gamma
vec4 toLinear(vec4 sRGB) {
bvec4 cutoff = lessThan(sRGB, vec4(0.04045));
vec4 higher = pow((sRGB + vec4(0.055))/vec4(1.055), vec4(2.4));
vec4 lower = sRGB/vec4(12.92);
return mix(higher, lower, cutoff);
}
// this function is pretty much a line by line translation of the image-rs hue rotate function
// all color changing functions must have the exact same behavior as the image-rs functions
vec3 rotateHue(vec3 p, float hue) {
float cosv = cos(hue * PI / 180.0);
float sinv = sin(hue * PI / 180.0);
float matrix[9] = float[] (
// Reds
0.213 + cosv * 0.787 - sinv * 0.213, 0.715 - cosv * 0.715 - sinv * 0.715, 0.072 - cosv * 0.072 + sinv * 0.928,
// Greens
0.213 - cosv * 0.213 + sinv * 0.143, 0.715 + cosv * 0.285 + sinv * 0.140, 0.072 - cosv * 0.072 - sinv * 0.283,
// Blues
0.213 - cosv * 0.213 - sinv * 0.787, 0.715 - cosv * 0.715 + sinv * 0.715, 0.072 + cosv * 0.928 + sinv * 0.072);
float new_r = matrix[0] * p.r + matrix[1] * p.g + matrix[2] * p.b;
float new_g = matrix[3] * p.r + matrix[4] * p.g + matrix[5] * p.b;
float new_b = matrix[6] * p.r + matrix[7] * p.g + matrix[8] * p.b;
return vec3(clamp(new_r, 0, 1), clamp(new_g, 0, 1), clamp(new_b, 0, 1));
}
float adjustContrastPixel(float c, float percent) {
c = c * max_value;
float d = ((c / max_value - 0.5) * percent + 0.5) * max_value;
float e = clamp(d, 0.0, max_value);
return e / max_value;
}
vec3 adjustContrast(vec3 p, float contrast) {
float percent = pow((100.0 + contrast) / 100.0, 2);
float new_r = adjustContrastPixel(p.r, percent);
float new_g = adjustContrastPixel(p.g, percent);
float new_b = adjustContrastPixel(p.b, percent);
return vec3(new_r, new_g, new_b);
}
vec3 brighten(vec3 p, float value) {
float light = (value / 100);
//vec3 hsl = rgb2hsl(p);
//hsl.z = clamp(hsl.z + light, 0, 1.0);
//return hsl2rgb(hsl);
return clamp(vec3(p.r + light, p.g + light, p.b + light), 0, 1);
}
vec3 adjustSaturation(vec3 p, float sat) {
sat = sat / 100;
vec3 hsl = rgb2hsl(p);
hsl.y = clamp(hsl.y + sat, 0, 1);
return hsl2rgb(hsl);
}
vec3 invertRgb(vec3 p) {
return vec3(1.0 - p.r, 1.0 - p.g, 1.0 - p.b);
}
vec3 getCheckColor() {
vec3 color1 = vec3(64, 64, 64) / max_value;
vec3 color2 = vec3(48, 48, 48) / max_value;
float checkSize = 12.0;
float x = floor(gl_FragCoord.x / checkSize);
float y = floor((gl_FragCoord.y - size.y) / checkSize);
if(mod(x + y, 2) == 0) {
return color1;
} else {
return color2;
}
}
void main() {
vec4 p = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords);
p = fromLinear(p);
p.rgb = rotateHue(p.rgb, hue);
p.rgb = adjustContrast(p.rgb, contrast);
p.rgb = adjustSaturation(p.rgb, saturation);
p.rgb = brighten(p.rgb, brightness);
if(bool(grayscale)) {
const vec3 toLuma = vec3(0.299, 0.587, 0.114);
p.rgb = vec3(dot(p.rgb, toLuma));
}
if(bool(invert)) {
p.rgb = invertRgb(p.rgb);
}
vec3 check_color = getCheckColor();
color.rgb = check_color * (1 - p.a) + p.a * p.rgb;
color = toLinear(color);
color.a = 1;
}