Solved
I'm making a 3D portal system in my engine (like Portal game). Each of the portals has its own orientation saved in a quaternion. To render the virtual scene in one of the portals I need to calculate the difference between the two quaternions, and the result use to rotate the virtual scene.
When creating the first portal on the left wall, and second one on the right wall, the rotation from one to another will take place in only one axis, but for example when the first portal will be created on the floor, and the second one on the right wall, the rotation from one to another could be in two axis, and that's the problem, because the rotation goes wrong.
I think the problem exists because the orientation for example X
axis and Z
axis are stored together in one quaternion and I need it separately to manualy multiply X
* Z
(or Z
* X
), but how to do it with only one quaternion, (the difference quaternion)? Or is there other way to correct rotate the scene?
EDIT:
Here on this picture are two portals P1 and P2, the arrows show how are they rotated. As I am looking into P1 I will see what sees P2. To find the rotation which I need to rotate the main scene to be like the virtual scene in this picture I'm doing following:
- Getting difference from quaternion P2 to quaternion P1
- Rotating result by 180 degrees in Y axis (portal's UP)
- Using the result to rotate the virtual scene
This method above works only when the difference takes place in only one axis. When one portal will be on the floor, or on te ceiling, this will not work because the difference quaternion is build in more than one axis. As suggested I tried to multiply P1's quaternion to P2's quaternion, and inversely but this isn't working.
EDIT 2:
To find the difference from P2 to P1 I'm doing following:
Quat q1 = P1->getOrientation();
Quat q2 = P2->getOrientation();
Quat diff = Quat::diff(q2, q1); // q2 * diff = q1 //
Here's the Quat::diff function:
GE::Quat GE::Quat::diff(const Quat &a, const Quat &b)
{
Quat inv = a;
inv.inverse();
return inv * b;
}
Inverse:
void GE::Quat::inverse()
{
Quat q = (*this);
q.conjugate();
(*this) = q / Quat::dot((*this), (*this));
}
Conjugate:
void GE::Quat::conjugate()
{
Quat q;
q.x = -this->x;
q.y = -this->y;
q.z = -this->z;
q.w = this->w;
(*this) = q;
}
Dot product:
float GE::Quat::dot(const Quat &q1, const Quat &q2)
{
return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
}
Operator*:
const GE::Quat GE::Quat::operator* ( const Quat &q) const
{
Quat qu;
qu.x = this->w*q.x + this->x*q.w + this->y*q.z - this->z*q.y;
qu.y = this->w*q.y + this->y*q.w + this->z*q.x - this->x*q.z;
qu.z = this->w*q.z + this->z*q.w + this->x*q.y - this->y*q.x;
qu.w = this->w*q.w - this->x*q.x - this->y*q.y - this->z*q.z;
return qu;
}
Operator/:
const GE::Quat GE::Quat::operator/ (float s) const
{
Quat q = (*this);
return Quat(q.x / s, q.y / s, q.z / s, q.w / s);
}
All this stuff works, because I have tested it with GLM library
I solved my problem. As it turned out I don't need any difference between two rotations. Just multiply one rotation by rotation in 180 degrees, and then multiply by inverse of second rotation that way (using matrices):
and translation calculating this way:
If you want to find a quaternion
diff
such thatdiff * q1 == q2
, then you need to use the multiplicative inverse:If your quaternions are rotation quaternions, they should all be unit quaternions. This makes finding the inverse easy: since
abs(q1) = 1
, yourinverse(q1) = conjugate(q1)
can be found by just negating thei
,j
, andk
components.However, for the kind of scene-based geometric configuration you describe, you probably don't actually want to do the above, because you also need to compute the translation correctly.
The most straightforward way to do everything correctly is to convert your quaternions into 4x4 rotation matrices, and multiply them in the appropriate order with 4x4 translation matrices, as described in most introductory computer graphics texts.
It is certainly possible to compose Euclidean transformations by hand, keeping your rotations in quaternion form while applying the quaternions incrementally to a separate translation vector. However, this method tends to be technically obscure and prone to coding error: there are good reasons why the 4x4 matrix form is conventional, and one of the big ones is that it appears to be easier to get it right that way.
Check https://www.emis.de/proceedings/Varna/vol1/GEOM09.pdf
Imagine to get dQ from Q1 to Q2, I'll explain why dQ = Q1*·Q2, instead of Q2·Q1*
This rotates the frame, instead of an object. For any vector v in R3, the rotation action of operator L(v) = Q*·v·Q
It's not Q·v·Q*, which is object rotation action.
If you rotates Q1 and then Q1* and then Q2, you can write (Q1·Q1*·Q2)*·v·(Q1·Q1*·Q2) = (Q1*·Q2)*·Q1*·v·Q1·(Q1*·Q2) = dQ*·Q1*·v·Q1·dQ
So dQ = Q1*·Q2
No, you have to multiply two quaternions together to get the final quaternion you desire.
Let's say that your first rotation is
q1
and the second isq2
. You want to apply them in that order.The resulting quaternion will be
q2 * q1
, which will represent your composite rotation (recall that quaternions use left-hand multiplication, soq2
is being applied toq1
by multiplying from the left)Reference
For a brief tutorial on computing a single quaternion, refer to my previous stack overflow answer
Edit:
To clarify, you'd face a similar problem with rotation matrices and Euler angles. You define your transformations about X, Y, and Z, and then multiply them together to get the resulting transformation matrix (wiki). You have the same issue here. Rotation matrices and Quaternions are equivalent in most ways for representing rotations. Quaternions are preferred mostly because they're a bit easier to represent (and easier for addressing gimbal lock)
Quaternions work the following way: the local frame of reference is represented as the imaginary quaternion directions i,j,k. For instance, for an observer standing in the portal door 1 and looking in the direction of the arrow, direction i may represent the direction of the arrow, j is up and k=ij points to the right of the observer. In global coordinates represented by the quaternion q1, the axes in 3D coordinates are
where q' is the conjugate, and for unit quaternions, the conjugate is the inverse.
Now the task is to find a unit quaternion q so that directions q*(i,j,k)*q' in local frame 1 expressed in global coordinates coincide with the rotated directions of frame 2 in global coordinates. From the sketch that means forwards becomes backwards and left becomes right, that is
which is readily achieved by equating
But details may be different, mainly that another axis may represent the direction "up" instead of j.
If the global system of the sketch is from the bottom, so that global-i points forward in the vertical direction, global-j up and global-k to the right, then local1-(i,j,k) is global-(-i,j,-k), giving
local2-(i,j,k) is global-(-k,j,i) which can be realized by
since
Comparing this to the actual values in your implementation will indicate how the assignment of axes and quaternion directions has to be changed.