Hi,
I get unstable/undesired/flaky results from Quaternion.setFromUnitVectors
. It happens when the unit vectors provided are in opposite direction - i.e. when their dot-product approaches -1.
Has anyone experienced / worked around this?
Hi,
I get unstable/undesired/flaky results from Quaternion.setFromUnitVectors
. It happens when the unit vectors provided are in opposite direction - i.e. when their dot-product approaches -1.
Has anyone experienced / worked around this?
I kind of worked around it by altering the default implementation so that I am very much less accepting anything close to opposite directed unit vecs - i.e. 0.1
instead of Number.EPSILON
. Then my implementation looks good again. See code below.
I will investigate it a bit further though.
export function qFromUnitVectors(vFrom: Vector3, vTo: Vector3Like): Quaternion {
// assumes direction vectors vFrom and vTo are normalized
let x, y, z, w = 0;
let r = vFrom.dot(vTo) + 1;
if (r < 0.1) {
// vFrom and vTo point in opposite directions
r = 0;
if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
x = -vFrom.y;
// noinspection JSSuspiciousNameCombination
y = vFrom.x;
z = 0;
w = r;
} else {
x = 0;
y = -vFrom.z;
z = vFrom.y;
w = r;
}
} else {
x = vFrom.y * vTo.z - vFrom.z * vTo.y;
y = vFrom.z * vTo.x - vFrom.x * vTo.z;
z = vFrom.x * vTo.y - vFrom.y * vTo.x;
w = r;
}
return new Quaternion(x, y, z, w).normalize();
}
Imagine a sphere, two points on that sphere define a path on the curve from one point to another. This path is your quaternion. Now, we always pick the shortest path. Good? good.
What is the shortest path between two points on the opposite sides of the sphere? The asnwer is - the entire sphere, or, in other words - there are infinite paths.
Hope that helps
Thanks for your response Usnul!
I realise that my question above is a bit too brief / cryptic.
I try to do better:
My animated figure is animated on pre-generated paths in a landscape. A set of connected Curve:s serves as a track for that animation. There is a start direction (START_DIR
) from where all these curves are layed out in that landscape.
By using the Quaternion.setFromUnitVectors
where I use the START_DIR
and the tangent of the curve as the two unit vectors I get an almost perfect animation. The figures turns in curves, fold forwards downhill, and backward uphill. It all looks good and is code wise a very simple solution.
However:
When the START_DIR
and the tangent are almost opposite the figure start to get roll-effects. I.e. the figure is twisted left or right as if there also is an axis to spin around through the belly button of that figure. Looks weird.
And thinking about it … I don’t think that we can say that there is something locking the roll when also saying that a calculated Quaternion is the shortest 3D-rotation-operation between two unit vectors. Right?
I have solved it now … with a simple trick to avoid the opposite direction of unit vectors. All animations look good again.
What I did was this:
let tangentAt = curve.getTangentAt(t);
// We get roll-instabilities when tangent and START_DIR essentially are pointing in opposite direction ...
if (START_DIR.dot(tangentAt) > -0.9) {
this.obj.quaternion.setFromUnitVectors(START_DIR, tangentAt);
} else {
// ... so when we detect that we use the negated tangent instead and then flip around the Y-axis as a post-step.
this.obj.quaternion.setFromUnitVectors(START_DIR, tangentAt.negate()).multiply(FLIP_AROUND_Y_AXIS);
}
Where FLIP_AROUND_Y_AXIS
is a precalculated Quaternion as:
const FLIP_AROUND_Y_AXIS = new Quaternion().setFromAxisAngle(UP, Math.PI);
There might be smarter ways to do this too … but I am happy at this point.
Game development proceeds.