J'ai deux vecteurs u et v. Existe-t-il un moyen de trouver un quaternion représentant la rotation de u à v?
Réponses
Trop de publicités? Quaternion q;
vector a = crossproduct(v1, v2)
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2)
N'oubliez pas de normaliser q
Richard a raison de dire qu'il n'y a pas de rotation unique, mais ce qui précède devrait donner "l'arc le plus court". C'est probablement ce dont vous avez besoin.
À Mi-Chemin Vecteur Solution
Je suis venu avec la solution que je crois Imbrondir essayait à présent (mais avec une petite erreur, ce qui est probablement pourquoi sinisterchipmunk eu de la difficulté à vérifier).
Étant donné que nous pouvons construire un quaternion représentant une rotation autour d'un axe comme suit:
q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == sin(angle / 2) * axis.z
Et que la dot et le produit vectoriel de deux vecteurs normés sont:
dot == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z
En le voyant comme une rotation de u à v peut être obtenu par rotation par theta (l'angle entre les vecteurs) autour de la perpendiculaire vecteur, il semble que si nous pouvons directement la construction d'un quaternion représentant une rotation à partir des résultats de la dot et de la croix-produits; cependant, comme il est, theta = angle / 2, ce qui signifie que s'il en résulte deux fois la rotation désirée.
Une solution consiste à calculer un vecteur à mi-chemin entre u et v, et l'utilisation de la dot et du produit vectoriel de u et la moitié de vecteur pour construire un quaternion représentant une rotation de deux fois l'angle entre u et la moitié de vecteur, qui nous emmène tout le chemin à v!
Il existe un cas particulier où u == -v et unique à mi-chemin vecteur devient impossible à calculer. C'est prévu, compte tenu de l'infinité de "le plus court de l'arc" rotations qui peut nous prendre de u à v, et nous devons simplement faire pivoter de 180 degrés autour de n'importe quel vecteur orthogonal à u (ou v) comme notre cas. Ceci est fait en prenant la croisée normalisée produit de u avec un tout autre vecteur de ne pas parallèle à l' u.
Le Pseudo-code qui suit (évidemment, dans la réalité, les cas spécial aurait pour prendre en compte à virgule flottante inexactitudes, probablement par la vérification au point des produits à l'encontre d'un certain seuil plutôt qu'une valeur absolue).
Notez également qu'il n'y a pas de cas particulier où u == v (l'identité quaternion est produit -- vérifier et voir par vous-même).
// N.B. the arguments are _not_ axis and angle, but rather the
// raw scalar-vector components.
Quaternion(float w, Vector3 xyz);
Quaternion(float w, float x, float y, float z);
Quaternion(Vector3 axis, float angle_degrees);
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
// It is important that the inputs are of equal length when
// calculating the half-way vector.
u = normalized(u);
v = normalized(v);
// Unfortunately, we have to check for when u == -v, as u + v
// in this case will be (0, 0, 0), which cannot be normalized.
if (u == -v)
{
// 180 degree rotation around any orthogonal vector
Vector3 other = (abs(dot(u, X_AXIS)) < 1.0) ? X_AXIS : Y_AXIS;
return Quaternion(normalized(cross(u, other)), 180);
}
Vector3 half = normalized(u + v);
return Quaternion(dot(u, half), cross(u, half));
}
À Mi-Chemin Quaternion Solution
C'est en fait la solution présentée dans la accepté de répondre, et il semble être légèrement plus rapide que la moitié de vecteur solution (~20% plus rapide par mes mesures, mais ne prenez pas mon mot pour lui). Je suis de l'ajouter ici au cas où d'autres, comme moi, sont intéressés par une explication.
Essentiellement, au lieu de calculer un quaternion à l'aide d'un mi-chemin de vecteur, vous pouvez calculer le quaternion qui entraîne deux fois la rotation nécessaire (comme détaillé dans l'autre solution), et de trouver le quaternion à mi-chemin entre celle-ci et le degré zéro.
Comme je l'ai expliqué avant, le quaternion pour le double de la rotation nécessaire est:
q.w == dot(u, v)
q.xyz == cross(u, v)
Et le quaternion pour zéro rotation est:
q.w == 1
q.xyz == (0, 0, 0)
Le calcul de la mi-chemin de quaternions est simplement une question d'additionner les quaternions et de la normalisation de la raison, tout comme avec les vecteurs. Cependant, comme c'est également le cas avec les vecteurs, les quaternions doivent avoir la même ampleur, sinon le résultat sera dirigé vers le quaternion avec la plus grande ampleur.
Un quaternion est construit à partir de la dot et du produit vectoriel de deux vecteurs ont la même grandeur que ceux des produits: length(u) * length(v)
. Plutôt que de diviser tous les quatre composantes de ce facteur, nous pouvons plutôt à l'échelle de l'identité de quaternions. Et si vous vous demandez pourquoi la accepté de répondre apparemment complique les choses en utilisant sqrt(length(u) ^ 2 * length(v) ^ 2)
, c'est parce que le carré de la longueur d'un vecteur est plus rapide à calculer que la longueur, donc nous pouvons sauver un sqrt
calcul. Le résultat est:
q.w = dot(u, v) + sqrt(length_2(u) * length_2(v))
q.xyz = cross(u, v)
Et puis normaliser le résultat. Le Pseudo-code suivant:
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
float k_cos_theta = dot(u, v);
float k = sqrt(length_2(u) * length_2(v));
if (k_cos_theta / k == -1)
{
// 180 degree rotation around any orthogonal vector
Vector3 other = (abs(dot(u, X_AXIS)) < 1.0) ? X_AXIS : Y_AXIS;
return Quaternion(normalized(cross(u, other)), 180);
}
return normalized(Quaternion(k_cos_theta + k, cross(u, v)));
}
Le problème énoncé n’est pas bien défini: il n’existe pas de rotation unique pour une paire de vecteurs donnée. Prenons le cas, par exemple, où u = <1, 0, 0> et v = <0, 1, 0> . Une rotation de u à v serait une rotation pi / 2 autour de l'axe z. Une autre rotation de u à v serait une rotation de pi autour du vecteur <1, 1, 0> .
Je ne suis pas très bon sur les Quaternions. Cependant j'ai lutté pendant des heures sur ce sujet, et ne pouvait pas faire Polaris878 solution de travail. J'ai essayé de pré-normalisation v1 et v2. Normaliser q. Normaliser q.xyz. Pourtant je ne l'obtenez pas. Le résultat reste ne me donne pas le bon résultat.
À la fin, bien que j'ai trouvé une solution qui a fait. Si ça aide quelqu'un d'autre, voici mon travail (python) code:
def diffVectors(v1, v2):
""" Get rotation Quaternion between 2 vectors """
v1.normalize(), v2.normalize()
v = v1+v2
v.normalize()
angle = v.dot(v2)
axis = v.cross(v2)
return Quaternion( angle, *axis )
Un cas particulier doit être fait si v1 et v2 sont paralèlles comme v1 == v2 ou v1 == -v2 (avec une certaine tolérance), où je crois que les solutions devraient être Quaternion(1, 0,0,0) (pas de rotation) ou Quaternion(0, *v1) (rotation de 180 degrés)
Du point de vue de l'algorithme, la solution la plus rapide est celle du pseudocode.
Quaternion shortest_arc(const vector3& v1, const vector3& v2 )
{
// input vectors NOT unit
Quaternion q( cross(v1, v2), dot(v1, v2) );
// reducing to half angle
q.w += q.magnitude(); // 4 multiplication instead of 6 and more numerical stable
// handling close to 180 degree case
//... code skipped
return q.normalized(); // normalize if you need UNIT quaternion
}
Assurez-vous que vous avez besoin des quaternions d’unités (d’habitude, cela est nécessaire pour l’interpolation).
REMARQUE: Les quaternions non-unités peuvent être utilisées avec certaines opérations plus rapidement que l'unité.