LWJGL - Проблемы с реализацией 'roll' в камере 6DOF с использованием кватернионов и матрицы перевода

Я потратил пару недель на эту проблему и, похоже, не могу найти правильного решения и мне нужен совет.

Я работаю над созданием класса Camera, используя LWJGL / Java, и использую Quaternions для обработки пеленга (рыскания), вращения по тангажу и крену. Я бы хотел, чтобы эта камера обрабатывала все 6 градусов движения в трехмерном пространстве и кренилась. Bearing, Pitch и Roll - все кватернионы. Я умножаю их на кватернион «изменение» и создаю из него матрицу перевода. Я помещаю это в буфер с плавающей запятой и умножаю матрицу просмотра модели на мой буфер, содержащий матрицу вращения.

Я могу заставить вращение подшипника и шага работать правильно, но когда я использую крен, я сталкиваюсь с проблемами. В основном вращение вокруг оси Z (качение), похоже, не работает. Когда я «вращаю» камеру, кажется, что она вращается вокруг глобальной оси Z, а не вокруг оси направления локальной камеры. Обычно я могу заставить работать 2 из 3 в зависимости от порядка, в котором я умножаю кватернионы, но я не могу заставить их работать вместе.

Поскольку все они работают независимо, я предполагаю, что что-то не так с моим методом ориентации, когда я комбинирую их и строю матрицу вращения. У меня проблемы с вставкой всего класса, поэтому вот методы и объявления, относящиеся к вращению:

private final static float DEGTORAD = (float)(Math.PI/180);    

//Eye - position of the camera in the 3D world.
private Vector3f eye;

//Camera axis vectors, calculated each time reorient() is called.
//Initialized to global x, y, and z axis initially.
private Vector3f up;
private Vector3f right;
private Vector3f direction;

//Angles of rotation (in degrees)    
private float pitchAngle;
private float bearingAngle;
private float rollAngle;

private Quaternion pitch;
private Quaternion bearing;
private Quaternion roll;

private FloatBuffer viewMatrixBuffer = BufferUtils.createFloatBuffer(16);
private Quaternion currentOrientation;

...

/**
 * Change the bearing (yaw)
 * @param bearing delta in degrees
 */
public void bearing(float bearingDelta){
    bearingAngle += bearingDelta;
    if(bearingAngle > 360){
        bearingAngle -= 360;
    }else if(bearingAngle < 0){
        bearingAngle += 360;
    }
    bearing.setFromAxisAngle(new Vector4f(0f, 1f, 0f, bearingAngle * DEGTORAD));
    bearing.normalise();
}

/**
 * Change the pitch
 * @param pitch delta in degrees
 */
public void pitch(float pitchDelta){
    pitchAngle += pitchDelta;
    if(pitchAngle > 360){
        pitchAngle -= 360;
    }else if(pitchAngle < 0){
        pitchAngle += 360;
    }
    pitch.setFromAxisAngle(new Vector4f(1f, 0f, 0f, pitchAngle * DEGTORAD));
    pitch.normalise();
}

/**
 * @param initialRoll
 */
public void roll(float initialRoll) {
    rollAngle += initialRoll;
    if(rollAngle > 360){
        rollAngle -= 360;
    }else if(rollAngle < 0){
        rollAngle += 360;
    }
    roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD));
    roll.normalise();
}

/**
 * Change direction to focus on a certain point in the world
 * @param eye
 */
public void lookThrough(){
    reorient();
    GL11.glMultMatrix(viewMatrixBuffer);
}    

public void reorient(){
    //Multiply in order: bearing, pitch, roll.  Non-commutative!
    Quaternion change = new Quaternion();
    Quaternion.mul(bearing, pitch, change);
    Quaternion.mul(roll, change, change);
    // orient the camera...
    Matrix4f rotationMatrix = getRotationMatrix(change);

    //Get the looking direction
    direction.x = rotationMatrix.m20;
    direction.y = rotationMatrix.m21;
    direction.z = rotationMatrix.m22;

    //Set the position
    rotationMatrix.m30 = eye.x;
    rotationMatrix.m31 = eye.y;
    rotationMatrix.m32 = eye.z;
    rotationMatrix.m33 = 1;

    rotationMatrix.invert();
    rotationMatrix.store(viewMatrixBuffer);

    viewMatrixBuffer.rewind();

    Vector3f.cross(new Vector3f(0,1,0), direction, null).normalise(right);
    Vector3f.cross(right, direction, null).normalise(up);               
}

Vector3f, Quaternion и Matrix4f - это классы LWJGL, а не индивидуальные.

Итак, мой вопрос: учитывая 3 кватерниона, представляющих пеленг, наклон и крен, как мне изменить матрицу ModelView для точного представления этих вращений?

РЕДАКТИРОВАТЬ: Я чувствую, что это очень близко. См. Ссылку Gist в комментарии RiverC. После поворота на столько градусов изображение сильно прыгает, прежде чем вернуться в нормальное состояние при вращении. Суть есть, но все еще немного не так.


person framauro13    schedule 14.10.2011    source источник


Ответы (2)


Вы делаете умножение в неправильном порядке.

Для двух вращений q1 и q2, если q2 следует за q1 (поскольку вращения обычно не связаны), вы умножаете q2 на q1.

В системе карданного типа, такой как элементы управления для FPS, приоритет всегда следующий: рыскание, тангаж, крен. Это предполагает следующую математику:

roll * pitch * yaw

В качестве точки Java я бы посоветовал не создавать новые Quaternions для каждого обновления, но в любом случае

change = Quaternion.mul(Quaternion.mul(roll, pitch, change), yaw, change);

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

Этот порядок ротации сбивает с толку, но если вы посмотрите вики-страницу о Quaternions, это общее правило.

person Community    schedule 24.05.2013
comment
Спасибо за обновления. Давно не трогал этот код, так что возвращаюсь к работе с LWJGl. Думаю, я близок. После внесения ваших изменений и изменений, предложенных gsimard, я подхожу ближе. Однако, когда рыскание (пеленг в вопросе) равно 0, шаг в порядке. Когда рыскание равно 180, угол наклона меняется на противоположный (вместо этого камера перемещается вниз по наклону вверх). Кроме того, вращение становится спорадическим после поворота примерно на 20 градусов. Если я продолжу вращать, он выровняется. Так что что-то все еще не так. Я выложу код на github и обновлю этот вопрос сегодня вечером. Думаю, это почти готово. Спасибо еще раз. - person framauro13; 26.06.2013
comment
Почему вы что-то делаете с матрицей вращения после ее создания? У вас должна быть возможность отправить Mat4 непосредственно в шейдер и использовать его для умножения вершин сцены, не изменяя его. Я имею в виду, что часть кода «// задает направление». Похоже, вы пытаетесь использовать это, чтобы сместить камеру через умножение, но, по моему опыту, это не удалось. Попробуйте бросить это. - person ; 27.06.2013
comment
Я думаю, что причина, по которой я сделал это изначально, заключалась в том, что я мог суммировать вектор направления с вектором глаза, чтобы сгенерировать вектор для взаимодействия с объектами в мире. Вектор «подбора» более или менее. Также я исправил проблему с переключением высоты тона. Я поменял местами пеленг и шаг, поэтому строка выглядит как Quaternion.mul(Quaternion.mul(roll, bearing, change), pitch, change); Все еще пытаюсь понять, почему крен становится шатким после определенного количества качения. - person framauro13; 27.06.2013
comment
Вот полный класс по сути после некоторого рефакторинга и внесения изменений, предложенных вами и gsimard: gist.github .com / bsjohnson / 26cbd6d01583c4cbc871. - person framauro13; 27.06.2013
comment
Кроме того, настройка m30 - m33 предназначена для преобразования положения камеры, если игрок переместил персонажа (вперед, назад, обстрел и т. Д.). Если есть лучший способ применить это к матрице вращения I ' м более чем открыты для его изменения. - person framauro13; 27.06.2013
comment
Раньше я делал это, но после моего последнего кода я не мог заставить смещение работать, и мне пришлось добавить его в позицию перед поворотом вершины (вместо того, чтобы это происходило автоматически при умножении в шейдере). Я думаю. просто наверное я как-то неправильно делаю матрицу вращения. Другими словами, в шейдере было (modelProjectionMatrix*rotationMatrix*(vertex+offsets)) - person ; 28.06.2013
comment
Я отмечаю, что этот ответ принят, поскольку он в конечном итоге решил проблему прокатки (вместе с ответом gsimard также дайте ему несколько голосов). Проблема дрожания, которую я вижу, похожа на блокировку gimble, но я бы не ожидал, что с использованием кватернионов. Я собираюсь сделать отдельный пост с исходным кодом, чтобы, надеюсь, решить эту проблему и получить работающий класс камеры. - person framauro13; 02.08.2013
comment
Я думаю, что блокировка Gimbal имеет прямое отношение к тому, как вы переводите движение мыши и клавиатуры во вращение / движение. Если вы поработаете с математикой, вы обнаружите, что с порядком подвеса (рыскание, тангаж, крен) вам нужно будет выполнить некоторую тригонометрию с помощью движений клавиатуры или мыши, чтобы полет работал правильно. - person ; 23.08.2013

Я знаю, что это старый, но все равно позвольте мне предположить. Проблема в том, что вы сами сказали:

Когда я «вращаю» камеру, кажется, что она вращается вокруг глобальной оси Z, а не вокруг оси направления локальной камеры.

Это происходит потому, что вы попросили его вращать вектор (0,0,1), то есть глобальную ось Z.

Вот что делают единичные кватернионы: они вращают вектор (здесь набор векторов, ваша матрица вращения) вокруг оси, заданной их мнимой векторной частью (x, y, z), некоторой угловой функцией скаляра w (w = cos (угол / 2)).

Если я понимаю, что вы пытаетесь сделать, то есть вращать камеру, как при наклоне головы слева направо, тогда вам следует построить кватернион поворота вокруг вашего вектора направления:

 roll.setFromAxisAngle(new Vector4f(direction.x, direction.y, direction.z, rollAngle * DEGTORAD));

Я предполагаю, что ваш вектор направления нормализован или что LWJGL знает, что делать с неунитарным вектором оси при вызове setFromAxisAngle.

person gsimard    schedule 04.04.2012
comment
Спасибо за обновления. Я внес это изменение, и оно близко, однако у меня все еще наблюдается странное поведение. Я сделал это изменение, предложенное RiverC, и объяснил новые проблемы в своем комментарии. Я сообщу более подробную информацию, когда вернусь домой сегодня вечером. Еще раз спасибо, я ценю помощь. - person framauro13; 26.06.2013