Intro.
Previously, I've asked a question on converting rgb triple to quaternion. After that question I've managed to get unit quaternions, but I am in doubt of their internal structure. There was no easy way to operate them, and to separate luma and chroma, since that were quaternions of unit length. According to my feeling about it, luminance should be encoded in the either real part, or a whole magnitude; And color "chroma" information should be encoded in the imaginary part.
Today I've decided to improve things up, taking another approach, different from the first one in the link above. I think it could success, since quaternion could store not only rotation(unit quaternion), but scale as well. First things first, so I'll start with explaining my next idea. I would use GLSL shader syntax in the following explanations.
Approach description and the question body.
For some pixel of an image, let's concieve a 3D vector vec3 u
within the unit cube, where positive coordinates are lying in closed range [0.0, 1.0], and are representing full rgb colorspace. So now u
's coordinates , u.x
, u.y
and u.z
would represent red, green and blue values of that pixel, accordingly. Then lets take a pure white vector const vec3 v = vec3(1.0, 1.0, 1.0);
. And let's define some quaternion q
, so that our vector u
is the "v
, rotated and scaled with quaternion q
". In simple words, q
must answer the question "How to transform v
, in order to get initially conceived color u
?". And lets introduce function for that "rotate and scale" operation: vec3 q2c(in vec4 q, in vec3 v)
. I'll call it "quaternion-to-color" converter.
Writing q2c(q, v)
is pretty simple, just as defined: q2c(q, v) == (q*vec4(v, 0.0))*q'
. Here, the "*
" operator denotes quaternion multiplication; Lets make it a function vec4 qmul(in vec4 q1, in vec4 q2)
. And "q'
" denotes q
's conjugate, lets make it vec4 qconj(in vec4 q)
. Omitting their simple implementation (that you may find in full source), we would come to classic code:
vec4 q2c(in vec4 q, in vec3 v) {
return qmul(qmul(q, vec4(v, 0.0)), qconj(q));
}
So now we have q2c(q,v)
function, that converts quaternion q
to color, by rotating and scaling some chosen 3D vector v
.
The question is How to find that quaternion q
?
From a programmer's perspective, the goal is To write reverse function vec4 c2q(in vec3 u, in vec3 v)
- a corresponding "color to quaternion" converter.
Please note, that you should not touch q2c()
, without a really good reason. E.g, a serious bug in its logic, leading to "impossibility to solve task", and you can proof that.
How could you check, if your answer is correct?
Indeed, the checking method would arise from the fact that you would get initial value, if you will manage to convert forth and back. So the checking condition is For any non-zero length v
, u
must always be equal to q2c(c2q(u, v), v)
. v
must have non-zero length, because one cannot "scale zero" to get "something".
To easy things up, I've prepared testing program, using shadertoy.com service.
You would require a decent computer, with working internet connection and a web browser with webGL support (I'm using Chrome). Program should work on any GPU, even embedded into intel's processors. It even worked on my lower-end smartphone!
To test your answer, you should put your proposed formula, written in GLSL
syntax, inside c2q()
function. Then press apply button, and your changes will come into effect:
Image at the left represents some unchanged source pixels. And right half will contain pixels, transformed forth and back by q2c(c2q())
. Obviously, halves must be visually equal, you should not notice any vertical line. An some little mathematical(unnoticeable) error may arise, but only due to floating point's nature - its finite precision and possible rounding errors.
Feel free to edit and experiment, changes will be done only locally, on your computer, and you cannot wreck anything. If video is not playing on first open (shadertoy bug) - try to pause/unpause it. Enjoy!
Hall of c2q()
Attempts
If everything is correct, the right side of image(processed one) should be equal to the left side(original). And here I would review different results, that were obtained by putting something instead of xxxxx
, in the c2q()
implementation:
vec4 c2q(vec3 u, vec3 v) {
return xxxxx;
}
Lets proceed!
Initially I've thought that must just work:
vec4(cross(u, v), dot(u, v))
:One of SE answers:
vec4( cross(u, v), sqrt( dot(u, u) * dot(v, v) ) + dot(u, v) )
:And with his hint "Don't forget to normalize q":
normalize(vec4( cross(u, v), sqrt( dot(u, u) * dot(v, v) ) + dot(u, v) ))
:@minorlogic's comment, seems to be a step closer: scale all
q
's components bysqrt( length(v)/length(u) )
,vec4(cross(u, v), dot(u, v)) * sqrt( length(u)/length(v) )
:With ratio swapped:
vec4(cross(u, v), dot(u, v)) * sqrt( length(v)/length(u) )
: