Search Unity

I don't understand this Quaternion.FromToRotation code behavior.

Discussion in 'Getting Started' started by Deleted User, Mar 1, 2016.

  1. Deleted User

    Deleted User

    Guest

    So I am still just playing with manipulating rigidbodies using the quaternion methods (functions? bah).

    I had to add the singleton to this code to make it work. Without the singleton the object begins to exhibit the expect rotation, but something about the logic loop causes it to stop and jitter.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class flyAI2 : MonoBehaviour {
    5.  
    6.     private Rigidbody flyBody;
    7.     private Vector3 randomVector;
    8.     private Quaternion targetRotation;
    9.     private bool once = false;
    10.  
    11.     void Start () {
    12.         flyBody = gameObject.GetComponent<Rigidbody>();
    13.         randomVector = Random.onUnitSphere;
    14.     }
    15.  
    16.     void Update () {
    17.  
    18.         if(once == false)
    19.         {
    20.  
    21.         targetRotation = Quaternion.FromToRotation(transform.forward, randomVector);
    22.  
    23.             once = true;
    24.         }
    25.  
    26.         transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, 90 * Time.deltaTime);
    27.  
    28.         Debug.DrawRay(flyBody.position, flyBody.transform.forward * 5, Color.blue, 0.01f);
    29.         Debug.DrawRay(flyBody.position, flyBody.transform.up * 5, Color.green, 0.01f);
    30.         Debug.DrawRay(flyBody.position, flyBody.transform.right * 5, Color.red, 0.01f);
    31.         Debug.DrawRay(flyBody.position, randomVector * 2.5f, Color.yellow, 0.01f);
    32.     }
    33. }
    34.  
    Okay so in my head the code in the singleton doesn't need to be. It would produce a tangent (I think) but it stops at half the distance to the target vector.
     
    Last edited by a moderator: Mar 2, 2016
  2. Deleted User

    Deleted User

    Guest

    I think I'm starting to get it. This page is helping. I have/had a fundamental misunderstanding of what quaternion rotations are.

    OK I shouldn't have been using Quaternion.RotateTowards.
     
    Last edited by a moderator: Mar 2, 2016
    questroojustin likes this.
  3. Deleted User

    Deleted User

    Guest

    This is my confusion: A quaternion just stores some rotation, no different than a set of euler angles would- but in a mathematically more robust way. It doesn't contain time or distance or anything it's just a point on a sphere.

    "FromToRotation Creates a rotation which rotates from fromDirection to toDirection."
    I can't understand what that would mean."which rotates" - if you use the line of code in the API:

    Code (CSharp):
    1. transform.rotation = Quaternion.FromToRotation(Vector3.up, transform.forward);
    It just creates a quaternion pointed at -Y. It rotated the object 90 degrees.

    I needs to say "FromToRotation Creates a rotation rotated from fromDirection to toDirection."

    I'm all sitting here thinking like, "quaternions rotate?!?" all evening. I guess? I don't freaking know. Someone like, delete this thread.
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    A quaternion is a rotation. It's also an orientation. These are two ways of looking at the same thing.

    Maybe it'll help to back it up to Vector3. A 3D vector is a position. It's also a change in position, if you choose to look at it that way. Suppose I have a Vector3 (2,6,0). If I treat this as a position, then it just means X=2, Y=6, Z=0. But if I add it to some previous position, then this vector is a change in position, and it means X +=2, Y +=6, Z += 0. (And it can also mean other things, like a speed, which is a change in position per second — all depending on how I use it.) Or, you can think of every vector as a change in position, and when you want to think of it as an absolute position, then you start at (i.e. add it to) Vector3.zero instead of something else.

    OK, same thing with quaternions. A quaternion represents a rotation, i.e. a change in orientation. You can compose (think "add" though that's not technically the correct term) it with any previous orientation to get a new orientation. Or you can compose it with the default orientation (Quaternion.identity) to treat it as an absolute orientation. Or you can treat it as rotation per second, and now it's a rotational velocity. It's all in how you use it... but fundamentally, it's a rotation (or orientation).

    So, yeah, FromToRotation returns a Quaternion that would rotate the first vector so that it matches the second vector.
     
  5. Deleted User

    Deleted User

    Guest

    I think I understand better today than I did yesterday. I'm still at that stage where if I don't think about it every day I'll probably get confused again though.

    A single axis pseudocode example case of FromToRotation might be:

    We are at 45 degrees. We FromToRotation (where we're at, 90 degrees).

    We are now at 90 degrees. We FromToRotation (origin 0 identity, -90 degrees).

    We are not at 0 degrees.

    Although that's obviously not how quaternions themselves work but it helps me understand what this function does. I didn't need to be using FromToRotation at all, either.
     
  6. Deleted User

    Deleted User

    Guest

    To be frank I don't understand a lot about Unity still, for example - Debug.Log is actually more expensive than Debug.DrawRay (presumably because it's writing to a file somewhere). That one caught me off guard. I'm one boolean nest in and wondering why things are grinding to a halt when I look at the profiler the first time and Debug calls are friggin' %70 of my CPU time.
     
    john_goren likes this.
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,113
    You've probably moved on and/or learned quaternions by now, but maybe this will help someone else.
    Try to imagine quaternions as colors but with four color components instead of 3 (let's say there is R, G, B, @, not like alpha but some peculiar color we're not able to see). Imagine this kind of "color" as a modifier, it doesn't do anything on its own, it can't be displayed, but can be multiplied with a physical color to modify it and produce a new one.

    Now, if that "color" is Quaternion.identity, if you multiply it with any other color, it stays the same.

    However, if you modify any of its components, and then apply it to a color, you'll get something different. How much different, depends on the relative strength of its components.

    Now when the docs say "FromToRotation Creates a rotation which rotates from fromDirection to toDirection" it could've been worded better, but they really mean that such quaternion will represent whatever rotation that modifies fromDirection so that it becomes toDirection, if it's applied to it. You could apply it to something else as well, but it will carry the same rotational information with it, essentially representing both rotation AND orientation at the same time, depending on how you want to use it. It's just a modifier.

    Basically: Quaternion.FromToRotation(dir1, dir2) * dir1 == dir2
    The multiplication part is how we apply quaternions to vectors, and they always go on the left side.

    Think about this through the colors: "FromToColor Creates a color which modifies from fromColor to toColor".

    You're probably thinking "What a stupid analogy, why talking about colors at all", however quaternion rotations DO actually blend like colors, it's just that their individual rotational components seem so unintuitive in our three dimensions.

    If that's hard to grasp, try this video, watch it to the end.
     
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,113
    Oh and btw Debug calls ARE expensive, mostly because they use reflection to dump the entire stack trace (click on an individual entry to be able to see it, every entry is a wall of text), but also because they are pulled through some serious infrastructure in the background, some of which likely encumbers the Unity's UI and slows down memory access.

    Use it scarcely and intelligently. You can also use global flags and #if preprocessing to selectively hide certain reports from your test builds.
     
    benthroop, JeffDUnity3D and JoeStrout like this.
  9. entirelydoomed

    entirelydoomed

    Joined:
    Mar 26, 2021
    Posts:
    67
    It's definitely an old thread but i think it's initial topic really suits what i am facing now. For some reason when using Quaternion.FromToRotation i run into gimbal lock apparently?? At some rotation my object starts slowly spinning. Strangely though i am not having this problem if i use Quaternion.LookRotation. Here's the code to compare:
    Code (CSharp):
    1. bones[i].rotation = Quaternion.FromToRotation(Vector3.up, bones[i + 1].position - bones[i].position) * Quaternion.Inverse(bone_start_rot[i]);
    Code (CSharp):
    1. bones[i].rotation = Quaternion.LookRotation(transform.forward, bones[i + 1].position - bones[i].position) * Quaternion.Inverse(bone_start_rot[i]);
    Also it was a huge surprise for me that FromToRotation assumes that 1st argument is passed in local space. Or if i'm wrong why doesn't it work if i swap Vector3.up with transfrom.up?
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    There is no gimbal lock with this method. You may be running into edge cases where your two vectors are so close that the inferred axis (the cross product between them) isn't what you think it should be, or something like that.

    FromToRotation, and quaternions in general, assume nothing about the coordinate system. If you pass in vectors in world space, you get a quaternion which you probably want to interpret as a world rotation. If you pass in local vectors, you would consider the result to be a local rotation. If you pass in a mix of world and local vectors, then of course it's garbage in, garbage out.
     
    orionsyndrome likes this.
  11. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,113
    As long as your two vectors belong to the same frame of reference, the final quaternion will be valid for that frame of reference. Note that FromToRotation is behaving particularly bad if the vectors are antiparallel, because the actual rotation path is mathematically undefined (think of a rotation between North and South pole). Also there is a rare event of discontinuity with this method due to computation order in Unity's implementation (as far as I can tell it happens only with axis aligned rotations, more on this here, with some demo code and a workaround), but one thing is certain: there is definitely no gimbal lock with quaternions.
     
  12. entirelydoomed

    entirelydoomed

    Joined:
    Mar 26, 2021
    Posts:
    67
    Yeah sorry, thank you all for the help, i've figured the problem out, because when my parent bone rotates to face child bone, child bone changes it's position so it's an infinite cycle. What i did was to store child bone position, rotate parent bone and then set child bone position back to stored value.
     
    JoeStrout likes this.
  13. adeick8

    adeick8

    Joined:
    Jan 4, 2023
    Posts:
    8
    Sorry I'm necro-ing this thread but this is super helpful information and I've come to this thread multiple times when trying to understand Quaternions.

    The differentiation between orientation and rotation is super helpful to me, but I'm still confused about a couple things.

    Vector3 can be a position, or a change in position. If I was going to make a method call FromToPosition, then it would probably return a Vector3 representing a change in position. So then I would add that (+=) to my original position to get the new position, I wouldn't override the original position (=).

    Quaternions can be an orientation, or a rotation (change in orientation). The method called FromToRotation seems like it returns a rotation (change in orientation). Instead of multiplying it (*=) to the original position, the Unity docs have an example of it overriding the original rotation(=).

    To restate, it seems like the docs are replacing a permanent/orientation Quaternion with a temporary/rotation Quaternion (generated by FromToRotation).
    Am I misunderstanding how Quaternions work or are the docs just poorly written?
    upload_2023-12-18_16-12-13.png
     
  14. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,445
    You're getting it, just fine. The distinction between your two terms 'B' and 'change in B' are really very close. Vector3 as a position, is simply thinking about a Vector3 as a change in position starting from Vector3.zero. Quaternion as an orientation, is simply thinking about a Quaternion as a change in orientation starting from Quaternion.identity.

    When changing model formats between different modeling tools, it is a massively common requirement to convert from one axis scheme to another. Modeling tools might consider the +Z axis as being altitude/height/up. Unity thinks of the +Y axis as being altitude/height. Sometimes the importer doesn't do this step, or sometimes the script-writer prefers to make corrections. So in this case, done only once at the moment of the object's
    Start()
    message callback, a canned change in orientation is done. If their example did this on every Update() call, then you'd be right to be confused about the purpose of continually fixing the orientation instead of applying a smaller continual change. As with all of the documentation, the usage is slightly contrived, or not following best practices, but not out of the realm of possibility. Its purpose is just to show how to call the API.
     
  15. adeick8

    adeick8

    Joined:
    Jan 4, 2023
    Posts:
    8
    Got it. Thanks!