SceneKit - отображение физического вращения на 360

17

У меня проблемы с отображением движения устройства (слияние датчика) с вращением узла SceneKit. Суть проблемы заключается в следующем,

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

Подробности реализации следующие:

У меня есть узел со сферой в виде geomertry и дочерний для корневого узла. Я использую датчик слияния, чтобы получить квантионы ориентации, а затем преобразуя их в эйлеровы углы. Код для этого:

- (SCNVector3)eulerAnglesFromCMQuaternion:(CMQuaternion) {
    GLKQuaternion gq1 =  GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(90), 1, 0, 0);
    GLKQuaternion gq2 =  GLKQuaternionMake(q.x, q.y, q.z, q.w);
    GLKQuaternion qp  =  GLKQuaternionMultiply(gq1, gq2);
    CMQuaternion rq =   {.x = qp.x, .y = qp.y, .z = qp.z, .w = qp.w};
    CGFloat roll = atan2(-rq.x*rq.z - rq.w*rq.y, .5 - rq.y*rq.y - rq.z*rq.z);
    CGFloat pitch = asin(-2*(rq.y*rq.z + rq.w*rq.x));
    CGFloat yaw = atan2(rq.x*rq.y - rq.w*rq.z, .5 - rq.x*rq.x - rq.z*rq.z);
    return SCNVector3Make(roll, pitch, yaw);
}

Уравнение конверсии взято из 3D-учебник по математике для разработки графики и игр . Я привык бронировать как справочник, не читал его. Но определенно в моем списке чтения.

Теперь, чтобы имитировать физическое вращение в SceneKit, я вращаю свой узел, содержащий SCNCamera. Это дочерний узел rootNode. И использую .rotation свойство делать то же самое. Здесь следует отметить, что Теперь поток обновления моего устройства выглядит примерно так

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:mq withHandler:^(CMDeviceMotion *motion, NSError *error) {
    CMAttitude *currentAttitude = motion.attitude;
    [SCNTransaction begin];
    [SCNTransaction setDisableActions:YES];
    SCNVector3 v = [self eulerAnglesFromCMQuaternion:currentAttitude.quaternion];
    self.cameraNode.eulerAngles = SCNVector3Make( v.y, -v.x, 0); //SCNVector3Make(roll, yaw, pitch)
    [SCNTransaction commit];
}];

Здесь следует обратить особое внимание на то, что углы Эйлера различны для устройства и для узла SceneKit. Ссылки объясняют их.

SceneKit Углы Эйлера

CMAttitude Euler Angles

И в моем понимании, отображение между двумя выглядит примерно так.

Теперь проблема, с которой я сталкиваюсь, заключается в том, что поворот камеры на 360 градусов закончился, прежде чем мой физический поворот вокруг точки закончился. Который пытается определить с помощью следующего изображения.

Я сомневаюсь в кватернионе - > Преобразование угла Эйлера вызывает ошибку, и кватернионная математика сейчас стоит у меня над головой.

Я надеюсь, что предоставил всю информацию, если что-то еще потребуется, я только рад добавить.

    
задан Jesly Varghese 13.08.2014 в 11:21
источник
  • Удивительные рисунки –  godfrzero 13.08.2014 в 13:00
  • Этот ответ решает для всех ориентаций устройств: stackoverflow.com/questions/31823012/... –  Alfie Hanssen 10.07.2016 в 14:40

2 ответа

10

Спасибо за описание вашей проблемы и начало решения! Я совершенно уверен, что это невозможно сделать с углами Эйлера из-за блокировки карданного подвеса (потеря 1 градуса свобода).

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

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:myQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
    CMAttitude *currentAttitude = motion.attitude;
    [SCNTransaction begin];
    [SCNTransaction setDisableActions:YES];

    SCNQuaternion quaternion = [self orientationFromCMQuaternion:currentAttitude.quaternion];
    _cameraNode.orientation = quaternion;

    [SCNTransaction commit];
}];
- (SCNQuaternion)orientationFromCMQuaternion:(CMQuaternion)q
{
    GLKQuaternion gq1 =  GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-90), 1, 0, 0); // add a rotation of the pitch 90 degrees
    GLKQuaternion gq2 =  GLKQuaternionMake(q.x, q.y, q.z, q.w); // the current orientation
    GLKQuaternion qp  =  GLKQuaternionMultiply(gq1, gq2); // get the "new" orientation
    CMQuaternion rq =   {.x = qp.x, .y = qp.y, .z = qp.z, .w = qp.w}; 

    return SCNVector4Make(rq.x, rq.y, rq.z, rq.w);
}
    
ответ дан Ruud Visser 23.01.2015 в 09:58
  • Было бы здорово увидеть, как настроить код на ландшафтную ориентацию. –  walker 24.09.2015 в 18:53
  • Этот ответ решает для всех ориентаций устройств: stackoverflow.com/questions/31823012/... –  Alfie Hanssen 10.07.2016 в 14:40
3

Если CoreMotion и SceneKit не используют одно и то же соглашение для углов Эйлера, то вы, вероятно, можете использовать промежуточные узлы для упрощения преобразования. Например, имея такую иерархию:

YawNode
  |
  |___PitchNode
      |
      |___RollNode
          |
          |___CameraNode

YawNode, PitchNode, RollNode будут вращаться только вокруг одной оси. Затем, играя в иерархическом порядке, вы можете воспроизвести соглашение CoreMotion.

При этом я не уверен, что кватернион преобразования - > Эйлер - отличная идея. Я подозреваю, что вы не получите непрерывный / плавный переход под углами, как 0 / PI / 2PI ... Я бы вместо этого попробовал quaternion-> gt; matrix4 и обновил бы «преобразование» вашего узла.

    
ответ дан Toyos 13.08.2014 в 14:53
  • Если я сделаю кватернион к матрице вращения (это будет матрица 3х3 или, как говорится в книге), как я могу сделать это 4x4 (дополнение?). Или как я могу преобразовать кватернион -> matrix4? –  Jesly Varghese 13.08.2014 в 15:58
  • Как насчет передачи кватерниона CM в SceneKit как кватернион? Вы можете сделать это с помощью свойства SCNNode.orientation. (И если координатные пространства не совпадают, используйте закрывающий узел или поворот, чтобы преобразовать пространство, содержащее узел.) –  rickster 13.08.2014 в 20:23
  • @rickster: попробовал это, но все же существует разница между началом и концом. У меня была проверка на значение датчика, похоже, что дрейф составляет около 20 градусов. Датчики отлично работают, так как другие приложения прекрасно работают. Что может быть неправильным? –  Jesly Varghese 14.08.2014 в 12:45
  • Чтобы добавить к этому, удивительно, что разница происходит только тогда, когда я перемещаюсь / поворачиваюсь вправо. И вращение идеально, когда я двигаюсь влево. –  Jesly Varghese 14.08.2014 в 16:30
  • @Toyos было бы здорово увеличить этот ответ с помощью фрагментов кода. Esp, потому что это решение, которое работает. В противном случае трудно перевести диаграмму. –  Alfie Hanssen 10.07.2016 в 14:39