New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AnimatedTriangle shouldn't rotate around a complete circle? #185

Open
scurest opened this Issue Jul 16, 2018 · 4 comments

Comments

Projects
None yet
3 participants
@scurest

scurest commented Jul 16, 2018

I have a problem with the sample GIF shown for the AnimatedTriangle sample.

Here are the input/output lists for the rotation curve in the model (rounded off). The output targets the triangle's rotation quaternion.

Input Output
0.0 (0, 0, 0, 1)
0.25 (0, 0, 0.707, 0.707)
0.5 (0, 0, 1, 0)
0.75 (0, 0, 0.707, -0.707)
1.0 (0, 0, 0, 1)

So the triangle rotates around in the same direction as time passes from t=0.0 to t=0.75, but I expected the triangle to then rotate in the opposite direction, more quickly, and traverse again, in the reverse direction, the arc it had just taken until it returns to its original position, since this is the path which interpolates between (0.0, 0.0, 0.707, -0.707) and (0.0, 0.0, 0.0, 1.0).

Instead, the sample GIF shows it continuing to rotate in the same direction as it has been, and at the same speed, until it reaches its original position again. This is what I would have expected if the final output were (0.0, 0.0, 0.0, -1.0). As it is, this appears impossible, since at time, say, t=0.95, the quaternion's Z must be greater than 0.707 (or less than -0.707) for this to happen, but that can't result from interpolating between 0.707 and 0.0.

But the viewer also shows it completing a complete circle, which is extremely confusing to me. The only explanation I could think of is that the viewer is dynamically choosing between the stored quaternion q, and -q, based on which is closer to the last point; or it converts to matrices before interpolating.

  • What is the viewer doing?
  • Is this expected behavior? Is the sample GIF correct?
  • Does the spec say anything about this?
@lexaknyazev

This comment has been minimized.

Show comment
Hide comment
@lexaknyazev

lexaknyazev Jul 16, 2018

Member

Rotation quaternions should be interpolated using slerp algorithm. It enforces path between endpoints. There's notion about that in the spec.

Member

lexaknyazev commented Jul 16, 2018

Rotation quaternions should be interpolated using slerp algorithm. It enforces path between endpoints. There's notion about that in the spec.

@scurest

This comment has been minimized.

Show comment
Hide comment
@scurest

scurest Jul 16, 2018

Slerping traverses the same set of points as lerping, just at a different speed. You still can't produce a value > 0.707 or < -0.707 in the Z component by interpolating (0, 0, 0.707, -0.707) and (0,0,0,1). Slerping can go either the long or short way round. Wikipedia notes this

However, because the covering is double (q and −q map to the same rotation), the rotation path may turn either the "short way" (less than 180°) or the "long way" (more than 180°). Long paths can be prevented by negating one end if the dot product, cos Ω, is negative, thus ensuring that −90° ≤ Ω ≤ 90°.

The choice of whether to slerp is independent of the choice of whether to always go the short way by negating one of the quaternions.

It also seems strange to me that the direction of rotation would be completely changed by switching to CUBIC interpolation (and giving appropriate tangents).

scurest commented Jul 16, 2018

Slerping traverses the same set of points as lerping, just at a different speed. You still can't produce a value > 0.707 or < -0.707 in the Z component by interpolating (0, 0, 0.707, -0.707) and (0,0,0,1). Slerping can go either the long or short way round. Wikipedia notes this

However, because the covering is double (q and −q map to the same rotation), the rotation path may turn either the "short way" (less than 180°) or the "long way" (more than 180°). Long paths can be prevented by negating one end if the dot product, cos Ω, is negative, thus ensuring that −90° ≤ Ω ≤ 90°.

The choice of whether to slerp is independent of the choice of whether to always go the short way by negating one of the quaternions.

It also seems strange to me that the direction of rotation would be completely changed by switching to CUBIC interpolation (and giving appropriate tangents).

@javagl

This comment has been minimized.

Show comment
Hide comment
@javagl

javagl Jul 16, 2018

Contributor

I'd need some time to wrap my head around the meaning of CUBIC interpolation for quaternions, so I'll have to put this aside for now.

Regarding the sample model: Preventing the "long path", as you described it by quoting from wikipedia, seems to be the common approach. At least, all viewers at https://github.com/cx20/gltf-test#simple-models-for-testing-individual-features (that show a proper animation at all) do the rotation as it is shown in the GIF.

The GIF was created using JglTF, where the quaternion interpolation is implemented like this, which in turn is based on the implementation in the Java "vecmath" library, which is mirrored here: https://github.com/hharrison/vecmath/blob/master/src/javax/vecmath/Quat4f.java#L609

It contains an implementation note

// From "Advanced Animation and Rendering Techniques"
// by Watt and Watt pg. 364, function as implemented appeared to be
// incorrect.  Fails to choose the same quaternion for the double
// covering. Resulting in change of direction for rotations.
// Fixed function to negate the first quaternion in the case that the
    // dot product of q1 and this is negative. Second case was not needed.

which seems to exactly refer to the case that you described.

@lexaknyazev Maybe it could make sense to add a hint/implementation note in the spec? Something that basically says that the slerp interpolation of quaternions always has to take the shorter path (referring to the quote from wikipedia).

Contributor

javagl commented Jul 16, 2018

I'd need some time to wrap my head around the meaning of CUBIC interpolation for quaternions, so I'll have to put this aside for now.

Regarding the sample model: Preventing the "long path", as you described it by quoting from wikipedia, seems to be the common approach. At least, all viewers at https://github.com/cx20/gltf-test#simple-models-for-testing-individual-features (that show a proper animation at all) do the rotation as it is shown in the GIF.

The GIF was created using JglTF, where the quaternion interpolation is implemented like this, which in turn is based on the implementation in the Java "vecmath" library, which is mirrored here: https://github.com/hharrison/vecmath/blob/master/src/javax/vecmath/Quat4f.java#L609

It contains an implementation note

// From "Advanced Animation and Rendering Techniques"
// by Watt and Watt pg. 364, function as implemented appeared to be
// incorrect.  Fails to choose the same quaternion for the double
// covering. Resulting in change of direction for rotations.
// Fixed function to negate the first quaternion in the case that the
    // dot product of q1 and this is negative. Second case was not needed.

which seems to exactly refer to the case that you described.

@lexaknyazev Maybe it could make sense to add a hint/implementation note in the spec? Something that basically says that the slerp interpolation of quaternions always has to take the shorter path (referring to the quote from wikipedia).

@scurest

This comment has been minimized.

Show comment
Hide comment
@scurest

scurest Jul 17, 2018

Cubic interpolation for quaternions is just cubic interpolation of the XYZW components followed by normalization of the quaternion (just like lerping). But the precise interpolation method doesn't matter much because (this is what I was getting at with my argument about Z):

The rotation quaternions shown in the GIF for times t=0.75 to t=1.0 lie in either U1 = {0}×{0}×[0, 0.707]×[-1, -0.707] or in U2 = -U1 = {0}×{0}×[-0.707, 0]×[0.707, 1]. These two sets are disconnected. Let f(t), t in [0.75, 1.0], interpolate between f(0.75) = (0, 0, 0.707, -0.707) and f(1.0) = (0, 0, 0, 1). If f is continuous (as lerp, slerp, and cubic spline are), then its image is connected, but then it is a contradiction that it contains both (0, 0, 0.707, -0.707), which is in U1, and (0, 0, 0, 1), which is in U2.

(If we always choose the short way, f is not always continuous; it may have a jump discontinuity at its right end-point.)


I opened an issue on the main spec repo at KhronosGroup/glTF#1395.

scurest commented Jul 17, 2018

Cubic interpolation for quaternions is just cubic interpolation of the XYZW components followed by normalization of the quaternion (just like lerping). But the precise interpolation method doesn't matter much because (this is what I was getting at with my argument about Z):

The rotation quaternions shown in the GIF for times t=0.75 to t=1.0 lie in either U1 = {0}×{0}×[0, 0.707]×[-1, -0.707] or in U2 = -U1 = {0}×{0}×[-0.707, 0]×[0.707, 1]. These two sets are disconnected. Let f(t), t in [0.75, 1.0], interpolate between f(0.75) = (0, 0, 0.707, -0.707) and f(1.0) = (0, 0, 0, 1). If f is continuous (as lerp, slerp, and cubic spline are), then its image is connected, but then it is a contradiction that it contains both (0, 0, 0.707, -0.707), which is in U1, and (0, 0, 0, 1), which is in U2.

(If we always choose the short way, f is not always continuous; it may have a jump discontinuity at its right end-point.)


I opened an issue on the main spec repo at KhronosGroup/glTF#1395.

@mosra mosra referenced this issue Sep 1, 2018

Merged

Cubic Hermite splines #267

26 of 26 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment