2014-12-29 3 views
5

Я пытаюсь создать программу 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.

Проблема возникает, когда я пытаюсь скопировать все разные вращения. Путь я пытаюсь следовать выглядит так:

enter image description here enter image description here

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

Мой текущий подход выглядит следующим образом, который состоит из просто комбинируя три ориентации кватернионы:

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); 

ответ

3

Вашего код с помощью углов Эйлера (ось вращения выровнено). Волны и прыжки связаны с тем, что углы Эйлера являются плохой параметризацией пространства трехмерных вращений. Вместо этого здесь два альтернативных подхода.

Построение матрицы поворота с помощью рамы

Если предположить, что птица указывает вниз ось х и в своей собственной локальной системе координат.

Позвольте p = [x y z] быть положением птицы. Пусть v - его вектор скорости. Пусть

f = v/|v| 
up = p/|p| 
s = cross(f, up) 

Теперь построим матрицу со строками f, вверх, s. В частности:

[ f[0] f[1] f[2] ] 
[ up[0] up[1] up[2] ] 
[ s[0] s[1] s[2] ] 

Затем генерируют кватернион с помощью функции quat_cast GLM в.

Избегайте gluLookAt, поскольку он использует устаревший стек фиксированной функции.

Построение с помощью ротаций (кватернионов)

Пусть R0 быть вращение от i к f. (Угол acos(dot(i,f)) и ось cross(i,f))

Пусть R1 быть вращение от R0*j к up. (Использование обозначения умножения матрицы, поскольку в этом контексте проще)

Позвольте R2 быть поворотным от R1*R0*k до s.

Окончательный поворот должен быть R2*R1*R0. Убедитесь, что это вращение равно матрице выше.

+0

Спасибо за ваш ответ Тейлор! Наверное, я ищу разные решения, где я могу объединить отдельные кватернионы для трех осей - у меня есть ориентационный кватернион для оси x, y- и z, как указано в коде, который я опубликовал , По моему мнению, можно умножить три кватерниона, чтобы получить комбинированную ориентацию, которая является конечной ориентацией моей птицы в каждом кадре. – Schnigges

+0

Я пробовал так много разных способов объединить кватернионы без успеха, и я хотел бы дать щедрость человеку, который может объяснить, как достичь этого (или, может быть, почему это невозможно в том, как я пытаюсь сделайте это.) – Schnigges

+0

Обязательно! Я могу понять, почему вы хотите, чтобы в конце концов кватернион представлял ориентацию, но зачем строить ориентацию через отдельные кватернионы? – Taylor

3

У меня нет готового кода для вас, но как насчет этой идеи? Предполагая, что у вас уже есть формула для местоположения птицы x, y, z в зависимости от t (сферическая спираль Сеифферта). Тогда:

eye = fn(t) 
center = fn(t + dt) // where will the bird be in the next time-step 
up  = normalize(eye - sphereCenter) 

Теперь gluLookAt (глаз, центр, вверх) обеспечит матрицу, и вы должны быть в состоянии использовать, чтобы сориентировать птицу.

Эта ссылка также может помочь: https://gamedev.stackexchange.com/questions/41940/how-does-glulookat-work.

Надеется, что это помогает,

--Roger

Смежные вопросы