Применить вращение вокруг оси, заданной касательной точкой

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

Основная стратегия взята из учебника Рэя Вендерлиха, но я переписал это в 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, но я не могу найти какой-либо пример, делающий это с кватернионами только с матрицами. Я попытался применить перевод и его инверсию вокруг вращения, но ничего не повлияло.

Поэтому я попытался сделать это в функции 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) 

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

ModelViewMatrix применяется в update () с помощью:

 effect?.transform.modelviewMatrix = modelViewMatrix 

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

One Solution collect form web for “Применить вращение вокруг оси, заданной касательной точкой”

Проблема в последней сделанной вами операции, вы должны поменять 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-теста, который можно запустить на игровой площадке.

введите описание изображения здесь

 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- с-перевод-в-а-матрица

  • Swift: создание кнопки, вызывающей метод в другом классе1
  • Как разрешить «двоичный оператор» == 'нельзя применить к двум операндам Foo?
  • Swift 4: установка разных дат и времени
  • Поток данных из сети в AVAudioEngine, возможно ли это?
  • Как я могу отобразить всплывающее сообщение в Swift, которое исчезает через 3 секунды или может быть отменено пользователем немедленно?
  • CABasicAnimation внутри UITableViewCell останавливается при перезагрузке UITableView
  • SpriteKit: как вызвать функцию в SKAction.sequence?
  • Почему интерфейс-конструктор не может использовать конкретный общий подкласс UIView?
  • NSWidgetExtensionContext openURL Swift
  • ViewWillDisappear nil значения для контроллера навигации / вкладки
  • Захват исключения NSKeyedUnarchiver
  • Interesting Posts

    CLLocationManager startUpdatingLocation не работает

    Возможно ли добавить файл .pch в проект?

    Неполная реализация

    Как реализовать масштаб-9 (9-срез) для пользовательского фонового рисунка, созданного для UIButton

    Swift performSegueWithIdentifier не работает

    SceneKit получает координату текстуры после касания Swift

    Просмотр карты Google, как я могу получить исходную точку или рамку для аннотации при масштабировании карты?

    Как нарисовать линию, как мел?

    Как лучше оптимизировать работу в сети на iOS?

    При изменении заголовка UIBarButtonItem переход вяло / мерцает

    Как сохранить аудиофайл после записи локально или firebase в объективе c?

    Как воспроизводить видео с URL-адреса потока с помощью некоторого механизма предварительной буферизации, как это делал facebook?

    Лучший подход к программному изменению направления Xib

    Как нарисовать треугольник с закругленными углами, используя quartz2d / coregraphics

    xcode4: надежно обнаружить каталог DerivedData проекта / рабочей области

    PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.