-
-
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) ); | |
} |
@janpaul123 Thanks that makes more sense. But yeah I still can't figure out how that formula was reached.
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);
}
@RichardBray it's
sqrt(3)/3
or1/sqrt(3)
. It's one of those values that you see often in trigonometry, see e.g. http://mathforum.org/dr.math/faq/formulas/faq.trig.html (Not super often though; I had to do some digging to find this one too 😅). Not sure how to arrive at this formula from scratch though. 😕