Bug 703 - Quaternion SLERP not working jumping and returning incorrect interpolation
Summary: Quaternion SLERP not working jumping and returning incorrect interpolation
Status: RESOLVED FIXED
Alias: None
Product: Jogl
Classification: JogAmp
Component: graph (show other bugs)
Version: 2
Hardware: All all
: P4 critical
Assignee: Rami Santina
URL:
Depends on:
Blocks:
 
Reported: 2013-03-13 23:18 CET by Tek
Modified: 2013-04-09 01:37 CEST (History)
5 users (show)

See Also:
Type: ---
SCM Refs:
2b3bb9426385d97375c3312f5c0f4e2a827b1fbb
Workaround: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tek 2013-03-13 23:18:26 CET
It's Tek again with quaternion class.  
I've been using jogamp.graph.math.Quaternion.slerp() to rotate our model but it seems like slerp() is implemented incorrectly or I am not understanding the math behind it.  



Currently, the slerp code does the following:
        if ((1.0f+cosom) > MathFloat.E) {
            ...
        }
        else {
            x =-a.y;
            y = a.x;
            z =-a.w;
            w = a.z;
            sclp = MathFloat.sin((1.0f-t) * MathFloat.PI * 0.5f);
            sclq = MathFloat.sin(t * MathFloat.PI * 0.5f);
            x = sclp*a.x + sclq*b.x;
            y = sclp*a.y + sclq*b.y;
            z = sclp*a.z + sclq*b.z;
        }

I am not sure why anyone would set x value to a.y, y to a.x, z to a.w, and w to a.z.  This explains why my quaternion is jumping during animation.

I believe "if ((1.0f+cosom) > MathFloat.E)" is trying to see if cosom is negative but I am not sure why only x and z will have opposite of a.y and a.w.

Also, angle, w, is not being set at all so that explains why my model doesn't animate until the last frame of display.

I am not a Quaternion expert so I don't know the full math behind Quaternion but if I am misinterpreting the slerp(), please let me know.  

I've implemented a slerp method according to Nick Bobic (http://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php?page=2)

/** Set this quaternion from a Sphereical interpolation
*  of two param quaternion, used mostly for rotational animation
* @param a initial quaternion
* @param b target quaternion
* @param t float between 0 and 1 representing interp.
*/
@Override
public void slerp(Quaternion a,Quaternion b, float t)
{
	float omega, cosom, sinom, sclp, sclq;
	cosom = a.getX()*b.getX() + a.getY()*b.getY() + a.getZ()*b.getZ() + a.getW()*b.getW();
	//adjust the signs (if necessary)
	if(cosom<0.0) {
        	cosom = -cosom;
        	b.setX(-b.getX());
 		b.setY(-b.getY());
 		b.setZ(-b.getZ());
 		b.setW(-b.getW());
 	}
 	// calculate coefficients
 	// standard case (slerp)
 	if ((1.0f-cosom) > MathFloat.E) {
        	omega = (float)MathFloat.acos(cosom);
        	sinom = (float)MathFloat.sin(omega);
 		sclp = (float)MathFloat.sin((1.0f-t)*omega) / sinom;
 		sclq = (float)MathFloat.sin(t*omega) / sinom;
 	}
 	else {
            // "from" and "to" quaternions are very close 
      	    //  ... so we can do a linear interpolation
 		sclp = 1.0f - t;
 		sclq = t;
 	}
 	//calculate the final values
 	x = sclp*a.getX() + sclq*b.getX();
 	y = sclp*a.getY() + sclq*b.getY();
 	z = sclp*a.getZ() + sclq*b.getZ();
 	w = sclp*a.getW() + sclq*b.getW();
 }
It's working correctly with my model and it was bit easier to understand.  Please advise that I had to override slerp() from a class that extended Quaternion.  Therefore, I had to use getters and setters.
Comment 1 Sven Gothel 2013-03-14 00:23:33 CET
Thank you Tek, I hope Rami can solve it.
Comment 2 Mark Raynsford 2013-04-08 17:52:05 CEST
This code looks correct to me.

It's derived from:

m = ((sin(θ) * (1 - t)) / sin(θ)) * qa
n = ((sin(θ) * t)       / sin(θ)) * qb

q(t) = m + n

Where:

θ = arcos(qa · qb)

According to "Mathematics for 3D Game Programming".

Unfortunately, I don't have unit tests for it.
Comment 3 Harvey Harrison 2013-04-08 18:07:29 CEST
(In reply to comment #2)
> This code looks correct to me.
> 
> It's derived from:
> 
> m = ((sin(θ) * (1 - t)) / sin(θ)) * qa
> n = ((sin(θ) * t)       / sin(θ)) * qb
> 

Hmm, something seems off in the above...doesn't that simplify
down to simple LERP

((sin(θ) * (1 - t)) / sin(θ))
..
(sin(θ) / sin(θ)) * (1 - t)
..
(1 - t)

Looking at the link, I think what you wanted was:

m = ((sin(θ * (1 - t))) / sin(θ)) * qa
n = ((sin(θ * t))       / sin(θ)) * qb

Also, the above falls down when sin(θ) == 0 (quarternions aligned or extactly 180 degress apart)

Have a look at the following:

http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/

Which covers the aligned and nearly-opposite cases correctly.

Harvey
Comment 4 Harvey Harrison 2013-04-09 00:33:33 CEST
Not compiled, not tested, just the code I have lying around from lunch.

public void slerp(Quaternion a, Quaternion b, float t) {
    float cosom = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
    float t1 = 1.0f - t;

    // if the two quaternions are close, just use linear interpolation
    if (cosom >= 0.95f) {
        x = a.x * t1 + b.x * t;
        y = a.y * t1 + b.y * t;
        z = a.z * t1 + b.z * t;
        w = a.w * t1 + b.w * t;
        return;
    }

    // the quaternions are nearly opposite, we can pick any axis normal to a,b
    // to do the rotation
    if (cosom <= -0.99f) {
        x = 0.5f * (a.x + b.x);
        y = 0.5f * (a.y + b.y);
        z = 0.5f * (a.z + b.z);
        w = 0.5f * (a.w + b.w);
        return;
    }

    // cosom is now withion range of acos, do a SLERP
    float sinom = FloatUtil.sqrt(1.0f - cosom * cosom);
    float omega = FloatUtil.acos(cosom);

    float scla = (float)FloatUtil.sin(t1 * omega) / sinom;
    float sclb = (float)FloatUtil.sin( t * omega) / sinom;

    x = a.x * scla + b.x * sclb;
    y = a.y * scla + b.y * sclb;
    z = a.z * scla + b.z * sclb;
    w = a.w * scla + b.w * sclb;
}
Comment 5 Sven Gothel 2013-04-09 01:15:30 CEST
(In reply to comment #4)
> Not compiled, not tested, just the code I have lying around from lunch.
> 
...

http://jogamp.org/git/?p=jogl.git;a=commit;h=2b3bb9426385d97375c3312f5c0f4e2a827b1fbb

Thank you!

@Tek: Please test. And if you can, please add a unit tests or two!
(Via email .. or better git pull req.)
Comment 6 Tek 2013-04-09 01:37:16 CEST
Let me test the code. I'll get back to you in few hours