703
2013-03-13 23:18:26 +0100
Quaternion SLERP not working jumping and returning incorrect interpolation
2013-04-09 01:37:16 +0200
1
1
3
JogAmp
Jogl
graph
2
All
all
RESOLVED
FIXED
P4
critical
---
0
fwdk98003
rami.santina
gouessej
harvey.harrison
org.jogamp
rami.santina
sgothel
---
2b3bb9426385d97375c3312f5c0f4e2a827b1fbb
---
oldest_to_newest
2266
0
fwdk98003
2013-03-13 23:18:26 +0100
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.
2267
1
sgothel
2013-03-14 00:23:33 +0100
Thank you Tek, I hope Rami can solve it.
2447
2
org.jogamp
2013-04-08 17:52:05 +0200
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.
2448
3
harvey.harrison
2013-04-08 18:07:29 +0200
(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
2456
4
harvey.harrison
2013-04-09 00:33:33 +0200
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;
}
2457
5
sgothel
2013-04-09 01:15:30 +0200
(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.)
2458
6
fwdk98003
2013-04-09 01:37:16 +0200
Let me test the code. I'll get back to you in few hours