J'essaie de laisser l'utilisateur de mon application faire pivoter un objet 3D dessiné au centre de l'écran en faisant glisser son doigt sur l'écran. Un mouvement horizontal à l'écran signifie une rotation autour d'un axe Y fixe, et un mouvement vertical signifie une rotation autour de l'axe X. Le problème que j'ai est que si je permets juste une rotation autour d'un axe, l'objet tourne bien, mais dès que j'introduis une deuxième rotation, l'objet ne tourne pas comme prévu.
Voici une photo de ce qui se passe:
L'axe bleu représente mon axe fixe. Imaginez l'écran ayant cet axe bleu fixe. C'est à cela que je veux que l'objet tourne par rapport. Ce qui se passe est en rouge.
Voici ce que je sais:
- La première rotation autour de Y (0, 1, 0) provoque le déplacement du modèle de l'espace bleu (appelez cet espace A) vers un autre espace (appelez cet espace B)
- Essayer de pivoter à nouveau en utilisant le vecteur (1, 0, 0) tourne autour de l'axe x dans l'espace B PAS dans l'espace A, ce qui n'est pas ce que je veux faire.
Voici ce que j'ai essayé, compte tenu de ce que je pense (je pense) (en omettant la coordonnée W par souci de concision):
- Faites d'abord tourner autour de Y (0, 1, 0) à l'aide d'un Quaternion.
- Convertissez la rotation Y Quaternion en une matrice.
- Multipliez la matrice de rotation Y par mon axe fixe x vecteur (1, 0, 0) pour obtenir l'axe X par rapport au nouvel espace.
- Tournez autour de ce nouveau vecteur X à l'aide d'un Quaternion.
Voici le code:
private float[] rotationMatrix() {
final float[] xAxis = {1f, 0f, 0f, 1f};
final float[] yAxis = {0f, 1f, 0f, 1f};
float[] rotationY = Quaternion.fromAxisAngle(yAxis, -angleX).toMatrix();
// multiply x axis by rotationY to put it in object space
float[] xAxisObjectSpace = new float[4];
multiplyMV(xAxisObjectSpace, 0, rotationY, 0, xAxis, 0);
float[] rotationX = Quaternion.fromAxisAngle(xAxisObjectSpace, -angleY).toMatrix();
float[] rotationMatrix = new float[16];
multiplyMM(rotationMatrix, 0, rotationY, 0, rotationX, 0);
return rotationMatrix;
}
Cela ne fonctionne pas comme je l'espère. La rotation semble fonctionner, mais à un moment donné, le mouvement horizontal ne tourne pas autour de l'axe Y, il semble tourner autour de l'axe Z.
Je ne sais pas si ma compréhension est fausse ou si quelque chose d'autre cause un problème. J'ai d'autres transformations que je fais à l'objet en plus de la rotation. Je déplace l'objet au centre avant d'appliquer la rotation. Je le fais pivoter en utilisant la matrice renvoyée par ma fonction ci-dessus, puis je le translate -2 dans la direction Z pour que je puisse voir l'objet. Je ne pense pas que cela perturbe mes rotations, mais voici le code pour cela de toute façon:
private float[] getMvpMatrix() {
// translates the object to where we can see it
final float[] translationMatrix = new float[16];
setIdentityM(translationMatrix, 0);
translateM(translationMatrix, 0, translationMatrix, 0, 0f, 0f, -2);
float[] rotationMatrix = rotationMatrix();
// centers the object
final float[] centeringMatrix = new float[16];
setIdentityM(centeringMatrix, 0);
float moveX = (extents.max[0] + extents.min[0]) / 2f;
float moveY = (extents.max[1] + extents.min[1]) / 2f;
float moveZ = (extents.max[2] + extents.min[2]) / 2f;
translateM(centeringMatrix, 0, //
-moveX, //
-moveY, //
-moveZ //
);
// apply the translations/rotations
final float[] modelMatrix = new float[16];
multiplyMM(modelMatrix, 0, translationMatrix, 0, rotationMatrix, 0);
multiplyMM(modelMatrix, 0, modelMatrix, 0, centeringMatrix, 0);
final float[] mvpMatrix = new float[16];
multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
return mvpMatrix;
}
Je suis coincé là-dessus depuis quelques jours. L'aide est très appréciée.
================================================== ================
MISE À JOUR:
Faire fonctionner cela dans Unity est simple. Voici un code qui fait pivoter un cube centré à l'origine:
public class CubeController : MonoBehaviour {
Vector3 xAxis = new Vector3 (1f, 0f, 0f);
Vector3 yAxis = new Vector3 (0f, 1f, 0f);
// Update is called once per frame
void FixedUpdate () {
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
transform.Rotate (xAxis, vertical, Space.World);
transform.Rotate (yAxis, -horizontal, Space.World);
}
}
La partie qui fait que les rotations se comportent comme je l'attends est le Space.World
paramètre de la Rotate
fonction sur la transformation.
Si je pouvais utiliser Unity je le ferais, malheureusement, je dois coder ce comportement moi-même.
Réponses:
Le problème que vous rencontrez s'appelle gimble lock . Je pense que ce que vous cherchez à faire est appelé rotation d'arcball . Les mathématiques de l'arcball peuvent être assez compliquées.
Une façon plus simple de le faire consiste à trouver un vecteur 2D perpendiculaire au balayage 2D à l'écran.
Prenez le vecteur et projetez-le sur la caméra près du plan pour obtenir un vecteur 3D dans l'espace mondial. Écran écran vers l'espace mondial .
Créez ensuite un quaternion avec ce vecteur et multipliez-le en gameobject. Probablement avec une transition slurp ou lerp.
Éditer:
Exemple d'unité: Dans l'exemple d'unité, l'état interne de la rotation des objets de jeu est un quaternion et non une matrice. La méthode transform.rotation génère un quaternion basé sur le vecteur et l'angle fournis et multiplie ce quaternion par le quaternion de rotation des objets de jeu. Il génère uniquement la matrice de rotation pour le rendu ou la physique à un stade ultérieur. Les quaternions sont additifs et évitent le verrouillage de vrille.
Votre code devrait ressembler à ceci:
ArcBall Rotation Opengl Tutorial
la source
J'ai pu obtenir les rotations attendues en faisant tourner une matrice de rotation accumulée.
la source
Votre image correspond à votre code rotationMatrix, en faisant pivoter l'axe x avec votre rotation y précédente, vous obtenez l'axe x local, lorsque vous faites ensuite pivoter l'objet autour de vous pour obtenir le résultat que vous affichez dans votre image. Pour que la rotation soit logique du point de vue des utilisateurs, vous souhaitez plutôt faire pivoter l'objet en utilisant l'axe des coordonnées universelles.
Si vous voulez que votre utilisateur puisse faire tourner votre objet plusieurs fois, il serait logique de stocker votre rotation dans un quaternion au lieu d'une matrice, avec le temps, plusieurs rotations (et inexactitudes en virgule flottante) donneront à la matrice un aspect de moins en moins semblable à une matrice de rotation, la même chose se produit dans un quaternion bien sûr, mais juste normaliser le quaternion le ramène à une bonne rotation.
Utilisez simplement le quaternion d'identité comme valeur initiale, et chaque fois que l'utilisateur glisse sur l'écran, vous faites pivoter votre quaternion avec le
Quaternion.fromAxisAngle(yAxis, -angleX)
code. Toujours en utilisant (1,0,0,1) pour x rotations et (0,1,0,1) pour y rotations.Puisque vous n'avez pas mentionné le langage ou un cadre spécifique, les méthodes sur Quaternion peuvent être appelées différemment bien sûr, et normaliser n'est pas nécessaire d'appeler souvent, mais comme la rotation provient d'un utilisateur qui balaie l'écran, elles ne le seront pas ralentir beaucoup les choses et de cette façon il n'y a aucune chance que le Quaternion glisse loin d'un quaternion unitaire.
la source
myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()
ils ne sont pas commutatifs, donc l'ordre dans lequel vous les effectuez compte. Ajoutez un autre commentaire avec votre framework / langage si cela n'a pas fonctionné et je creuserai un peu plus.