Как найти вектор для кватерниона из вращений XYZ - PullRequest
8 голосов
/ 26 марта 2012

Я создаю очень простой проект на OpenGL, и я застрял в ротациях.Я пытаюсь вращать объект самостоятельно по всем 3 осям: X, Y и Z. У меня были бессонные ночи из-за проблемы «блокировки карданного подвеса» после вращения вокруг одной оси.Затем я узнал, что кватернионы решат мою проблему.Я исследовал кватернионы и реализовал его, но я не смог преобразовать мои вращения в кватернионы.Например, если я хочу повернуть вокруг оси Z на 90 градусов, я просто создаю вектор {0,0,1} для своего кватерниона и поворачиваю его вокруг этой оси на 90 градусов, используя следующий код:

http://iphonedevelopment.blogspot.com/2009/06/opengl-es-from-ground-up-part-7_04.html (самая сложная матрица снизу)

Это нормально для одного вектора, но, скажем, сначала я хочу повернуть на 90 градусов вокруг Z, затем на 90 градусов вокруг X (просто в качестве примера),Какой вектор мне нужно передать?Как рассчитать этот вектор?Я не очень разбираюсь в матрицах и тригонометрии (я знаю основы и общие правила, но я просто не одарен), но мне нужно это сделать.Есть много учебников по кватернионам, но я, кажется, не понимаю ни одного (или они не отвечают на мой вопрос).Мне просто нужно научиться строить вектор для поворотов вокруг более чем одной оси.

ОБНОВЛЕНИЕ : я нашел эту прекрасную страницу о кватернионах и решил реализовать их следующим образом: http://www.cprogramming.com/tutorial/3d/quaternions.html

Вот мой код для умножения кватерниона:

void cube::quatmul(float* q1, float* q2, float* resultRef){
 float w = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3];
 float x = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2];
 float y = q1[0]*q2[2] - q1[1]*q2[3] + q1[2]*q2[0] + q1[3]*q2[1];
 float z = q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1] + q1[3]*q2[0];

 resultRef[0] = w;
 resultRef[1] = x;
 resultRef[2] = y;
 resultRef[3] = z;
}

Вот мой код для применения кватерниона к моей матрице вида модели (у меня есть переменная tmodelview, которая является моей целевой моделью видаматрица):

void cube::applyquat(){
 float& x = quaternion[1];
 float& y = quaternion[2];
 float& z = quaternion[3];
 float& w = quaternion[0];
 float magnitude = sqrtf(w * w + x * x + y * y + z * z);
 if(magnitude == 0){
     x = 1;
     w = y = z = 0;
 }else
 if(magnitude != 1){
     x /= magnitude;
     y /= magnitude;
     z /= magnitude;
     w /= magnitude;
  }

 tmodelview[0] = 1 - (2 * y * y) - (2 * z * z);
 tmodelview[1] = 2 * x * y + 2 * w * z;
 tmodelview[2] = 2 * x * z - 2 * w * y;
 tmodelview[3] = 0; 

 tmodelview[4] = 2 * x * y - 2 * w * z;
 tmodelview[5] = 1 - (2 * x * x) - (2 * z * z);
 tmodelview[6] = 2 * y * z - 2 * w * x;
 tmodelview[7] = 0;

 tmodelview[8] = 2 * x * z + 2 * w * y;
 tmodelview[9] = 2 * y * z + 2 * w * x;
 tmodelview[10] = 1 - (2 * x * x) - (2 * y * y);
 tmodelview[11] = 0;

 glMatrixMode(GL_MODELVIEW);
 glPushMatrix();
 glLoadMatrixf(tmodelview);
 glMultMatrixf(modelview);
 glGetFloatv(GL_MODELVIEW_MATRIX, tmodelview);
 glPopMatrix();
}

И мой код вращения (который я вызываю извне), где quaternion - это переменная класса куба:

void cube::rotatex(int angle){
 float quat[4];
 float ang = angle * PI / 180.0;
 quat[0] = cosf(ang / 2);
 quat[1] = sinf(ang/2);
 quat[2] = 0;
 quat[3] = 0;
 quatmul(quat, quaternion, quaternion);
 applyquat();
}

void cube::rotatey(int angle){
 float quat[4];
 float ang = angle * PI / 180.0;
 quat[0] = cosf(ang / 2);
 quat[1] = 0;
 quat[2] = sinf(ang/2);
 quat[3] = 0;
 quatmul(quat, quaternion, quaternion);
 applyquat();
}

void cube::rotatez(int angle){
 float quat[4];
 float ang = angle * PI / 180.0;
 quat[0] = cosf(ang / 2);
 quat[1] = 0;
 quat[2] = 0;
 quat[3] = sinf(ang/2);
 quatmul(quat, quaternion, quaternion);
 applyquat();
}

Я звоню, скажем rotatex, 10-11 раз для вращения только на 1 градус, но мой куб поворачивается почти на 90 градусов после 10-11 раз на 1 градус, что не имеет смысла.Кроме того, после вызова функций вращения по разным осям мой куб перекошается, становится двумерным и исчезает (столбец в матрице вида модели становится все нули) необратимо, что, очевидно, не должно происходить при правильной реализации кватернионов.

1 Ответ

8 голосов
/ 26 марта 2012

Вы подходите к этому неправильно.Если у вас есть три угла поворота Эйлера для осей X, Y и Z, превращение их в кватернион, а затем в матрицу вам не поможет.Блокировка карданного подвеса возникает из-за представления вашего желаемого поворота.Если вы сохраняете вращение, которое вы хотите, как углы XYZ Эйлера, то вы получите блокировку карданного подвеса.

Вам нужно сохранить ориентацию желаемой как кватернион, чтобы получить преимущества.То есть можно взять текущую ориентацию в качестве кватерниона, а затем задать вопрос «как повернуть ее вокруг оси Z на 90 градусов и использовать ее в качестве моей новой ориентации?», Но бесполезно спрашивать «мой текущийориентация определяется этими углами XYZ-Эйлера, как мне превратить это в кватернион? ".

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

Редактировать: Код, который вы разместили, верен, за исключением того, что знаки для tmodelview[6] и tmodelview[9] не правы.

...