LWJGL — проблемы с реализацией «roll» в камере 6DOF с использованием кватернионов и матрицы трансляции

#java #matrix-multiplication #lwjgl #quaternions #rotational-matrices

#java #умножение матрицы #lwjgl #кватернионы #вращательные матрицы

Вопрос:

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

Я работаю над созданием класса камеры с использованием LWJGL / Java и использую кватернионы для обработки поворотов по азимуту (рысканию), тангажу и крену. Я бы хотел, чтобы эта камера обрабатывала все 6 степеней перемещения в 3D-пространстве и выполняла рулон. Азимут, тангаж и крен — это все кватернионы. Я умножаю их на кватернион ‘change’ и создаю из него матрицу трансляции. Я помещаю это в буфер с плавающей запятой и умножаю матрицу modelview на мой буфер, содержащий матрицу вращения.

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

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

Ответ №1:

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

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

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

 roll * pitch * yaw
  

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

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

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

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

Комментарии:

1. Спасибо за обновление. Давно не касался этого кода, поэтому я снова набираю скорость с помощью LWJGl. Я думаю, что я близок. После внесения ваших изменений и изменений, предложенных gsimard, я становлюсь ближе. Однако, когда отклонение от курса (в вопросе) равно 0, шаг в порядке. Когда рыскание составляет 180, высота тона меняется на противоположную (вместо этого высота тона перемещает камеру вниз). Кроме того, вращение становится спорадическим после поворота примерно на 20 градусов. Если я продолжу вращать, он выровняется. Так что что-то все еще не совсем правильно. Я выложу код на github и обновлю этот вопрос позже вечером. Я думаю, что это почти там. Еще раз спасибо.

2. Почему вы делаете что-то с матрицей вращения после ее создания? Вы должны иметь возможность отправлять Mat4 непосредственно в шейдер и использовать его для умножения вершин сцены без ее изменения. Я имею в виду, что часть кода «// задает направление». Похоже, вы пытаетесь использовать это для смещения камеры с помощью умножения, но, по моему опыту, это было сбито и пропущено. Попробуйте отказаться от этого.

3. Я думаю , что причина, по которой я сделал это изначально, заключалась в том, что я мог суммировать вектор направления с вектором глаза, чтобы сгенерировать вектор для взаимодействия с объектами в мире. Вектор «выбора» более или менее. Кроме того, я исправил проблему с изменением высоты тона. Я поменял порядок пеленга и шага, поэтому строка читается Quaternion.mul(Quaternion.mul(roll, bearing, change), pitch, change); , все еще пытаясь выяснить, почему крен становится шатким после прокатки определенного количества.

4. Вот полный класс по сути после некоторого рефакторинга и внесения изменений, предложенных вами и gsimard: gist.github.com/bsjohnson/26cbd6d01583c4cbc871

5. Кроме того, настройка m30 — m33 заключается в переводе положения камеры, если игрок переместил персонажа (вперед, назад, обстрел и т. Д.) Если есть лучший способ применить это к матрице вращения, я более чем открыт для ее изменения.

Ответ №2:

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

Когда я когда-либо «переворачиваю» камеру, кажется, что она вращается вокруг глобальной оси 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 .

Комментарии:

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