J'ai un objet affiché en utilisant OpenGL ES sur un iPad. Le modèle est défini par des sommets, des normales et des index aux sommets. L'origine du modèle est 0,0,0. Utilisation de UIGestureRecognizer Je peux détecter divers gestes - balayer deux doigts horizontalement pour la rotation autour de y, verticalement pour la rotation autour de x. Geste de rotation à deux doigts pour la rotation autour de y. Panoramique pour déplacer le modèle. Pincez/faites un zoom sur l'échelle. Je veux que le spectateur puisse manipuler le modèle pour voir (par exemple) le revers du modèle ou le tout à la fois.rotation autour de l'axe défini par point touché
La stratégie de base vient de Ray Wenderlich's tutorial mais je l'ai réécrite ce à Swift.
Je comprends escouades d'être un vecteur et un angle. Les vecteurs up
, right
et front
représentent les trois axes:
front = GLKVector3Make(0.0, 0.0, 1.0)
right = GLKVector3Make(1.0, 0.0, 0.0)
up = GLKVector3Make(0.0, 1.0, 0.0)
de sorte que les pommes de quaternions une rotation autour de chacun des trois axes (bien que seulement un des dx
, dy
, dz
a une valeur décidée par le geste de reconnaissance .)
func rotate(rotation : GLKVector3, multiplier : Float) {
let dx = rotation.x - rotationStart.x
let dy = rotation.y - rotationStart.y
let dz = rotation.z - rotationStart.z
rotationStart = GLKVector3Make(rotation.x, rotation.y, rotation.z)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd)
rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd)
state = .Rotation
}
Dessin utilise le modelViewMatrix, calculée par la fonction suivante:
func modelViewMatrix() -> GLKMatrix4 {
var modelViewMatrix = GLKMatrix4Identity
// translation and zoom
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, translationEnd.x, translationEnd.y, -initialDepth);
// rotation
let quaternionMatrix = GLKMatrix4MakeWithQuaternion(rotationEnd)
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, quaternionMatrix)
// scale
modelViewMatrix = GLKMatrix4Scale(modelViewMatrix, scaleEnd, scaleEnd, scaleEnd);
// rotation
return modelViewMatrix
}
Et surtout cela fonctionne. Cependant tout est relatif à l'origine. Si le modèle est pivoté, le pivot est toujours un axe passant par l'origine. Si vous zoomez en regardant la fin du modèle à l'opposé de l'origine puis en le faisant pivoter, le modèle peut rapidement basculer hors de la vue. Si le modèle est mis à l'échelle, puis l'origine est toujours le point fixe le modèle de plus en plus grande ou plus petite - si l'origine est hors de l'écran et l'échelle est réduite, le modèle peut disparaître de la vue car elle s'effondre vers l'origine ...
Ce qui devrait arriver, c'est que quelle que soit la vue actuelle, le modèle tourne ou évolue par rapport à la vue courante. Pour une rotation autour de l'axe y, cela signifierait définir l'axe y autour duquel la rotation se produit en passant verticalement au milieu de la vue actuelle. Pour une opération d'échelle, le point fixe du modèle se trouverait au centre de l'écran, le modèle se rétrécissant vers ce point ou croissant vers celui-ci.
Je sais que la solution 2D est toujours de traduire à l'origine, appliquer la rotation et ensuite appliquer l'inverse de la première traduction. Je ne vois pas pourquoi cela devrait être différent en 3D, mais je ne trouve pas d'exemple avec des matrices de quaternions. J'ai essayé d'appliquer une translation et son inverse autour de la rotation mais rien n'a d'effet.
J'ai donc essayé de le faire dans la fonction rotate:
let xTranslation : Float = 300.0
let yTranslation : Float = 300.0
let translation = GLKMatrix4Translate(GLKMatrix4Identity, xTranslation, yTranslation, -initialDepth);
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(translation) , rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dx * multiplier, up), rotationEnd)
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(dy * multiplier, right), rotationEnd)
rotationEnd = GLKQuaternionMultiply((GLKQuaternionMakeWithAngleAndVector3Axis(-dz, front)), rotationEnd)
// inverse translation
let inverseTranslation = GLKMatrix4Translate(GLKMatrix4Identity, -xTranslation, -yTranslation, -initialDepth);
rotationEnd = GLKQuaternionMultiply(GLKQuaternionMakeWithMatrix4(inverseTranslation) , rotationEnd)
La traduction est 300300, mais il n'y a pas d'effet du tout, il pivote toujours là où je sais que l'origine soit. J'ai cherché longtemps un code d'échantillon et n'en ai trouvé aucun.
Le modelViewMatrix est appliqué dans la mise à jour() avec:
effect?.transform.modelviewMatrix = modelViewMatrix
Je pourrais aussi tricher en réglant toutes les valeurs dans le modèle afin que 0,0,0 tombe à un point central - mais ce serait toujours être une origine fixe et ne serait que légèrement meilleure.
La multiplication de matrices peut être très contre-intuitif - mon La première pensée (depuis que j'ai moi-même tout chamboulé) est que vous multipliez les matrices dans le mauvais ordre. Je ne sais pas exactement quel comportement vous cherchez; comparez à [ce site de visualisation de modèles] (https://sketchfab.com/models/7355bbb7edf14fed9273eedadf513f5c) qui conserve une origine fixe par rapport à la vue. Est-ce ce que vous voulez, ou comment voudriez-vous que le vôtre soit différent? –
C'est un bon exemple d'origine fixe. Traduire le vélo loin du centre, puis il se balance dans un large arc autour du centre. Je voudrais que l'axe de rotation soit un point entre les deux doigts faisant le geste de rotation. Maintenant, cela ne fait que définir un point 2d mais maintenir le même z que l'origine pour la rotation serait correct. –
Pouvez-vous clarifier plus ce que vous voulez? Par exemple, pouvez-vous mettre une liste de questions? – ELKA