Why is Quaternion.FromToRotation() not working properly?

I’m trying to make a space game involving moving around on procedurally generated planets. I’m trying to get gravity working properly using a simple sphere to represent a planet, and a capsule to represent a player. I followed Sebastian Lague’s “Faux Gravity” tutorial video, and implemented the code. The gravity part works just fine; the capsule falls in the direction of the planet. However, the capsule does NOT orient its rotation properly.

The function that is supposed to line up the capsule in the direction of gravity is as follows:

body.rotation = Quaternion.FromToRotation(bodyUp, gravityUp);

where body is the rigidbody reference to the capsule, bodyUp is the vector for the local upward direction of the capsule, and gravityUp is the normalized vector for the direction between the planet’s center and the capsule.

The function as I understand it is supposed to orient the capsule so its up direction matches the gravity direction. Instead, when the capsule moves down from the top of the sphere, it leans upward (in world space, so on the planet surface sideways), to the point that it leans completely on its side at the bottom and movement (which is based around local space) becomes impossible.

Does anyone know why this is not working and/or have a solution?

Try

body.rotation *= Quaternion.FromToRotation(bodyUp, gravityUp);

instead. To explain - Quaternion.FromToRotation give the rotation to rotate one vector into another vector. So, in this case, the resulting quaternion will represent the rotation that will transform our objects up vector into the gravity normal vector. However we don’t want to set this as the rotation, because now the relative rotation we just calculated will become our absolute orientation, which we definitely don’t want. Instead, we want to add this rotation to our objects current orientation. To add a rotation to your current orientation, you can multiply the orientation by the desired rotation.

@wrmungas
Try multiplying the FromToRotation by body’s rotation, like this:

body.rotation = Quaternion.FromToRotation(bodyUp, gravityUp) * body.rotation

Thank you guys both! I probably just missed a multiply sign from the video or something :slight_smile:

Apologies for necroposting this, but it’s right on the question I’m pursuing. The documentation’s example uses the multiplication assignment operator:

transform.rotation *= Quaternion.FromToRotation(transform.up, Vector3.up);

That’s the same as:

transform.rotation = transform.rotation * Quaternion.FromToRotation(transform.up, Vector3.up);

In my tests, neither works. But Krystian’s code does work:

transform.rotation = Quaternion.FromToRotation(transform.up, Vector3.up) * transform.rotation;

I’m not surprised they have different results, as Quaternion multiplication is not commutative. But I am surprised that the second one works, and not the first. If the FromToRotation method returns a rotation that rotates from the transform’s up vector to the global up vector, it seems that post-multiplication would be the right choice.

What am I missing?

I’m not super clear on all of the quat math but I believe it depends on the reference frame of the new rotation. There’s two ways to apply the rotation:

a. transform.rotation *= q is local
b. transform.rotation = transform.rotation * q is also local
c. transform.rotation = q * transform.rotation is world (eg, what is q?)

Example:

If you had a rocket on your desk, but the nose isn’t pointing straight up like you want (transform.up should be vector3.up). If you do k = Quaternion.FromToRotation(transform.up, Vector3.up) you calculate a rotation that can rotate the rocket by k to make it point up. That’s great, thats what we want.

How? Well, you have two options. Left or right. Pre-multiply or Post-multiply.

Right/Post:
transform.rotation = transform.rotation * k means "Rotate the rocket around its own tilted body.” (the reference frame is local).

Left/Pre:
transform.rotation = k * transform.rotation means “Rotate the rocket in the room’s space so its nose points up.” (the reference frame is world).

Basically, the order determines the reference frame, but you also have to understand your inputs and their frame.

Ah, yes! The Quaternion returned by FromToRotation is for world space. So one must pre-multiply if one wants the result to apply in world space. I think this means the Unity documentation on this is wrong. I can easily set up an initial orientation (Euler angles 90, 0, 90) that, when post-multiplying as per this example:

transform.rotation *= Quaternion.FromToRotation(transform.up, Vector3.up);

does not align the transform’s y-axis with the global y-axis. Rewriting that to use pre-multiplication:

transform.rotation = Quaternion.FromToRotation(transform.up, Vector3.up) * transform.rotation;

does work, for precisely the reason you gave.

Interestingly, the older documentation (c. Unity 5) was even worse. It didn’t multiply at all. It just replaced transform.rotation with the Quaternion returned by FromToRotation. Also interestingly, for some cases, the example given does work (angles (90, 0, 0), or angles (0, 0, 90)). But not in general.

Do you think I should submit a comment on the documentation page?

Thanks for explaining this!