2016-09-15 2 views
5

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.

+0

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? –

+0

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. –

+0

Pouvez-vous clarifier plus ce que vous voulez? Par exemple, pouvez-vous mettre une liste de questions? – ELKA

Répondre

2

Le problème est dans la dernière opération que vous avez fait, vous devez échanger avec le inverseTranslationrotationEnd:

rotationEnd = GLKQuaternionMultiply(rotationEnd, GLKQuaternionMakeWithMatrix4(inverseTranslation)) 

Et je pense que la rotation partielle (dx, dy, dz) devrait suivre la même règle.

En fait, si vous voulez changer le pivot, voici comment votre devrait être fait la multiplication de la matrice:

modelMatrix = translationMatrix * rotationMatrix * inverse(translationMatrix) 

et le résultat en coordonnées homogènes sera calculé comme suit:

newPoint = translationMatrix * rotationMatrix * inverse(translationMatrix) * v4(x,y,z,1) 

Exemple

Ceci est un exemple de test 2D que vous pouvez exécuter dans une aire de jeux.

enter image description here

let v4 = GLKVector4Make(1, 0, 0, 1) // Point A 

let T = GLKMatrix4Translate(GLKMatrix4Identity, 1, 2, 0); 
let rot = GLKMatrix4MakeWithQuaternion(GLKQuaternionMakeWithAngleAndVector3Axis(Float(M_PI)*0.5, GLKVector3Make(0, 0, 1))) //rotate by PI/2 around the z axis. 
let invT = GLKMatrix4Translate(GLKMatrix4Identity, -1, -2, 0); 

let partModelMat = GLKMatrix4Multiply(T, rot) 
let modelMat = GLKMatrix4Multiply(partModelMat, invT) //The parameters were swapped in your code 
//and the result would the rot matrix, since T*invT will be identity 

var v4r = GLKMatrix4MultiplyVector4(modelMat, v4) //ModelMatrix multiplication with pointA 
print(v4r.v) //(3,2,0,1) 

//Step by step multiplication using the relation described above 
v4r = GLKMatrix4MultiplyVector4(invT, v4) 
v4r = GLKMatrix4MultiplyVector4(rot, v4r) 
v4r = GLKMatrix4MultiplyVector4(T, v4r) 
print(v4r.v) //(3,2,0,1) 

Quant à l'échelle, si je comprends bien ce que vous voulez, je vous recommande de le faire comme il est fait ici: https://gamedev.stackexchange.com/questions/61473/combining-rotation-scaling-around-a-pivot-with-translation-into-a-matrix

+0

Merci pour votre explication détaillée, je comprends beaucoup plus sur ce qui se passe maintenant. –