2016-09-15 5 views
5

У меня есть объект, отображаемый с использованием OpenGL ES на iPad. Модель определяется вершинами, нормалями и индексами для вершин. Происхождение модели - 0,0,0. Используя UIGestureRecognizer, я могу обнаруживать различные жесты - двуглавый проведите по горизонтали для вращения вокруг y, вертикально для вращения вокруг x. Двусторонний поворотный жест для вращения вокруг y. Панорамирование для перемещения модели вокруг. Жест для увеличения/масштабирования. Я хочу, чтобы зритель мог манипулировать моделью, чтобы увидеть (например) обратную сторону модели или всего этого сразу.Применить поворот вокруг оси, заданной касательной точкой

Основная стратегия исходит от Ray Wenderlich's tutorial, но я переписал это в Swift.

Я понимаю, что кватернионы являются вектором и углом. Векторы up, right и front представляют три оси:

front = GLKVector3Make(0.0, 0.0, 1.0) 
right = GLKVector3Make(1.0, 0.0, 0.0) 
up = GLKVector3Make(0.0, 1.0, 0.0) 

Таким образом, кватернионные яблоки вращение вокруг каждой из трех осей (хотя только один из dx, dy, dz имеет значение, решается на жест распознаватель .)

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 

} 

Чертеж использует modelViewMatrix, рассчитанное по следующей функции:

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 
} 

И в основном это работает. Однако все относительно происхождения. Если модель повернута, то ось вращения всегда является осью, проходящей через начало координат - если увеличить масштаб, глядя на конец модели в сторону от начала координат и затем вращаясь, модель может быстро отскочить из вида. Если модель масштабируется, то начало координат всегда является фиксированной точкой, при этом модель становится все больше или меньше - если исходное значение находится за пределами экрана, а масштаб уменьшается, модель может исчезнуть из вида, когда она сворачивается к началу координат ...

Что должно произойти, так это то, что независимо от текущего вида модель вращается или масштабируется относительно текущего вида. Для вращения вокруг оси y, которая будет означать определение оси y, вокруг которой происходит вращение, проходящего вертикально через середину текущего вида. Для операции масштабирования неподвижная точка модели будет находиться в центре экрана, при этом модель сжимается к ней или растет с этой точки.

Я знаю, что в 2D решение всегда должно переводить в начало координат, применять поворот, а затем применять инверсию первого перевода. Я не понимаю, почему это должно быть разным в 3D, но я не могу найти какой-либо пример, делающий это с кватернионами только с матрицами. Я попытался применить перевод и его инверсию вокруг вращения, но ничего не повлияло.

Так что я попытался сделать это в функции циклического сдвига:

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) 

Перевод 300300, но нет никакого эффекта, она по-прежнему вращает вокруг, где я знаю, происхождение быть. Я долго искал образец кода и не нашел.

modelViewMatrix применяется в обновлении() с:

effect?.transform.modelviewMatrix = modelViewMatrix 

Я мог также обмануть путем корректировки всех значений в модели, так что 0,0,0 падает в центральной точке - но это будет все еще будет фиксированным происхождением и будет лишь незначительно лучше.

+0

Умножение матриц может быть очень нелогичным - мой Первоначальная мысль (поскольку я много раз перепутала это сам) заключается в том, что вы умножаете матрицы в неправильном порядке. Я не уверен, какое поведение вы ищете; сравните [этот сайт-просмотр модели] (https://sketchfab.com/models/7355bbb7edf14fed9273eedadf513f5c), который сохраняет фиксированное происхождение относительно представления. Это то, что вы собираетесь делать, или как вы хотите, чтобы ваши разные? –

+0

Это хороший пример фиксированного происхождения. Передайте велосипед от центра, а затем он качается по широкой дуге вокруг центра. Я хотел бы, чтобы ось вращения была точкой между двумя пальцами, делающими поворотный жест. Теперь, когда определяется только точка 2d, но поддерживается тот же z, что и начало вращения, будет нормально. –

+0

Можете ли вы уточнить, что хотите? Например, можете ли вы задать список вопросов? – ELKA

ответ

2

Проблема заключается в последней операции, которую вы сделали, вы должны поменять inverseTranslation с rotationEnd:

rotationEnd = GLKQuaternionMultiply(rotationEnd, GLKQuaternionMakeWithMatrix4(inverseTranslation)) 

И я думаю, что частичное вращение (Dx, Dy, Dz) должен следовать тому же правилу.

В самом деле, если вы хотите изменить точку опоры, это то, как ваша матрица умножения должно быть сделано:

modelMatrix = translationMatrix * rotationMatrix * inverse(translationMatrix) 

и результат в однородных координатах будет рассчитываться следующим образом:

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

Пример

Это пример 2D-теста, который можно запустить на игровой площадке.

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) 

Что касается масштаба, если я правильно понимаю, что вы хотите, я бы рекомендовал сделать это, как это сделано здесь: https://gamedev.stackexchange.com/questions/61473/combining-rotation-scaling-around-a-pivot-with-translation-into-a-matrix

+0

Спасибо за подробное объяснение, я больше понимаю, что происходит сейчас. –

Смежные вопросы