Отображение физического вращения 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]]()
Я сомневаюсь, что преобразование углов кватерниона → Эйлера вызывает ошибку, а математика кватернионов сейчас на моей голове.
Надеюсь, что я предоставил всю информацию, если что-то еще потребуется, я просто рад добавить.
Ответы
Ответ 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.