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