iOS OpenGL ES 2.0 Quaternion Rotation Slerp to XYZ position

Я следую руководству по кватернионам: http://www.raywenderlich.com/12667/how-to-rotate-a-3d-object-using-touches-with-opengl и пытаюсь повернуть глобус в какое-то положение XYZ. У меня есть исходный кватернион, и я генерирую случайное местоположение XYZ на поверхности земного шара. Я передаю это местоположение XYZ в следующую функцию. Идея заключалась в том, чтобы сгенерировать вектор lookAt с помощью GLKMatrix4MakeLookAt и определить конечный кватернион для шага slerp из матрицы lookAt.

- (void)rotateToLocationX:(float)x andY:(float)y andZ:(float)z {

    // Turn on the interpolation for smooth rotation
    _slerping = YES; // Begin auto rotating to this location
    _slerpCur = 0;
    _slerpMax = 1.0;
    _slerpStart = _quat;

    // The eye location is defined by the look at location multiplied by this modifier
    float modifier = 1.0;

    // Create a look at vector for which we will create a GLK4Matrix from
    float xEye = x;
    float yEye = y;
    float zEye = z;
    //NSLog(@"%f %f %f %f %f %f",xEye, yEye, zEye, x, y, z);
    _currentSatelliteLocation = GLKMatrix4MakeLookAt(xEye, yEye, zEye, 0, 0, 0, 0, 1, 0);
    _currentSatelliteLocation = GLKMatrix4Multiply(_currentSatelliteLocation,self.effect.transform.modelviewMatrix);

    // Turn our 4x4 matrix into a quat and use it to mark the end point of our interpolation
    //_currentSatelliteLocation = GLKMatrix4Translate(_currentSatelliteLocation, 0.0f, 0.0f, GLOBAL_EARTH_Z_LOCATION);
    _slerpEnd = GLKQuaternionMakeWithMatrix4(_currentSatelliteLocation);

    // Print info on the quat
    GLKVector3 vec = GLKQuaternionAxis(_slerpEnd);
    float angle = GLKQuaternionAngle(_slerpEnd);
    //NSLog(@"%f %f %f %f",vec.x,vec.y,vec.z,angle);

    NSLog(@"Quat end:");
    [self printMatrix:_currentSatelliteLocation];
    //[self printMatrix:self.effect.transform.modelviewMatrix];

}

Интерполяция работает, я получаю плавное вращение, однако конечное местоположение никогда не совпадает с вводом XYZ — я знаю это, потому что мой глобус представляет собой сферу, и я вычисляю XYZ из широты и долготы. Я хочу смотреть прямо вниз по вектору «lookAt» к центру Земли из этого положения широты/долготы на поверхности земного шара после вращения. Я думаю, что это может иметь какое-то отношение к вектору вверх, но я пробовал все, что имело смысл.

Что я делаю неправильно. Как я могу определить последний кватернион, который, когда я заканчиваю вращение, смотрит вниз на вектор XYZ на поверхности земного шара? Спасибо!


person PhilBot    schedule 05.06.2013    source источник


Ответы (2)


Это следующее ваше значение: центр вашего земного шара (0, 0, 0), радиус R, начальная позиция (0, 0, R), ваша конечная позиция (0, R, 0), поэтому вращайте глобус 90 градусов вокруг оси X? Если это так, просто установите положение глаза функции взгляда в конечное положение, а параметры взгляда в центр земного шара.

m_target.x = 0.0f;
m_target.y = 0.0f;
m_target.z = 1.0f;

m_right.x = 1.0f;
m_right.y = 0.0f;
m_right.z = 0.0f;

m_up.x = 0.0f;
m_up.y = 1.0f;
m_up.z = 0.0f;
void CCamera::RotateX( float amount )
{
    Point3D target = m_target;
    Point3D up = m_up;

    amount = amount / 180 * PI;

    m_target.x = (cos(PI / 2 - amount) * up.x) + (cos(amount) * target.x);
    m_target.y = (cos(PI / 2 - amount) * up.y) + (cos(amount) * target.y);
    m_target.z = (cos(PI / 2 - amount) * up.z) + (cos(amount) * target.z);

    m_up.x = (cos(amount) * up.x) + (cos(PI / 2 + amount) * target.x);
    m_up.y = (cos(amount) * up.y) + (cos(PI / 2 + amount) * target.y);
    m_up.z = (cos(amount) * up.z) + (cos(PI / 2 + amount) * target.z);

    Normalize(m_target);
    Normalize(m_up);
}

void CCamera::RotateY( float amount )
{
    Point3D target = m_target;
    Point3D right = m_right;

    amount = amount / 180 * PI;

    m_target.x = (cos(PI / 2 + amount) * right.x) + (cos(amount) * target.x);
    m_target.y = (cos(PI / 2 + amount) * right.y) + (cos(amount) * target.y);
    m_target.z = (cos(PI / 2 + amount) * right.z) + (cos(amount) * target.z);

    m_right.x  = (cos(amount) * right.x) + (cos(PI / 2 - amount) * target.x);
    m_right.y  = (cos(amount) * right.y) + (cos(PI / 2 - amount) * target.y);
    m_right.z  = (cos(amount) * right.z) + (cos(PI / 2 - amount) * target.z);

    Normalize(m_target);
    Normalize(m_right);
}

void CCamera::RotateZ( float amount )
{
    Point3D right = m_right;
    Point3D up = m_up;

    amount = amount / 180 * PI;

    m_up.x = (cos(amount) * up.x) + (cos(PI / 2 - amount) * right.x);
    m_up.y = (cos(amount) * up.y) + (cos(PI / 2 - amount) * right.y);
    m_up.z = (cos(amount) * up.z) + (cos(PI / 2 - amount) * right.z);

    m_right.x = (cos(PI / 2 + amount) * up.x) + (cos(amount) * right.x);
    m_right.y = (cos(PI / 2 + amount) * up.y) + (cos(amount) * right.y);
    m_right.z = (cos(PI / 2 + amount) * up.z) + (cos(amount) * right.z);

    Normalize(m_right);
    Normalize(m_up);
}

void CCamera::Normalize( Point3D &p )
{
    float length = sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
    if (1 == length || 0 == length)
    {
        return;
    }

    float scaleFactor = 1.0 / length;
    p.x *= scaleFactor;
    p.y *= scaleFactor;
    p.z *= scaleFactor;
}
person Merlin    schedule 05.06.2013
comment
Привет, я попробовал ваше предложение, но я не получаю вращения для координат xyz, которые я ввожу. Я рассматриваю существующие вращения, умножая координаты xyz на матрицу базового эффекта, содержащую вращения. Интересно, что каждый раз, когда я называю это «автоповоротом», я получаю другое конечное место. У тебя есть другие идеи? Спасибо! - person PhilBot; 06.06.2013
comment
@PhilBot Или, может быть, вы можете рассчитать угол поворота вокруг осей x, y, z и попробовать вычислить ответ. - person Merlin; 06.06.2013

Ответ на этот вопрос представляет собой комбинацию следующей функции rotateTo и изменения кода из руководства Рэя по адресу ( http://www.raywenderlich.com/12667/how-to-rotate-a-3d-object-using-touches-with-opengl). Как говорится в одном из комментариев к этой статье, в GLKQuaternion умножается произвольный коэффициент 2,0 Q_rot = GLKQuaternionMakeWithAngleAndVector3Axis(угол * 2,0, ось);. Удалите эту «2» и используйте следующую функцию для создания _slerpEnd — после этого земной шар будет плавно вращаться до указанного XYZ.

// Rotate the globe using Slerp interpolation to an XYZ coordinate
- (void)rotateToLocationX:(float)x andY:(float)y andZ:(float)z {

    // Turn on the interpolation for smooth rotation
    _slerping = YES; // Begin auto rotating to this location
    _slerpCur = 0;
    _slerpMax = 1.0;
    _slerpStart = _quat;

    // Create a look at vector for which we will create a GLK4Matrix from
    float xEye = x;
    float yEye = y;
    float zEye = z;
    _currentSatelliteLocation = GLKMatrix4MakeLookAt(xEye, yEye, zEye, 0, 0, 0, 0, 1, 0);

    // Turn our 4x4 matrix into a quat and use it to mark the end point of our interpolation
    _slerpEnd = GLKQuaternionMakeWithMatrix4(_currentSatelliteLocation);

}
person PhilBot    schedule 08.06.2013