Jump to content

Lerping quaternions the long way


Numa
 Share

Recommended Posts

Hi there, 

by default it seems the ANIMATIONTYPE_QUATERNION slerps between 2 quaternions the short way (270 degrees turns into -90 for instance).

If I compute the angle between the 2 quaterinon and determine that it's more than 180, how would I go about slerping it the long way?

I tried looking at the code but variable names such as num1 num2 num3 num4 num5 num6 didn't really help :) Any help appreciated!

 

Cheers,

Quaternion.Slerp = function (left, right, amount) {
            var num2;
            var num3;
            var num = amount;
            var num4 = (((left.x * right.x) + (left.y * right.y)) + (left.z * right.z)) + (left.w * right.w);
            var flag = false;
            if (num4 < 0) {
                flag = true;
                num4 = -num4;
            }
            if (num4 > 0.999999) {
                num3 = 1 - num;
                num2 = flag ? -num : num;
            }
            else {
                var num5 = Math.acos(num4);
                var num6 = (1.0 / Math.sin(num5));
                num3 = (Math.sin((1.0 - num) * num5)) * num6;
                num2 = flag ? ((-Math.sin(num * num5)) * num6) : ((Math.sin(num * num5)) * num6);
            }
            return new Quaternion((num3 * left.x) + (num2 * right.x), (num3 * left.y) + (num2 * right.y), (num3 * left.z) + (num2 * right.z), (num3 * left.w) + (num2 * right.w));
        };

 

Link to comment
Share on other sites

The slerp function explicitly flips things when the angle is greater than 180 (when num4 < 0). I tried to take all that out but it caused some objects in my scene to be moved infinitely in a direction:

Quaternion.Slerp = function (left, right, amount) {
            var num2;
            var num3;
            var num = amount;
            var num4 = (((left.x * right.x) + (left.y * right.y)) + (left.z * right.z)) + (left.w * right.w);
            /*var flag = false;*/
            if (num4 < 0) {
                //flag = true;
                //num4 = -num4;
            }
            if (num4 > 0.999999) {
                num3 = 1 - num;
                num2 = /*flag ? -num :*/ num;
            }
            else {
                var num5 = Math.acos(num4);
                var num6 = (1.0 / Math.sin(num5));
                num3 = (Math.sin((1.0 - num) * num5)) * num6;
                num2 = /*flag ? ((-Math.sin(num * num5)) * num6) : */((Math.sin(num * num5)) * num6);
            }
            return new Quaternion((num3 * left.x) + (num2 * right.x), (num3 * left.y) + (num2 * right.y), (num3 * left.z) + (num2 * right.z), (num3 * left.w) + (num2 * right.w));
        };

 

Link to comment
Share on other sites

I also tried to reproduce the Slerp alogithm from the Ogre engine, I'm pretty sure I got it right but it still doesn't rotate properly / makes objects disappear until the last frame is reached:

Quaternion.Slerp = function (left, right, amount) {

            var shortestPath = false;
            
            var dotProduct = (left.x * right.x) + (left.y * right.y) + (left.z * right.z) + (left.w * right.w);
            
            if (dotProduct < 0.0 && shortestPath)
            {
                dotProduct = -dotProduct;
                right = right.scale(-1);
            }            

            if (Math.abs(dotProduct < 0.99999))
            {
                var sin = Math.sqrt(1 - Math.sqrt(dotProduct));
                var angle = Math.atan2(sin, dotProduct);
                var inverseSin = 1.0 / sin;
                var coeff0 = Math.sin((1.0 - amount) * angle) * inverseSin;
                var coeff1 = Math.sin(amount * angle) * inverseSin;

                return new Quaternion((coeff0 * left.x) + (coeff1 * right.x), (coeff0 * left.y) + (coeff1 * right.y), (coeff0 * left.z) + (coeff1 * right.z), (coeff0 * left.w) + (coeff1 * right.w));
            }
            else
            {            
                var coeff0 = (1.0 - amount);
                var coeff1 = amount;

                var t = new Quaternion((coeff0 * left.x) + (coeff1 * right.x), (coeff0 * left.y) + (coeff1 * right.y), (coeff0 * left.z) + (coeff1 * right.z), (coeff0 * left.w) + (coeff1 * right.w));
                return t.normalize();
            }
        };

 

Link to comment
Share on other sites

12 hours ago, adam said:

http://www.babylonjs-playground.com/#2INAYY#3

I commented out the

if (num4 > 0.999999) {

in BABYLON.Quaternion.

Oh yeah it actually works in the playground!

It still doesn't work on my project though, everything disappears during the animation and I only get the last frame. Any idea why? I tried plugging in my values in the playground and they work fine but for some reason it breaks my animation, and there's nothing in the logs.

 

 

 

----------------------
That IF actually needs to be there (it still works if you leave it as long as you remove the flag part).

If the dot product of 2 quaternions nears -1, they are nearly parallel and the spherical interpolation isn't numerically stable so we must do a linear interpolation there.

If the dot product nears 1, they are almost identical and there is no need for a spherical interpolation either so again we do a linear one.

that IF normally checks for the dot product's absolute value since we flip num4 if it's negative. If you get rid of that first IF then you need to use Math.abs otherwise we're missing the case where the dot product nears -1. So the long-way version should look like this:

BABYLON.Quaternion.Slerp = function (left, right, amount) {
            var num2;
            var num3;
            var num = amount;
            var num4 = (((left.x * right.x) + (left.y * right.y)) + (left.z * right.z)) + (left.w * right.w);
            if (Math.abs(num4) > 0.999999) {
                
                // DO LINEAR INTERPOLATION
                num3 = 1 - num;
                num2 = num;
                // which later translates to: (1.0 - amount) * left + amount * right;                
            }
            else {
                var num5 = Math.acos(num4);
                var num6 = (1.0 / Math.sin(num5));
                num3 = (Math.sin((1.0 - num) * num5)) * num6;
                num2 = (Math.sin(num * num5)) * num6);
            }
            return new BABYLON.Quaternion((num3 * left.x) + (num2 * right.x), (num3 * left.y) + (num2 * right.y), (num3 * left.z) + (num2 * right.z), (num3 * left.w) + (num2 * right.w));
        };

 

Link to comment
Share on other sites

Hi, sorry I've been away for a while. It turns out those fixes worked fine, my graphics card seems to be the issue, it's a bit old and for some reason was giving up on the animation all together. I tried it on a different computer and it played fine both ways :)

 

Thanks for your help everyone :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...