🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Check if Quaternion.Slerp has changed "direction"

Started by
5 comments, last by Iron-Warrior 4 years, 5 months ago

Hello, I am creating a VR game where the player's motion controllers control their hands. Unlike most VR games, the in-game hands do not follow the virtual hands 1:1, but instead interpolate their position/orientation towards the motion controllers' tracked position/orientations.

(This is the game for context

—the interpolation allows the very large giant to interact with the player without moving silly fast).

I am using Unity engine and their implementation of Quaternion.Slerp to interpolate the hand rotation towards the goal. This works very well, but an issue happens if the VR player rotates their wrist very quickly. Because Slerp takes the shortest path to the target orientation, if the player manages to rotate their wrist a full 180 degrees before the hand can catch up, the shortest path will now be in the opposite direction and the hand will reverse its rotation direction accordingly. Below is an animation of this occurring in slow motion. The axis markers denote the hand's current orientation and the goal orientation.

I would prefer instead that if the rotation direction has changed, I can determine if a “flip” is going to occur and instead continue along the current direction (which is now no longer the shortest path—Unity can do this with passing a negative value into SlerpUnclamped).

Blue shows the current hand orientation, orange the goal, and red the direction of travel. The lighter drawings are the first frame, with the darker showing the second. The shortest path is now in the opposite direction in the next frame.

I'm not sure how to determine if a direction change has occurred (or if this is possible), since quaternions live in 3D space (and not 2D like the above diagram. I looked into using the sign of the dot product to compare orientations between frames, since it looks like it passes from positive to negative when 180 degrees are passed, but I've also been able to get that positive→negative transition to occur without passing 180, so I don't entirely know what its properties are.

Intuitively it is easy to see when a flip occurs, but I'm not sure how to figure it out in code, or if quaternions have any properties than can help.

Thanks for any help,

Erik

Advertisement

Here are my thoughts for how to solve this. The general idea is to ensure that the next interpolated orientation is rotating in the same “direction” as the target orientation relative to the previous orientations.

Notation:
qt_{t-1} = previous target hand orientation at time t-1
qt_t = target hand orientation at time t
qc_{t-1} = previous interpolated hand orientation at time t-1
qc_t = hand orientation at time t, this is what we need to figure out. qc_t = slerp( qt_t, qc_{t-1}, a*sign ), where sign determines the rotation direction.

With this notation, we can compute two candidates for qc_t, one with positive sign, one with negative. Then, the task is to evaluate which one is rotating in the same way as the target.

To do this, we compute “delta” rotations by dividing the orientations by their previous orientations (same as multiplying by inverse quaternion). This gets us the rotation to move from one orientation to another.
dqc(+) = qc_t(+) / qc_{t-1} (positive candidate)
dqc(-) = qc_t(-) / qc_{t-1} (negative candidate)
dqt = qt_t / qt_{t-1}

Next, compute the delta rotations between each candidate and the target rotation delta:
d(+) = dqc(+) / dqt
d(-) = dqc(-) / dqt

Finally, convert d(+) and d(-) to axis+angle representation. The one with the smallest angle absolute value determines the sign of the interpolation parameter, since this is the rotation direction that minimizes the difference between the target and current rotation deltas.

You could cross product previous orientation with new one, and then cross product the previous orientation with pre-previous, gaining two vectors. If you dot those two vectors, check for sign. If it is negative, their direction difference is over 180 degrees. That could initiate your clamp-seek logic.

Thank you both for the insightful responses. My successful solution ended up converting both the previous delta rotation (as a Quaternion) and the upcoming delta (that was about to be performed) to axis angle representation. Then, if the dot of the axes was negative it would mean that the hand was attempting to switch rotation directions. If this was happening, I would take the angle between the hand's current orientation and the goal and check if it was greater than some threshold (since of course direction changes occur all the time—but large direction changes should almost never occur over a single frame). This seems to be very stable.

@Iron-Warrior That can work, but you need to be careful that the axes are not perpendicular - my solution using quaternions does conceptually the same thing, but is more robust in edge cases and doesn't require the thresholding you mention.

Aressera said:

@Iron-Warrior That can work, but you need to be careful that the axes are not perpendicular - my solution using quaternions does conceptually the same thing, but is more robust in edge cases and doesn't require the thresholding you mention.

Ahh okay, thanks for the advice.

This topic is closed to new replies.

Advertisement