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