LWJGL - Проблемы реализации «рулона» в камере 6DOF с использованием кватернионов и матрицы перевода - PullRequest
5 голосов
/ 14 октября 2011

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

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

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

Ответы [ 2 ]

3 голосов
/ 24 мая 2013

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

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

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

roll * pitch * yaw

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

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

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

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

3 голосов
/ 04 апреля 2012

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

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

...