Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » quaternion lerp instead of slerp

This thread is locked; no one can reply to it. rss feed Print
quaternion lerp instead of slerp
hribek
Member #10,549
January 2009

EDIT: sorry name of topic is bad.:-/ Lerp and Slerp should co-exist.

FIXME: this is the right place to post this stuff

WHY: reasons for why to use normalize(lerp(quaternion, quaternion)) over slerp(quaternion, quaterion):

  • faster

  • commutative

  • simpler to code (not a point if there is a code for slerp is it ;) ) & understand

WHY NOT:

HOW:
It might look something like. Different linnes are marked by ///.

1/// from 4.2.2's slerp
2/// should work, not tested !!!
3void quat_nlerp(AL_CONST QUAT *from, AL_CONST QUAT *to, float t, QUAT *out, int how)
4{
5 QUAT to2;
6 double angle;
7 double cos_angle;
8 double scale_from;
9 double scale_to;
10 double sin_angle;
11 double norm; /// *
12 ASSERT(from);
13 ASSERT(to);
14 ASSERT(out);
15 
16 cos_angle = (from->x * to->x) +
17 (from->y * to->y) +
18 (from->z * to->z) +
19 (from->w * to->w);
20 
21 if (((how == QUAT_SHORT) && (cos_angle < 0.0)) ||
22 ((how == QUAT_LONG) && (cos_angle > 0.0)) ||
23 ((how == QUAT_CW) && (from->w > to->w)) ||
24 ((how == QUAT_CCW) && (from->w < to->w))) {
25 cos_angle = -cos_angle;
26 to2.w = -to->w;
27 to2.x = -to->x;
28 to2.y = -to->y;
29 to2.z = -to->z;
30 }
31 else {
32 to2.w = to->w;
33 to2.x = to->x;
34 to2.y = to->y;
35 to2.z = to->z;
36 }
37 
38 if ((1.0 - ABS(cos_angle)) > EPSILON) {
39 ////* spherical linear interpolation (SLERP) */
40 /// angle = acos(cos_angle);
41 /// sin_angle = sin(angle);
42 scale_from = 1.0 - t; ///sin((1.0 - t) * angle) / sin_angle;
43 scale_to = t; ///sin(t * angle) / sin_angle;
44 }
45 else {
46 /* to prevent divide-by-zero, resort to linear interpolation */
47 scale_from = 1.0 - t;
48 scale_to = t;
49 }
50 
51 out->w = (float)((scale_from * from->w) + (scale_to * to2.w));
52 out->x = (float)((scale_from * from->x) + (scale_to * to2.x));
53 out->y = (float)((scale_from * from->y) + (scale_to * to2.y));
54 out->z = (float)((scale_from * from->z) + (scale_to * to2.z));
55 
56 // normalize; so that it remains on unit circle
57 norm = quat_normal(out); /// sqrt(norm) ?
58 ASSERT(norm != 0);
59 
60 out->w = out.w / norm; ///
61 out->x = out.x / norm; ///
62 out->y = out.y / norm; ///
63 out->z = out.z / norm; ///
64}

Elias
Member #358
May 2000

I don't understand what commutative means. Can you explain it a bit shorter and simpler than the papers do? Let's say, in my game I have a spaceship. It looks somewhere. Now I want it to look somewhere else. And I use slerp to interpolate between the two rotations. Would anything change if I use nlerp instead? If yes, how would it look different?

Also, I don't quite get how your /// comments work - just use a standard diff format, everyone in the dev forum will be used to reading that :)

--
"Either help out or stop whining" - Evert

hribek
Member #10,549
January 2009

Quote:

I don't understand what commutative means.

Commutativity means that it does not matter in which order you perform something. In case of nlerp You can imagine creating in-between rotations as addition of numbers. You can add them in any order. In case of rotations You can create working_rotation how You wish.

Quote:

Can you explain it a bit shorter and simpler than the papers do? Let's say, in my game I have a spaceship. It looks somewhere. Now I want it to look somewhere else. And I use slerp to interpolate between the two rotations. Would anything change if I use nlerp instead? If yes, how would it look different?

You will just get extra FPS (because You get rid of some expensive goniometry - sin(),acos()). Ship won't rotate at the same speed. Difference is minimal (some pictures and text from guy who "popularized" it)

AFAI understand when You animate something You can do that by just assigning weights (10 % walking, 30 % running, 60 % jumping) to bones (directions, quaternions) and You can nlerp between them.

>> diff
Well I thought it might be better to show whole code :-/

Elias
Member #358
May 2000

Quote:

Commutativity means that it does not matter in which order you perform something. In case of nlerp You can imagine creating in-between rotations as addition of numbers. You can add them in any order. In case of rotations You can create working_rotation how You wish.

Hm. I don't know enough about quaternions then I guess, would have to see actual example code using Allegro's slerp function, to see code which would not work with it but would work with nlerp.

Quote:

You will just get extra FPS (because You get rid of some expensive goniometry - sin(),acos()). Ship won't rotate at the same speed. Difference is minimal (some pictures and text from guy who "popularized" it)

I see. I think the correct solution is more important for us than a slight performance gain (which in a game you likely could not even measure). But if you use the quaternions in something like physics simulation, I'm sure a deviation by "only" 8 degrees would wreak havoc :P

--
"Either help out or stop whining" - Evert

hribek
Member #10,549
January 2009

from gamedev, posted by haegarr

Quote:

I like to make things complicated ;) and so I want to point out that quaternion slerp may be the right or the wrong way to go. It depends on what you want to reach. Your interpolation is done for keyframe animation of skeletons. Therein are 2 possible purposes for interpolation:

(1) "intra-track interpolation" is done to yield in an intermediate value between 2 samples on the same track
(2) "inter-track interpolation" is done to yield in blending of animations

Now, slerp has a constant speed over the arc, since in fact the angle is interpolated. That guarantees a smooth motion. However, when a keyframe is crossed, a sudden change is introduced into the motion. This is a fact as long as any linear interpolation is used (slerp, nlerp) since linear interpolations provide only continuity in location, not in speed (notice please that the constant speed of slerp is only on the arc segment, not when going to the next segment). Due to this you may decide that constant speed over the arc is questionable, and may go with the nlerp instead (which is easier to implement and faster in processing).

The nlerp doesn't show constant speed, but it has the advantage of being commutative (the slerp isn't commutative). For case (1) commutativity plays no role, since you can ever interpolate "naturally" from the sample with the lower timestamp to the one with the greater timestamp, even if you would play the animation backwards. However, for case (2) commutativity plays a role: The overall animation will look different if you blend animation A with B or else animation B with A. In other words, if the blending depends on the order, you impose your artist to think about that, and you need to implement a guarantee to get always the prescibed order. Hence, IMHO nlerp would be the better way for case (2).

So it's not so easy after all ::). I wish it was ;).

So. It looks like it is handy to have both. One for getting the arc of rotation right (PHYSICS). Other to blend several animation (GFX).

I must apologize again for confusing title of this thread. :(

SiegeLord
Member #7,827
October 2006
avatar

Both slerp and nlerp have tradeoffs, and it seems unwise to say that one is the more proper one. I don't think Allegro 4.9 has quaternions yet, though I guess they might be useful in the primitives addon, once (or if) it goes full 3d. In which case, I shall provide both functions.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Go to: