- Home /
Not interpolate quaternion on shortest path?
Hi,
I have been searching a long time but I did not find something that hints at a solution. Basically I want to smoothly interpolate between two Rotations. However I do not want to interpolate along the shortest path.
When I use Quaternion.Lerp, SLerp or RoateTowards it would rotate 90 degrees when interpolating between (in eulers) (0,270,0) and (0,0,0), but I want it to rotate 270 degrees.
I tried using eulerAngles on both source and target rotation, interpolating the vectors and reconverting them into quaternions with Quaternion.Euler but that gives me the same results.
Is there an inbuilt way to use this "non-optimal" interpolation?
greetings
I was just about to write that this does not work either, since I already tried it, but it got me thinking.
$$anonymous$$y problem originated from the fact that rotation.eulerAngles is clamped between 0 and 360 and that is why I was interpreting the interpolation wrong.
I found that localEulerAngles contains the rotation displayed in the editor, so I can work with that.
Thank you for your time
Edit: how do I check a question answered? Does convert to answer do this, or does this only convert my comment?
Answer by robertbu · Jun 21, 2013 at 02:53 PM
If you do some searching around on UA, you will find a number of answers to this question. One answer is to treat eulerAngles as write-only and maintain your own Vector3. Here is one of those answers:
http://answers.unity3d.com/questions/475760/rotation-direction-in-coroutine.html
@FreetimeCoder - eulerAngles has a habit of changing representation on you. You can get around the 0/360 boundary by changing the representation in the Vector3 you maintain.
Ok, but is there a reason not to use localEulerAngles? From what I can tell it does exactly what I need.
@FreetimeCoder - As explained in the answer at the link provided, eulerAngles can change its representation. Write a bit of code that sets the eulerAngles to (180,0,0), and then use a Debug.Log() statement to read it back. You will get (0,180,180). eulerAngles changes in a predictable pattern, so if you are careful, it may work for you. But as stated, it is safer to keep your own Vector3 and never read eulerAngles.
I see what you mean, however I cannot use that method because the first time I get a rotation is when the level started and I read localEulerAngles from the gameobject's transformation. I never modify it afterwards only interpolate something else between different objects' transformations.
Ok, I found that transform.localRotation is much better than localEulerAngles, because the later sometimes even changes between in-editor and in-game while the localRotation Quaternion does not.
(I must confess this again takes me back to the "shortest path" thing, but I have given up on trying to acheive a perfect solution)
I think this solves this topic.
Answer by Maeslezo · Jan 27, 2017 at 09:14 AM
I had the same problem, so I have written the lerp and slerp myself, with a flag to indicate whether you want to force the short way or not. I am pretty sure that Unity checks that to invert the rotation. With this code, you can indicate if you want that behaviour.
I have checked myself and it seems it works ok, but check yourself just in case:
public static class QuaternionExtension
{
public static Quaternion Lerp(Quaternion p, Quaternion q, float t, bool shortWay)
{
if (shortWay)
{
float dot = Quaternion.Dot(p, q);
if (dot < 0.0f)
return Lerp(ScalarMultiply(p, -1.0f), q, t, true);
}
Quaternion r = Quaternion.identity;
r.x = p.x * (1f - t) + q.x * (t);
r.y = p.y * (1f - t) + q.y * (t);
r.z = p.z * (1f - t) + q.z * (t);
r.w = p.w * (1f - t) + q.w * (t);
return r;
}
public static Quaternion Slerp(Quaternion p, Quaternion q, float t, bool shortWay)
{
float dot = Quaternion.Dot(p, q);
if (shortWay)
{
if (dot < 0.0f)
return Slerp(ScalarMultiply(p, -1.0f), q, t, true);
}
float angle = Mathf.Acos(dot);
Quaternion first = ScalarMultiply(p, Mathf.Sin((1f - t) * angle));
Quaternion second = ScalarMultiply(q, Mathf.Sin((t) * angle));
float division = 1f / Mathf.Sin(angle);
return ScalarMultiply(Add(first, second), division);
}
public static Quaternion ScalarMultiply(Quaternion input, float scalar)
{
return new Quaternion(input.x * scalar, input.y * scalar, input.z * scalar, input.w * scalar);
}
public static Quaternion Add(Quaternion p, Quaternion q)
{
return new Quaternion(p.x + q.x, p.y + q.y, p.z + q.z, p.w + q.w);
}
}
An implementation that's a bit more optimised would be:
public static Quaternion Slerp(Quaternion p, Quaternion q, float t, bool shortWay)
{
float dot = Quaternion.Dot(p, q);
float sign = (shortWay && dot < 0.0f) ?-1f : 1f;
float angle = Mathf.Acos(dot * sign);
float division = 1f / Mathf.Sin(angle);
float t0 = Mathf.Sin((1f - t) * angle) * division * sign;
float t1 = Mathf.Sin((t) * angle) * division;
return new Quaternion(
p.x*t0 + q.x * t1,
p.y*t0 + q.y * t1,
p.z*t0 + q.z * t1,
p.w*t0 + q.w * t1
);
}
Note that I currently can't test this method as I don't have Unity at hand. I can also not guarantee that it works as I haven't verified the original method, but it looks right. My version is just a simplification / inlining of the original post. It reduces the method calls and also the required multiplications
Imagine if Unity made it easy for us and added this solution and all you'd have to do is to set an additional bool param, wouldn't that be awesome ... I've had to look up this issue so many times over the years and always ended up implementing some length workaround.
Answer by wolfgraphicsLLC · Jan 28, 2021 at 07:06 PM
ok @FreetimeCoder i know of at least 2 ways to fix that off the top of my head first as a dev not as a just programmer how is your object set up wat does the scenario look like in the scene view and as well the inspector setting s and setup in hierarchy cold you either upload a sort video or a couple screen shots showing this? or another option is you could go to discord to https://discord.gg/gjWDxcPC and go to the programing and general unity channel and @ john nic?9wolfgraphicsllc) that's me I am one of the tutors and moderators there as well there is a create a ticket channel where you can get a little more focused assistance. and i would be glad to look it over and walk you through and trouble shoot it with you for like i always say when it comes to dev work and coding alike there is always more than one way to skinn a cat