Miroirs de rotation Slerping

9

Je tourne mon personnage de jeu pour regarder la cible en utilisant le code suivant:

        transform.rotation = Quaternion.Slerp(startQuaternion, lookQuaternion, turningNormalizer*turningSpeed/10f)

startQuaternion est la rotation actuelle du personnage quand une nouvelle cible est donnée.

lookQuaternion est la direction que le personnage doit regarder et il est défini comme ceci:

    destinationVector = currentWaypoint.transform.position - transform.position;
    lookQuaternion = Quaternion.LookRotation(destinationVector, Vector3.up);

tournantNormalizer est juste Time.deltaTimeincrémenté et turningSpeedest une valeur statique donnée dans l'éditeur.

Le problème est que bien que le personnage tourne comme il se doit la plupart du temps, il a des problèmes lorsqu'il doit faire près de 180 degrés. Ensuite, il tremble parfois et reflète la rotation:

entrez la description de l'image ici

Dans cette image mal dessinée, le personnage (à droite) commence à se tourner vers le cercle à gauche. Au lieu de simplement tourner à gauche ou à droite, il commence cette "danse miroir":

  1. Il commence à tourner vers le nouveau parement
  2. Ensuite, il s'enclenche soudainement au même angle mais de l'autre côté et continue de tourner

Il effectue cette "mise en miroir" aussi longtemps qu'il regarde la cible. Est-ce une chose avec des quaternions, slerping / lerping ou autre chose?

EDIT1: Apparemment, le problème ne provient pas de la rotation elle-même. Le problème le plus probable est que le personnage se déplace vers le visage pendant qu'il tourne. En limitant l'angle, entre face et cible, lorsque le personnage est autorisé à se déplacer, il réduit et parfois élimine la rotation instable / miroir.

Bien sûr, cela soulève plus de questions, pourquoi le personnage ne peut-il pas bouger et tourner en même temps sans problèmes? Code utilisé pour le mouvement:

transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);
Esa
la source
'LookQuaternion' est-il mis à jour à chaque image? Si c'est le cas, ma présomption serait que de petites inexactitudes conduisent à une sorte de gigue où le joueur `` dépasse '' la rotation du regard et donc la nouvelle direction à regarder est juste de l' autre côté de `` directement derrière '' ... .
Steven Stadnicki

Réponses:

5

Chaque orientation dans l'espace 3D peut être représentée par 2 quaternions d'unités distinctes qet -q(négativement par composants q). Par exemple, l'orientation représentée par la matrice d'identité 3x3 Ipeut être représentée par 2 quaternions:

 q:  { 0,  0,  0},  1
-q:  {-0, -0, -0}, -1

Les deux représentent la même orientation dans l'espace 3D, leur produit scalaire est exactement -1, chacun d'eux se trouvant dans l'autre hémisphère, exactement sur les côtés opposés de l'hypersphère.

Le résultat que vous observez lorsque le slerp prend l'arc le plus long pour tourner, c'est lorsque vous slerpez entre 2 quaternions qui ne se trouvent pas dans le même hémisphère. Si cela se produit, annulez simplement l'un d'entre eux avant de le déformer, le slerp prendra alors l'arc le plus court.

Le produit scalaire est un outil simple pour savoir si cela se produit. Si le produit scalaire des deux quaternions est inférieur à 0, alors ils ne se trouvent pas dans le même hémisphère. Donc, si le produit scalaire des deux est inférieur à 0, annulez simplement l'autre quaternion au niveau des composants avant de les ralentir.

Maik Semder
la source
Je me demande si je vous ai bien compris pendant que je l'ai essayé avec cela, if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }mais cela n'a pas corrigé le problème. Désolé pour le mauvais formatage, je n'arrive pas à apprivoiser cette section de commentaire.
Esa
Presque, mais non Inverse, c'est une opération différente de celle Negate. if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }Je n'utilise pas C #, mais d'après l'apparence du docu, il semble que vous pouvez également utiliser directement la fonction Negate.
Maik Semder
if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Maik Semder
Je crains que cela n'ait pas résolu le problème. J'ai mis à jour la question.
Esa
Ouais c'est probablement un mélange de différents bugs. Simplifiez votre configuration de test, testez une chose à la fois, pas la translation et la rotation en même temps. Assurez-vous d'abord que l'un fonctionne, puis l'autre, puis les deux ensemble. Concentrez-vous uniquement sur la rotation en premier, utilisez des valeurs d'orientation bien connues, comme commencer à 170 degrés, descendre à zéro, voir où cela va mal et l'afficher ici
Maik Semder
1

Eh bien, votre problème est que ces deux vecteurs qui forment vos quaternions font 180 degrés, donc la question à poser lors de l'interpolation de l'arc est-il censé prendre? l'arc supérieur ou inférieur ??

C'est ce qui provoque essentiellement la mise en miroir, chaque fois que vous interpolez, c'est l'inverse tout en conservant l'angle.

Selon ce lien .

Lorsque thêta est à 180 degrés, le résultat n'est pas défini car il n'y a pas de direction la plus courte pour tourner, je ne pense pas que cette situation soit encore gérée correctement, il serait préférable de choisir un axe arbitraire normal à qa ou qb, je le ferais apprécier toutes les idées pour la meilleure façon de le faire.

Ce que vous devez faire est d'interpoler votre quaternion de départ avec un quaternion intermédiaire (pour déterminer quel arc prendre) et une fois atteint, utilisez le quaternion intermédiaire pour continuer jusqu'au quaternion cible réel.

concept3d
la source
1

Le problème que je pense que vous voyez n'a rien à voir avec votre code, mais est plutôt un problème fondamental d'interpolation le long de la surface d'une sphère (dans ce cas, pour la viewDirection). Entre (presque) deux points sur une sphère, il y a un seul grand cercle - par exemple, en fixant arbitrairement notre point de départ au pôle nord, le grand cercle serait le méridien sur lequel se trouve le point final. L'interpolation d'un point à l'autre se déplace le long de ce grand cercle.

Maintenant, pour la plupart des points d'une sphère, les points voisins correspondent aux grands cercles voisins; par exemple, les méridiens sur lesquels se trouvent Los Angeles et Phoenix sont assez proches les uns des autres. Mais lorsque le point de destination est l' antipode du point d'origine - du pôle sud au pôle nord du point de départ - il n'y a plus un seul grand cercle à travers les deux, mais à la place tous les grands cercles à travers l'un passent par l'autre. Pire encore, cela signifie que les points proches du pôle sud ne sont pas «la plupart des points»; deux points l'un près de l'autre et les deux près du pôle sud peuvent avoir des méridiens très divergents.

Je soupçonne que ce que vous voyez est une manifestation de cette instabilité dans le méridien - ou en d'autres termes, l'arc d'interpolation. Lorsque la cible de regard est directement derrière le personnage, des éléments tels que l'imprécision de premier ordre dans votre `` intégration Euler '' de la position du personnage et la façon dont la direction du regard change d'une image à l'autre, vont les conduire de manière extravagante. différents arcs d'interpolation d'une image à l'autre, ce qui entraînera probablement le «décalage» que vous voyez.

En ce qui concerne l'utilisation, la meilleure chose qui me vient à l'esprit est de ne pas recalculer la direction de visée «cible» à chaque image; au lieu de cela, utilisez le même pour une étendue de plusieurs images, puis calculez-en une nouvelle et utilisez-la pour plusieurs images, etc. Si besoin est, vous pouvez même interpoler d'une cible à l'autre sur l'intervalle de quelques images afin que la direction du mouvement ne change pas trop brusquement.

Steven Stadnicki
la source