Quaternion FromToRotaion confusion

Ok Quaternions confuse the heck out of me, and if i’m honest I don’t have a great grasp of most 3d math to begin with.

Thing is I have some code that I thought was fine but is now failing in certain conditions and I’m not sure why.

If I raycast an object down to an irregular surface, i then rotate the object to align to the surface normal of where the raycast hit. I do this as follows:

myTransform.rotation *= Quaternion.FomToRotation(myTransform.up, hitNormal);

This works if I spawn an object and drop it down. However if I spawn an object, rotate it randomly along its Up vector(which in this case is the same as world, Vector3.Up) ad THEN drop it down, the rotation I get is often wrong.

I added some debug code to show what my object’s up vector is after the rotation to align to the normal and I noticed something weird. Often the X and Z components of the final up vector are transposed in relation to he surface normal vector.

In other words if the raycast hit.normal is (-0.1, 0.9, 0.5) after using Quaternion.FromToRotation, my object’s Up vector is (0.5, 0.9, -0.1).

Like I said this is an area I am woefully inept at so it figured this is where the bug would be. Can anyone give me some guidance on what I may be missing o where I might be looking to figure out what is going on?

Hi @jwvanderbeck

I don’t think Quaternion.FromToRotation is promising to keep your original heading i.e. transform.forward, AFAIK it is just a Quaternion (which I don’t either know much about) rotation that points correctly towards your hit normal.

What I would do to keep heading:

  1. Get transform right direction vector.

  2. Get hit normal.

  3. Calculate Vector3 cross product of these two, to get new forward vector that is perpendicular to hit normal and current right vector of transform.

  4. Use Quaternion.LookRotation with this new forward vector and hit normal as up direction instead of using Quaternion.FromToRotation.

I think you are doing your multiplication backwards. (Multiplication is not commutative for Quaternions.)

You wrote
myTransform.rotation *= Quaternion.FomToRotation(myTransform.up, hitNormal);
which is equivalent to
myTransform.rotation = myTransform.rotation * Quaternion.FomToRotation(myTransform.up, hitNormal);
but I think you want
myTransform.rotation = Quaternion.FomToRotation(myTransform.up, hitNormal) * myTransform.rotation;

1 Like

Despite seeming simple, this is a tricky application. You kinda have to swap the problem around in your head.

In your case, you have an object rotated to a certain heading and you’re saying “I want you to align to the ground as if I dropped you there.”

This won’t work by asking this question in this fashion, because it “loses” the heading information you put in.

Instead you want to ask the question,

“I am looking this way, how do I look forward (in the direction I was heading), but tilted so the top of my head points directly away from the ground slope here?”

The way I chose to do it is to make three proxy objects that each raycast themselves to the ground right near the player.

I call those objects LEFT, RIGHT and AFT, and it’s like an inverted tricycle: LEFT and RIGHT are ahead of me, AFT is behind me.

To update this I create a Plane defined from those three points (wherever they lie on the ground around my normal point), and make the following three calculations:

        Plane p = new Plane (TL.position, TR.position, TAFT.position);
        Vector3 fwd = (TL.position + TR.position) * 0.5f - TAFT.position;
        transform.parent.LookAt (transform.position + fwd, p.normal);

TL is the LEFT transform, TR is the RIGHT transform, and TAFT is the aft transform.

During play those are invisible GameObjects that I create, then every frame BEFORE doing the above, I raycast them straight down from my tank, offset slightly LEFT, RIGHT and AFT from my current heading, and then do the above three lines of computation.

You could use the single normal calculation to synthesize LEFT, RIGHT and AFT proxy objects too, but it won’t be as smooth when you hit a seam, since you’re only sampling a single normal. But for static-dropped objects it might be fine.

Wow thank you very much. I think this might be the answer. The way I was doing it was the way most examples show it and I ever stopped to consider that it wasn’t commutative. I need to do some more testing but at first glance swapping the multiplication order seems to fix the problem.

EDIT: Stupid T key