Search code examples
javascriptthree.jsgame-enginequaternionscannon.js

Incremental application of Quaternion


I'm trying to figure out how I might apply a rotation in multiple increments, to an orientation which is dynamic (i.e. I cannot use slerp).

Details: I have an object in a 3D space, whose orientation can be described by quaternion Q. While this object is rotating, it receives periodic updates from another server, which defines the true orientation. For example, at time t1, the object's orientation was Q1, and received an update that the correct orientation should be K1.

I cannot simply replace Q1 by K1 because the visual result will not be smooth, so I would rather correct gradually from Q1 to K1 over a sequence of 10 steps. Also, I cannot use slerp, because Q1 is not static. Rather, I would like to derive an incremental correction, which I call dK, until the next server update arrives.

Right now I derive dK in the following way:

  1. delK = K1 * Q1.conjugate()
  2. dk = delK / 10

What actually happens at step 2, is that I convert delK to an axis+angle representaion, and then divide the angle by 10. Then I convert back to a quaternion.

Question 1: Is the approach described above mathematically correct?

Question 2: I am seeing cases where dk is not a small correction and possibly rotating in the opposite direction. What might cause this.

This is for the implementation of client-side prediction in JavaScript incheon.


Solution

  • You could replace your 10-step approach with a "slerp towards" solution:

    Q = slerp(Q, Q1, 1 - Math.pow(smoothingRatio, deltaTime)
    

    This solution will slerp Q towards Q1 in a frame-rate independent way, and it allows Q1 to be dynamic. After 1 second of interpolation, the remaining fraction of the "distance" to Q1 will be smoothingRatio. Read more here.

    If you have rotations larger than 180 degrees per update, then the closest axis of rotation will be different than on the server. A naive solution would be to send updates more often, or limit the rotational speed of your object(s).

    If you were to send angular velocity along with your updates, you could do better client side prediction, and maybe get around the "opposite direction" problem. The idea would be to keep rotating the quaternion along the angular velocity sent in the last update.

    The Quaternion#integrate function in Cannon.js implements quaternion integration, which you could use.

    // Rotate Q along the angular velocity provided in the last update
    Q = Q.integrate(angularVelocity, deltaTime, Vec3.ZERO);