Advertisement

Quaternion Addition causing warping

Started by September 01, 2020 11:05 AM
6 comments, last by kenanmarasli 4 years, 2 months ago

Hey Y'all,

I've been trying to build a little physics engine for fun, mostly based on this document. It lists the following function for implementing rotation (on page 15):

q² = q¹ + (∆t/2)q¹ ∗ ω²

Where:

  • q² is the orientation of the current time step (as a quaternion)
  • q¹ is the orientation of the previous time step
  • ∆t is the time delta
  • ω² is the angular velocity of the current time step (treated as a quaternion with a 0 scalar)
  • ∗ represents quaternion multiplication

I implemented this as follows

Transform.Rotation += (deltaTime.DeltaF / 2) * Transform.Rotation * new quat(AngularVelocity, 0);
 //AngularVelocity is a vec3

I am using GlmSharp for vec3 and quat, which implements quaternion addition component-wise, which I'm pretty sure is fine:

/// <summary>
/// Returns a quat from component-wise application of operator+ (lhs + rhs).
/// </summary>
public static quat operator+(quat lhs, quat rhs) => new quat(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w);

However this causes warping on my objects as they accumulate rotation:

This is without apply any kind of scaling or translation.

Searching around the web I found very little people talking about using quaternion addition, so am I interpreting the document's function incorrectly? I have tried implementing angular velocity in other ways but none of them have worked. What am I missing?

Thanks for reading and have a nice day!

q2 += 0.5 * w1 * q1 * dt, if w1 is in world space (multiplication from left)

q2 += 0.5 * q1 * w1 * dt, if w1 is in local (body) space (multiplication from right)

I would also normalize q2 after integration!

Advertisement

@Dirk Gregorius Normalizing was the solution! I don't fully grasp quaternions yet, so didn't know about normalizing them. Thanks for the help!

Quaternions support some of the familiar operations from vector algebra, such as magnitude and vector addition. However, we must remember that the sum of two unit quaternions does not represent a 3D rotation, because such a quaternion would not be of unit length. As a result, you won’t see any quaternion sums in a game engine, unless they are scaled in some way to preserve the unit length requirement.

from Gregory, J. (2009). Game Engine Architecture (3rd ed.), 3D Math for Games, Quaternions (pp. 394-403).

Normalizing is a fairly expensive operation so you should not do that unless you have to. What you can do instead is to multiply them. I don't know how many types of quaternion multiplications there are but I know that Grassman product is used to apply (concatenate) rotations.

@kenanmarasli Sorry for the late response!

You were right! Glmsharp implements the Grassman product for mulitplication, and together with the following formula for applying angular momentum (I only found this recently):

θ = |∆t * ω²|
a = ω² / |ω²| //(ω² normalized)
q² = [cos(θ / 2), a * sin(θ / 2)] ∗ q¹

I was able to implement angular momentum without having to normalize:

var theta = (dt * AngularVelocity).Length;
var a = AngularVelocity.Normalized;
Transform.Rotation = new quat(a * Math.Sin(theta / 2), Math.Cos(theta / 2)) * Transform.Rotation;
//Glmsharp's quat constructor takes the scalar as the last argument, rather than first like in the formula above

Thanks a bunch!

I'm glad @Etny

Now you have saved yourself a normalization operation ?

Advertisement

kenanmarasli said:

I'm glad @Etny

Now you have saved yourself a normalization operation ?

I don't know if you are being facetious, but trigonometric operations are much more expensive than normalizing a quaternion (scaling by 1/sqrt(x^2+y^2+z^2+w^2), which can be made faster using a fast inverse-square-root function).

alvaro said:

kenanmarasli said:

I'm glad @Etny

Now you have saved yourself a normalization operation ?

I don't know if you are being facetious, but trigonometric operations are much more expensive than normalizing a quaternion (scaling by 1/sqrt(x^2+y^2+z^2+w^2), which can be made faster using a fast inverse-square-root function).

This has nothing to do with being facetious.

Constructing rotations and concatenating them are different things. I have suggested the Grassman Product for concatenating rotations, which is implemented as a collection of basic arithmetic operations and dot products, all of which can be pipelined, and accelerated with SIMD architecture.

Your objection lies on how to create a quaternion representation of a rotation, which is different than the Grassman Product.

This topic is closed to new replies.

Advertisement