Подтвердить что ты не робот

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

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

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

Детали реализации выглядят следующим образом:

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

- (SCNVector3)eulerAnglesFromCMQuaternion:(CMQuaternion)q
{
  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 Math Primer для графики и развития игр. Я привык записывать как ссылку, не прочитал ее. Но определенно в моем списке чтения.

Теперь, чтобы имитировать физическое вращение в SceneKit, я вращаю свой node содержащий 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 node. Ссылки объясняют их.

SceneKit Euler Angles

Углы Эйлера Эйлера

И в моем понимании сопоставление между двумя происходит примерно так.

[Image]

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

[Image2]

Я сомневаюсь, что преобразование углов кватерниона → Эйлера вызывает ошибку, а математика кватернионов сейчас на моей голове.

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

4b9b3361

Ответ 1

Спасибо за описание вашей проблемы и начало решения! Я абсолютно уверен, что это невозможно сделать с углами Эйлера из-за карданного замка (свободный от 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);
}

Ответ 2

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

YawNode
  |
  |___PitchNode
      |
      |___RollNode
          |
          |___CameraNode

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

Говоря, я не уверен, что кватернион преобразования → Эйлер - отличная идея. Я подозреваю, что вы не получите непрерывный/плавный переход под углами, такими как 0/PI/2PI... Вместо этого я попробую quaternion- > matrix4 и обновить "преобразование" вашего node.