Кватернион вращается, пытаясь повернуть объект вокруг своей оси

Я показываю символы в Opengl, у меня они вращаются вокруг оси с помощью кватернионов, я хочу добиться, чтобы они вращались вокруг своей оси, но при использовании кватернионов, если я вращаю объект в соответствии с одной осью, другие не принимают учитывая, что это вращение будет вращаться вокруг фиксированной мировой оси. Учитывая накопительный кватернион Q (прежние вращения) и 3 угла AngleX, AngleY ​​и AngleZ. Сначала я подумал, что нужно попробовать вращение Эйлера, и попробовал несколько техник:

Quaternion rot1;
Quaternion rot2;
Quaternion rot3;
Vector3 axis ;
float angle;
QAccumulative.getAxisAngle(&axis, &angle);

// first try : around fix axes
    cout << "axis : " << axis << endl;
    rot1.FromAxis(Vector3(1.0,0.0,0.0),angleX);
    rot2.FromAxis(Vector3(0.0,1.0,0.0),angleY);
    rot3.FromAxis(Vector3(0.0,0.0,1.0),angleZ);
    QAccumulative = QAccumulative * rot1;
    QAccumulative = QAccumulative * rot2;
    QAccumulative = QAccumulative * rot3;


/*
// second try, around current modified axes
    rot1.FromAxis(Vector3(axis.x,0.0,0.0),angleX);
    rot2.FromAxis(Vector3(0.0,axis.y,0.0),angleY);
    rot3.FromAxis(Vector3(0.0,0.0,axis.z),angleZ);
    QAccumulative = QAccumulative * rot1;
    QAccumulative = QAccumulative * rot2;
    QAccumulative = QAccumulative * rot3;

*/
/*
// third try with Euler rotation
    Quaternion rotation;
    rotation.FromEuler(10*angleX,10*angleY,10*angleZ);
    QAccumulative = QAccumulative * rotation;
*/
    QAccumulative.normalise();

Ни один из них пока не работал ... я не думаю, что мои реализации ротации - это проблемы, но если кто-то это сделает, я опубликую код. Разве Эйлер не то, что я думал? Какое чередование я должен использовать для достижения своей цели?

РЕДАКТИРОВАТЬ: я пробовал это, предложенное сообщением:

rotate(float angleX,float angleY,float angleZ) {
    Vector3 axis = Vector3(angleX,angleY,angleZ);
    Vector3 worldAxis = QAccumulative * axis;  
    Quaternion worldRotation( worldAxis.x,worldAxis.y,worldAxis.z, 10 );        
    QAccumulative = worldRotation * QAccumulative;
    QAccumulative.normalise();

Персонаж по-прежнему вращается вокруг фиксированной оси.

РЕДАКТИРОВАТЬ: я применил псевдокод, указанный в сообщении, и он сработал:

rotate(float angleX,float angleY,float angleZ) {
    Vector3 axis = Vector3(angleX,angleY,angleZ);
    Vector3 worldAxis = QAccumulative * axis;  
    Quaternion worldRotationx( 1.0,0,0, angleZ );    
    Quaternion worldRotationy( 0,1.0,0, angleX);        
    Quaternion worldRotationz( 0,0,1.0, -angleY );        
    QAccumulative = worldRotationx * worldRotationy * worldRotationz * QAccumulative;
    QAccumulative.normalise();

person aze    schedule 10.07.2014    source источник
comment
Вы говорите, что хотите иметь возможность вращать персонажа вокруг оси в его локальной системе координат, используя кватернионы?   -  person kbirk    schedule 10.07.2014
comment
Да, именно в случае с персонажем или плоскостью, я хочу, чтобы он вращался согласованно, если вы понимаете, что я имею в виду (местная система координат кажется правильным выражением).   -  person aze    schedule 10.07.2014


Ответы (1)


Мое первое предложение - не определять вращение кватерниона с использованием углов Эйлера:

Вот несколько ссылок, которые объясняют это лучше, чем я могу:

http://bitsquid.blogspot.ca/2013/03/what-is-gimbal-lock-and-why-do-we-still.html

https://mathoverflow.net/questions/95902/the-gimbal-lock-shows-up-in-my-quaternions

http://answers.unity3d.com/questions/573035/avoiding-gimbal-lock.html

Я рекомендую определить ваши вращения как одну ось и угол и написать интерфейс вашего класса, чтобы следовать этому, вот несколько предложений:

Vector3 operator* ( const Vector3& vec ) const;        // rotate a vector
Quaternion operator* ( const Quaternion& quat ) const; // concatenate 
void set( const Vector3& axis, float angle );          // set quaternion
void getAxisAngle( Vector3* axis, float* angle );      // extract axis and angle

Что касается применения вращения локальной оси с использованием кватернионов, вы можете просто преобразовать локальную ось в мировое пространство и применить преобразование таким образом.

Я предполагаю, что вы храните преобразование символов как кватернион вращения, вектор перемещения и вектор / скаляр масштаба. В этом случае вы просто применяете кватернион вращения символов к локальной оси, чтобы перенести его в мировое пространство, а затем используете эту ось мирового пространства для построения и применения вращения, как обычно.

У вас есть текущее вращение ваших персонажей, сохраненное в виде кватерниона:

Quaternion characterQuaternion;

Затем у вас есть локальное вращение, которое вы хотите применить:

Vector3 localAxis;
float angle;

Вам нужно преобразовать локальную ось в мировое пространство, затем построить из нее кватернион и применить его к кватерниону ваших персонажей:

Vector3 worldAxis = characterQuaternion * localAxis;       // transform local axis to world coordinate system   
Quaternion worldRotation( worldAxis, angle );              // build quaternion   
characterQuaternion = worldRotation * characterQuaternion; // apply the rotation

Например, если вы хотите применить к вашему персонажу вращение «креном» на угол 45 единиц (градусы или радианы в зависимости от ваших методов вращения):

введите описание изображения здесь

localAxis = Vector3(1,0,0);
angle = 45;
person kbirk    schedule 10.07.2014
comment
где мне использовать мои новые параметры: angleX, angleY ​​и angleZ? или мне следует делать то, что вы говорите, после одной из техник, которые я описал выше? - person aze; 10.07.2014
comment
извините, я не очень хорошо указал элементы моей программы, я добавил предложение во введении. - person aze; 10.07.2014
comment
Я отредактировал свой ответ. В основном вам следует избегать использования углов Эйлера, поскольку они подвержены блокировке кардана. - person kbirk; 10.07.2014
comment
Спасибо за информацию об Эйлере, я подумал, что это допустимый вариант, но, согласно вашему предложению, я не понимаю, когда мне следует применить новое вращение, characterQuaternion - это мой Qaccumulative, localAxis соответствует Qacc.getAxisAngle (), поэтому где делать я добавляю свои новые ракурсы? Должно быть что-то, чего я не понял из вашего сообщения. - person aze; 10.07.2014
comment
Ваш класс кватерниона должен включать следующие функции: вращение вектора (я использую для этого оператор *), конкатенацию с другим кватернионом (я также использую для этого оператор *) и построение кватерниона по оси и углу (я использую конструктор для этого). - person kbirk; 10.07.2014
comment
Да, хорошо, я пробовал ваше решение, но персонаж все еще движется неправильным образом: применение вращения не меняет другие направления вращения. Вращения не зависят от новой оси. - person aze; 10.07.2014
comment
Подожди секунду, думаю, я понял, что ты говоришь, позволь мне попробовать. РЕДАКТИРОВАТЬ: черт возьми, это сработало, спасибо, я кое-что думаю об этом. Математика - это непросто, но весело. Я собираюсь опубликовать свой рабочий код - person aze; 10.07.2014