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