glMatrix. Кватернионы


два часа теории перед
практическими занятиями


Клиначев Николай Васильевич

[Home] [←] [PgUp]  [Esc] [Space]  [PgDn] [→] [End]

Способы кодирования ориентации

  1. Углы Эйлера: прецессии, нутации, и собственного вращения (α, β, γ)
  2. Углы Брайнта-Крылова: крен, тонгаж, рысканье (φ, θ, ψ)
  3. Кватернион – 4 числа (x, y, z, w) от -1 до +1
  4. Матрица поворота (3x3, из векторов направлений): right, up, forward

Углы Эйлера – α, β, γ

Хорошо, что ось вращения Земли наклонена. А то было бы как с кораблями – если мачта в зените, угол нутации обнуляется, нелинейные "Кинематические уравнения Эйлера" вырождаются (происходит деление на ноль, на sin от угла нутации). И ни кто не знает, что происходит.

Углы Брайнта-Крылова – φ, θ, ψ

Хорошо, что корабли, у которых мачта лежит на воде, ни кому не нужны. А то было бы как с истребителями – сделал "кобру", дифферент / тонгаж равен 90, нелинейные "Кинематические уравнения Брайнта-Крылова" вырождаются. И опять ни кто не знает, что происходит.

Кватернион – x, y, z, w

Кручу, верчу – лечу куда хочу. "Кинематические уравнения Родрига-Гамильтона" линейны, не вырождаются. :-)

Кодирование ориентации кватернионом – x, y, z, w

Углы – это 360°. Кватернион – 720°.

Углы кодируют ориентацию в пределах одного оборота. Кватернион – в пределах двух. У систем ориентации исчезает запретный сектор для позиционирования. В котором из-за, шумов датчиков, возможны периодические развороты аппарата на 360°.

Кватернион ориентации всегда нормирован

1  ===  (x * x + y * y + z * z + w * w) ^ 0.5

Нормализацию могут нарушить лишь ошибки интегрирования "Кинематических уравнений". Если вы их используете, то нормализуйте кватернион на выходе СУ – quat.normalize.

Для "реверс-движений" (для смены знака кватерниона) не используйте функцию quat.invert. Используйте функцию quat.conjugate

Кватернион и Аффинная матрица

Задание для ориентации определяют кватернионом. Коэффициенты кватерниона пересчитывают в коэффициенты аффинной матрицы. Аффинную матрицу подключают к видеокарте для вывода фрагмента 3D-сцены.

Термины и обозначения

  • Q – ориентация (кватернион)
  • P – позиция (x, y, z)
  • V – вектор (P2 - P1)
  • L – дистанция |V|
  • N – направление (V / L)
  • N – номаль (перпендикуляр)

Префиксы для величин

  • g – задание для величины
  • y – фактическая величина
  • d – приращение величины
  • i – величина со знаком минус
  • z – регистр задержки

эти соглашения из ТАУ

Как определить кватернион?

var gQ = quat.create();         // [0, 0, 0, 1]
quat.fromEuler(gQ, 90, 0, 0);   // из углов Эйлера

Как повернуть кватернион?

// Фрагмент функции обработки нажатия клавиш
// [↑] [↓] [←] [→] [1] [2]
switch (event.keyCode) {
    case 38 : quat.rotateX(gQ, gQ, -0.04); break;
    case 40 : quat.rotateX(gQ, gQ, +0.04); break;
    case 37 : quat.rotateY(gQ, gQ, -0.04); break;
    case 39 : quat.rotateY(gQ, gQ, +0.04); break;
    case 49 : quat.rotateZ(gQ, gQ, +0.04); break;
    case 50 : quat.rotateZ(gQ, gQ, -0.04); break;
    default : return true;
}
return false;

Как вычислить направление света?

// Камера перемещается в неподвижной сцене
// cmQ – кватернион ориентации камеры
// gVL – направление на ламинарный свет (для сцены)
// yVL – направление на ламинарный свет (в камере)
// yVL вычисляют "Реверс-движением". Если камера
// поворачивается по часовой, то свет – против

// Вычислим кватернион, iQcm, для реверс-движения
quat.conjugate(iQcm, cmQ);        // iQcm = "-"cmQ
// Повернем gVL, получим направление света в ..
vec3.transformQuat(yVL, gVL, iQcm);  // оке камеры

// Примечание. |gVL| = 1, |cmQ| = 1  =>  |yVL| = 1

Как определить Матрицу камеры?

// В WebGL камера находиться в позиции [0, 0, 0]
// и видит в направлении [0, 0, -1]. Её движение
// к позиции cmP и ориентация cmQ эмулируются.
// Используем "Метод прямых движений с инверсией"

mat4.fromRotationTranslation(cmMatrix, cmQ, cmP);
mat4.invert(cmMatrix, cmMatrix); // Матрица камеры

// ... Альтернативный "Метод обратных движений"
//     не требует вычисления инверсной матрицы ...

// ... Используют так же функцию  mat4.lookAt  ...

Как определить Матрицу сцены?
(для отрисовки элемента)

// Рисуем станцию "Салют-7"
//   в позиции, yP7, с ориентацией yQ7.
// cmMatrix – аффинная "Матрица камеры"
// mvMatrix – аффинная "Матрица сцены"
// dwMatrix – матрица для текущих операций
//
// Перешли в сцене к станции "Салют-7"
mat4.translate(mvMatrix, cmMatrix, yP7);
// Кватернион ориентации "Салюта-7" в матрицу
mat4.fromQuat(dwMatrix, yQ7);
// Повернули сцену для отрисовки "Салюта-7"
mat4.multiply(mvMatrix, mvMatrix, dwMatrix);
// Выбрали масштаб для отрисовки по осям x, y и z
mat4.scale(mvMatrix, mvMatrix, [4.0, 4.0, 6.0]);

Сумма ориентаций

// gQ – кватернион целевой ориентации (телескопа)
// yQ – кватернион фактической ориентации
// dQ – кватернион дополнительного поворота

quat.multiply(gQ, yQ, dQ);      // gQ = yQ "+" dQ

Повороты суммируют, умножая кватернионы (а деления для кватернионов нет)

Разность ориентаций

// gQ – кватернион целевой ориентации (телескопа)
// yQ – кватернион фактической ориентации
// dQ – кватернион поворота до цели

// Вычислим ошибку ориентации dQ = gQ "-" yQ

// "Реверс движение" – iQ – к начальной ориентации
quat.conjugate(iQ, yQ);         // iQ = "-" yQ
// Переход dQ от yQ к цели через "Реверс движение"
quat.multiply(dQ, iQ, gQ);      // dQ = iQ "+" gQ
// Проверка (движение от фактической к целевой)
quat.multiply(_Q, yQ, dQ);      // _Q = yQ "+" dQ
quat.equals(_Q, gQ) === true;   // === gQ Убедились

Дистанция и поворот → Вектор

// dQ – кватернион до цели
// dL – дистанция до цели
// dP – вектор до цели
// yP – позиция наблюдателя
// yC – позиция цели (искомое)

// Повернём дистанцию [0, 0, -dL] кватернионом
vec3.transformQuat(dP, [0, 0, -dL], dQ);
// yC = yP + dP
vec3.add(yC, yP, dP);           // Нашли позицию Ц

// И, обратно, вектор (кватернионом) в дистанцию
quat.conjugate(dQ, dQ);         // Реверс-движение
vec3.transformQuat(dP, dP, dQ); // dP == [0, 0, -dL]

Вектор → Дистанция и поворот

// dP – вектор до цели – vec3.subtract(dP, yC, yP)
var dP = vec3.fromValues(30, 50, -70);
var dL = vec3.length(dP);   // Дистанция   до цели
var dQ = quat.create();     // Кватернион  до цели
var _P = vec3.create();
vec3.normalize(_P, dP);     // Направление до цели
quat.rotationTo(dQ, [0.0, 0.0, -1.0], _P);

// Проверка. Поворот и дистанция → Вектор
vec3.transformQuat(_P, [0, 0, -dL], dQ);
vec3.equals(_P, dP) === true;   // === dP Убедились

Вектор → Дистанция и поворот

// dP – вектор до цели – vec3.subtract(dP, yC, yP)
var dP = vec3.fromValues(30, 50, -70);
var dL = vec3.length(dP);   // Дистанция до цели
var dQ = quat.create();     // Кватернион до цели
var uV = vec3.fromValues(0, 1, 0);  // Нормаль ↑
var rV = vec3.fromValues(0, 0, 0);  // Нормаль →
vec3.normalize(dP, dP);         // Направление
vec3.cross(rV, dP, uV);         // Перпендикуляр
vec3.normalize(rV, rV);         // Нормаль вправо
vec3.cross(uV, rV, dP);         // Нормаль вверх
quat.setAxes(dQ, dP, rV, uV);   // Матрица поворота
quat.conjugate(dQ, dQ);         // Реверс-движение
// Проверка. Дистанция и поворот → Вектор
vec3.transformQuat([], [0, 0, -dL], dQ); // == dP

Инструменты

Литература

Что делать, как жить?


Делать ракету!
Выполняем комплект л.р. и курсовую аэрокосмического факультета.


29.12.2018