Skip to content

Instantly share code, notes, and snippets.

Last active August 30, 2023 09:03
  • Star 24 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Optimised Hue shift function in GLSL
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) );
Copy link

vmedea commented Jan 16, 2023

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)

Copy link

l375cd commented Aug 30, 2023

vmedea, thank you for the code.

How to adapt your code fro RGBA (alpha) image, vec4?

Copy link

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

Copy link

l375cd commented Aug 30, 2023

@ForeverZer0, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment