Как вы можете извлечь ориентацию из матрицы преобразования?

10

У меня есть матрица преобразования 4x4 M, и я хочу выяснить форму сферы при преобразовании M. (Сфера находится в начале координат и имеет радиус 1.)

Я знаю, что могу найти центр, просто умножив М на (0,0,0,1).

Тем не менее, радиус становится проблемой, так как М может сдавливать и вращать сферу. Как я могу узнать новый радиус (ы) полученного эллипсоида? Есть ли способ узнать ориентацию?

Более конкретно, мне нужно знать размер ограничивающей сферы, которая будет заключать трансформированную сферу. Другими словами, что является максимумом | M * V - M * (0,0,0,1) |, где V - единичный вектор (точка на исходной сфере).

CaptainCodeman
источник
1
Разве вы не можете просто вычислить длину преобразованных векторов осей? (3 столбца части вращения вашей матрицы) Ограничивающая сфера будет иметь радиус, равный длине самого длинного вектора.
Барт
Нет, я не думаю, что это правильно. Самое длинное направление не может быть выровнено по оси. (Представьте, что вы раздавили, повернули, снова раздавили, повернули еще немного и т. Д.)
CaptainCodeman
Хм, не уверен, что это важно. Если мне удастся убедить себя, я напишу ответ позже сегодня. ;)
Барт
Проблема в том, что, если вы выполняете преобразование SCALE, базовые векторы матрицы M не должны оставаться ортогональными друг другу.
GPUquant
1
stackoverflow.com/questions/4368961/…
Джефф Гейтс

Ответы:

6

Математически, количество, о котором вы спрашиваете, называется нормой оператора . К сожалению, нет простой формулы для этого. Если это полностью общее аффинное преобразование - например, если оно может иметь произвольную комбинацию поворотов и неравномерных масштабов в любом порядке - тогда я боюсь, что для этого нет ничего, кроме использования разложения по сингулярным значениям . Если вы примените SVD к своей матрице, то наибольшее единственное значение будет максимальным радиусом полученного эллипсоида. Другие особые значения также будут двумя другими его радиусами, и процедура SVD также может извлечь ориентацию осей для вас.

Внедрение SVD не для слабонервных, так как предполагает поиск собственных значений. Если все, что вам нужно, это сами сингулярные значения, они являются квадратными корнями из собственных значений M ^ T * M. Поэтому, если у вас есть под рукой решатель собственных значений 3x3 или вы не против написать его, вы можете использовать его. Если вы хотите извлечь ориентации осей, то это становится более сложным, поскольку вы также должны найти собственные векторы. В этой статье Википедии есть список ссылок на библиотеки для работы с SVD, одну из которых вы можете использовать в своем проекте.

Если форма вашей матрицы ограничена таким образом, что неравномерное масштабирование происходит не более одного раза и является первым примененным преобразованием, то есть наиболее правым, когда вы используете векторы столбцов, тогда вы можете упростить это, просто посмотрев на длину преобразованные векторы оси. Только в этом случае - т. Е. Один неоднородный масштаб, сопровождаемый любой последовательностью поворотов, отражений и равномерных масштабов - если смотреть только на векторы осей, вы получите правильный ответ.

Натан Рид
источник
Спасибо, я ценю подробный ответ. Где разложение, представленное в другом ответе, не работает?
CaptainCodeman
2
@CaptainCodeman Другой ответ просто смотрит на преобразованные векторы оси (то есть столбцы матрицы), как я описал в третьем параграфе. Это не работает в случае, если после поворота имеется неравномерный масштаб, так как тогда масштабирование не применяется вдоль исходных осей.
Натан Рид
2

Может быть, извлечь масштабные коэффициенты из матрицы, а затем использовать максимальное значение ее компонентов. Используя матрицу SRT (Scale-Rotation-Translation), вы можете сделать это следующим образом:

glm::mat4 m = ...;
// Extract col vectors of the matrix
glm::vec3 col1(m[0][0], m[0][1], m[0][2]);
glm::vec3 col2(m[1][0], m[1][1], m[1][2]);
glm::vec3 col3(m[2][0], m[2][1], m[2][2]);
//Extract the scaling factors
glm::vec3 scaling;
scaling.x = glm::length(col1);
scaling.y = glm::length(col2);
scaling.z = glm::length(col3);

float scaleFactor = MAX(scaling.x, MAX(scaling.y, scaling.z));

(на основе http://wklej.org/id/950061/ - имя декомпозировать TRS, а не декомпозировать SRT, потому что я использую имена, упорядоченные по порядку, матрицы которых умножаются в OpenGL).

Теперь вы можете умножить исходный радиус сферы на scaleFactor, и у вас есть ограничивающая сфера.

отворот
источник