Работа с углами Эйлера не очень хорошо работает в 3D-средах, есть несколько проблем и случаев, когда они просто не работают. И вам даже не нужно их использовать.
То, что вы должны сделать, это использовать тот факт, что матрицы преобразования - это не что иное, как основы системы координат, записанные в понятной форме. Итак, у вас есть матрица вида модели MV. Это состоит из преобразования пространства модели, за которым следует преобразование вида (основные матрицы столбцов умножаются справа налево):
MV = V * M
Итак, мы хотим знать, каким образом «камера» находится внутри мира. Это дается вам с помощью матрицы обратного просмотра V^-1
. Конечно, вы можете инвертировать матрицу вида, используя метод Гаусса-Джордана, но большую часть времени ваша матрица вида будет состоять из матрицы вращения 3 × 3 с добавленным столбцом вектора переноса P.
R P
0 1
Напомним, что
(M * N)^-1 = N^-1 * M^-1
, а также
(M * N)^T = M^T * N^T
так что, похоже, существует какая-то связь между транспозицией и инверсией. Не все транспонированные матрицы являются их обратными, но есть некоторые, где транспонирование матрицы является ее обратной. А именно это так называемые ортонормированные матрицы. Вращения ортонормированы. Так
R^-1 = R^T
аккуратный! Это позволяет нам найти обратную матрицу представления следующим образом (я предлагаю вам попытаться доказать это в качестве примера):
V = / R P \
\ 0 1 /
V^-1 = / R^T -P \
\ 0 1 /
Так как это поможет нам разместить новый объект на сцене на расстоянии от камеры? Что ж, V - это преобразование из мирового пространства в пространство камеры, поэтому V ^ -1 превращается из камеры в мировое пространство. Итак, учитывая точку в пространстве камеры, вы можете преобразовать ее обратно в мировое пространство. Скажем, вы хотите разместить что-то в центре вида на расстоянии d
. В пространстве камеры это будет точка (0, 0, -d, 1)
. Умножьте это на V ^ -1:
V^-1 * (0, 0, -d, 1) = (R^T)_z * d - P
Что именно то, что вы хотите. В вашей OpenGL-программе у вас где-то есть матрица представления V, возможно, еще не названная должным образом, но в любом случае она есть. Допустим, вы используете старый OpenGL-1 и GLU's gluLookAt:
void display(void)
{
/* setup viewport, clear, set projection, etc. */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...);
/* the modelview matrix now holds the View transform */
На данный момент мы можем извлечь матрицу вида модели
GLfloat view[16];
glGetFloatv(GL_MODELVIEW_MATRIX, view);
Теперь view
в главном порядке столбца. Если бы мы использовали его напрямую, мы могли бы напрямую обратиться к столбцам. Но помните, что transpose является обратным вращению , поэтому мы на самом деле хотим вектор 3-й строки. Итак, давайте предположим, что вы сохраняете view
, так что в вашем обработчике событий (внешний дисплей) вы можете сделать следующее:
GLfloat z_row[3];
z_row[0] = view[2];
z_row[1] = view[6];
z_row[2] = view[10];
И нам нужна позиция
GLfloat * const p_column = &view[12];
Теперь мы можем вычислить положение новых объектов на расстоянии d
:
GLfloat new_object_pos[3] = {
z_row[0]*d - p_column[0],
z_row[1]*d - p_column[1],
z_row[2]*d - p_column[2],
};
Вот, пожалуйста. Как видите, нигде вам не приходилось работать с углами или тригонометрией, это просто прямая линейная алгебра.