Hue Shifting with Transforms

Recently I had the opportunity to fiddle with some image adjustment algorithms to show an approximate preview for a texture on a mesh which among others included a hue shift.

Of course shifting the hue is no big deal, there’s already of plethora of online resources that demonstrate how to change a RGB color to HSL and back which makes hue shifting as easy as incrementing a value, which is very nifty of course, however let’s assume you already have a setup that computes a matrix to adjust brightness, contrast and saturation which is sent to a shader, this is where things get interesting.

Now the obvious solution would have been to modify the shader, pass the hue value do the adjustment and be done with it, however that broke backwards compatibility, so this was not an option. Luckily, HSL isn’t just a formula, once you try to visualize RGB as a coordinate system instead of pixel values, you start to realize that a rotation on the (1,1,1) axis would essentially create a hue shift, HSL is only a convention to bring the Z (or blue in our case) axis to the (1,1,1) direction so you can make the rotation.

So let’s consider that Create*Rotation would make a rotation matrix on the specified axis with the specified angle and that MultiplyMatrix multiplies two matrices leaving the result in the last argument, then the following sequence will create a matrix adjustment for a hue shift:

CreateXRotation(xrot, PI / 4)
CreateYRotation(yrot, PI / 6)
CreateZRotation(hueShift, hue)
CreateYRotation(invyRot, -PI / 6)
CreateXRotation(invxRot, -PI / 4)
MultiplyMatrix(xrot, yrot, tohsl)
MultiplyMatrix(tohsl, hueShift, shiftedMat)
MultiplyMatrix(invyRot, invxRot, torgb)
MultiplyMatrix(shiftedMat, torgb, adjustment)

Which gives us an adjustment matrix that we can premultiply into the existing matrix, problem solved and we keep backwards compatibility.

One thing to be noted is that hue has to be in radians, the most intuitive course of action is to let users specify it in degrees then turn it into radians before creating the matrix, but that is just a minor detail.

And there you have it, an easy to use transform to implement hue shifting when working with HSL values is not an option