Я пытаюсь создать программу OpenGL, где предполагается, что модель птицы должна следовать определенному пути вдоль поверхности сферы, описанной сферической спиралью Сеиферта. Тем не менее, я уже давно застрял на поворотах.Вращение кватернионов с тремя осями в OpenGL
В качестве первого шага, я сделать птицу просто следовать по круговой траектории в направлении оси х плоскостей г:
// 1. Circle in x-z plane
float phi = TWO_PI * t; // t = [0..1]
float x = boundingSphereRadius * cos(phi);
float y = 0.0f;
float z = boundingSphereRadius * sin(phi);
float rotationAngle = glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f),
glm::normalize(glm::vec3(x, 0, z)),
glm::vec3(0.0f, 1.0f, 0.0f)) - HALF_PI;
glm::fquat rotation = glm::angleAxis(rotationAngle, glm::vec3(0.0f, 1.0f, 0.0f));
Фиксированный -HALF_PI
необходим, чтобы птица правильно выровнена. Это работает отлично, и аналогичным образом я мог бы добиться кругового вращения в плоскости x-y- и y-z.
Проблема возникает, когда я пытаюсь скопировать все разные вращения. Путь я пытаюсь следовать выглядит так:
Как требование, птичий живот должен всегда обращена к поверхности сферы и птицы должны летать в прямом направлении.
Мой текущий подход выглядит следующим образом, который состоит из просто комбинируя три ориентации кватернионы:
glm::fquat rotationX = glm::angleAxis(glm::orientedAngle(glm::normalize(glm::vec3(0.0f, 0.0f, 1.0f)), glm::normalize(glm::vec3(x, 0, z)), glm::vec3(0.0f, 1.0f, 0.0f)) - HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationY1 = glm::angleAxis(-HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationY2 = glm::angleAxis(glm::orientedAngle(glm::vec3(0.0f, 1.0f, 0.0f), glm::normalize(glm::vec3(x, y, 0)), glm::vec3(0.0f, 0.0f, 1.0f)), glm::vec3(0.0f, 0.0f, 1.0f));
glm::fquat rotationY = rotationY2 * rotationY1;
glm::fquat rotationZ = glm::angleAxis(glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f), glm::normalize(glm::vec3(0, y, z)), glm::vec3(1.0f, 0.0f, 0.0f)) + HALF_PI, glm::vec3(1.0f, 0.0f, 0.0f));
glm::fquat rotation = rotationZ * rotationY * rotationX;
Однако изменения ориентации совершенно неправильно, и при некоторых углах наблюдаются скачки происходят.
EDIT:
Я пробуя различные круги на сфере, где сейчас больше, чем один поворот необходимо. Для beta = gamma = 0.0f
и alpha = HALF_PI
круг снова в х-плоскости г и значение rotationAngleXZ
меняется, в то время как rotationAngleXY
либо -HALF_PI
из HALF_PI
и rotationAngleYZ
либо 0.0f
или PI
. Я предполагаю, что я столкнулся с Gimbal Lock здесь, и я прочитал множество статей об этом, но я все еще не уверен, как я могу предотвратить это в этом случае.
// 10. `Arbitrary` circles on sphere surface
// http://math.stackexchange.com/questions/643130/circle-on-sphere
//
// Parameters:
// alpha = 0...HALF_PI - For alpha = 0, the circle is just a point - For alpha = HALF_PI, the circle is a Great Circle
// (beta, gamma) = center of circle in spherical coordinates
float phi = TWO_PI * t;
float x = boundingSphereRadius * ((sin(alpha) * cos(beta) * cos(gamma)) * cos(phi) + (sin(alpha) * sin(gamma)) * sin(phi) - (cos(alpha) * sin(beta) * cos(gamma)));
float y = boundingSphereRadius * ((sin(alpha) * sin(beta)) * cos(phi) + cos(alpha) * cos(beta));
float z = boundingSphereRadius * (-(sin(alpha) * cos(beta) * sin(gamma)) * cos(phi) + (sin(alpha) * cos(gamma)) * sin(phi) + (cos(alpha) * sin(beta) * sin(gamma)));
float rotationAngleXZ = glm::orientedAngle(glm::normalize(glm::vec3(0.0f, 0.0f, 1.0f)), glm::normalize(glm::vec3(x, 0, z)), glm::vec3(0.0f, 1.0f, 0.0f));
std::cout << "Rotation Angle XZ = " << rotationAngleXZ << std::endl;
glm::fquat rotationXZ = glm::angleAxis(rotationAngleXZ - HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
float rotationAngleXY = glm::orientedAngle(glm::vec3(0.0f, 1.0f, 0.0f), glm::normalize(glm::vec3(x, y, 0)), glm::vec3(0.0f, 0.0f, 1.0f));
std::cout << "Rotation Angle XY = " << rotationAngleXY << std::endl;
glm::fquat rotationXY_Y = glm::angleAxis(-HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationXY_Z = glm::angleAxis(rotationAngleXY, glm::vec3(0.0f, 0.0f, 1.0f));
glm::fquat rotationXY = rotationXY_Z * rotationXY_Y;
float rotationAngleYZ = glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f), glm::normalize(glm::vec3(0, y, z)), glm::vec3(1.0f, 0.0f, 0.0f));
std::cout << "Rotation Angle YZ = " << rotationAngleYZ << std::endl;
glm::fquat rotationYZ = glm::angleAxis(rotationAngleYZ + HALF_PI, glm::vec3(1.0f, 0.0f, 0.0f));
glm::fquat rotation = glm::normalize(rotationXZ) * glm::normalize(rotationXY) * glm::normalize(rotationYZ);
Спасибо за ваш ответ Тейлор! Наверное, я ищу разные решения, где я могу объединить отдельные кватернионы для трех осей - у меня есть ориентационный кватернион для оси x, y- и z, как указано в коде, который я опубликовал , По моему мнению, можно умножить три кватерниона, чтобы получить комбинированную ориентацию, которая является конечной ориентацией моей птицы в каждом кадре. – Schnigges
Я пробовал так много разных способов объединить кватернионы без успеха, и я хотел бы дать щедрость человеку, который может объяснить, как достичь этого (или, может быть, почему это невозможно в том, как я пытаюсь сделайте это.) – Schnigges
Обязательно! Я могу понять, почему вы хотите, чтобы в конце концов кватернион представлял ориентацию, но зачем строить ориентацию через отдельные кватернионы? – Taylor