-
-
Save mairod/a75e7b44f68110e1576d77419d608786 to your computer and use it in GitHub Desktop.
vec3 hueShift( vec3 color, float hueAdjust ){ | |
const vec3 kRGBToYPrime = vec3 (0.299, 0.587, 0.114); | |
const vec3 kRGBToI = vec3 (0.596, -0.275, -0.321); | |
const vec3 kRGBToQ = vec3 (0.212, -0.523, 0.311); | |
const vec3 kYIQToR = vec3 (1.0, 0.956, 0.621); | |
const vec3 kYIQToG = vec3 (1.0, -0.272, -0.647); | |
const vec3 kYIQToB = vec3 (1.0, -1.107, 1.704); | |
float YPrime = dot (color, kRGBToYPrime); | |
float I = dot (color, kRGBToI); | |
float Q = dot (color, kRGBToQ); | |
float hue = atan (Q, I); | |
float chroma = sqrt (I * I + Q * Q); | |
hue += hueAdjust; | |
Q = chroma * sin (hue); | |
I = chroma * cos (hue); | |
vec3 yIQ = vec3 (YPrime, I, Q); | |
return vec3( dot (yIQ, kYIQToR), dot (yIQ, kYIQToG), dot (yIQ, kYIQToB) ); | |
} |
Thank you so much for this! Contrary to what you seem to think though, your original function produces much more natural looking/beautiful results than the cheaper hueshift function provided by @viruseg. Of course it depends on context whether or not that's needed, but I for one deeply appreciate it!
EDIT but, I believe this is a faster implementation ->
vec2 rotate2(vec2 v, float fi) {
return v*mat2(cos(fi), -sin(fi), sin(fi), cos(fi));
}
// YIQ color rotation/hue shift
vec3 hueShiftYIQ(vec3 rgb, float hs) {
float rotAngle = hs*-6.28318530718;
const mat3 rgb2yiq = mat3(0.299, 0.596, 0.211,
0.587, -0.274, -0.523,
0.114, -0.322, 0.312);
const mat3 yiq2rgb = mat3(1, 1, 1,
0.956, -0.272, -1.106,
0.621, -0.647, 1.703);
vec3 yiq = rgb2yiq * rgb;
yiq.yz *= rot(rotAngle);
return yiq2rgb * yiq;
}
Thank you so much for this! Contrary to what you seem to think though, your original function produces much more natural looking/beautiful results than the cheaper hueshift function provided by @viruseg.
I had the same conclusion. The idea to use Rodrigues rotation formula is clever, but the problem is that the RGB to YIQ translation isn't a pure rotation. It also has skew and scaling components. So the output won't be the same.
The most compact version I could come up with, with some symbolic manipulation (along the same lines as Rodrigues' but replacing the cross product), that should be equivalent, is:
vec3 hue_shift(vec3 color, float dhue) {
float s = sin(dhue);
float c = cos(dhue);
return (color * c) + (color * s) * mat3(
vec3(0.167444, 0.329213, -0.496657),
vec3(-0.327948, 0.035669, 0.292279),
vec3(1.250268, -1.047561, -0.202707)
) + dot(vec3(0.299, 0.587, 0.114), color) * (1.0 - c);
}
(Godot shader, but should be compatible to GLSL)
vmedea, thank you for the code.
How to adapt your code fro RGBA (alpha) image, vec4?
@l375cd It doesn't do anything with alpha channel, so just use GLSL swizzling to only pass in the RGB channels, and then re-apply the alpha channel to the result. Something like the pseudo-code below.
in vec2 texCoord;
out vec4 outFrag;
uniform sampler2D image;
uniform float hue
void main()
vec4 color = texture(image, texCoord);
outFrag = vec4(hueShift(color.rgb, hue), color.a);
}
@ForeverZer0, thank you so much!
Will it work with sRGB color format, or should I convert my color to RGB before using this function?
My version of hue shift I did for some codegolf shader.
vec3 h(vec3 c, float s){
return c * mat3(c = .66 * cos(s + vec3(0, 2.09, 4.18)) + .33, c.zxy, c.yzx);
}
@janpaul123 Thanks that makes more sense. But yeah I still can't figure out how that formula was reached.