netlib.narod.ru< Назад | Оглавление | Далее >

12.2. Детали реализации

12.2.1. Вычисление матрицы вида

Сейчас мы покажем, как можно вычислить матрицу преобразования вида, на основании заданных векторов камеры. Предположим, что векторы p = (pxpypz), r = (rxryrz), u = (uxuyuz) и d = (dxdydz) являются соответственно вектором местоположения, правым вектором, верхним вектором и вектором взгляда камеры.

Вспомните, что в главе 2 мы говорили о том, что преобразование пространства вида трансформирует геометрию мира таким образом, что камера помещается в начало координат и ее оси совпадают с осями мировой системы координат (рис. 12.2).


Рис. 12.2. Преобразование из мирового пространства в пространство вида

Рис. 12.2. Преобразование из мирового пространства в пространство вида. В результате этого преобразования камера перемещается в начало координат и поворачивается так, чтобы быть направленной вдоль положительного направления оси Z. Обратите внимание, что все объекты сцены также подвергаются этому преобразованию, так что формируемый камерой вид сцены не изменяется


Следовательно, нам нужна матрица преобразования V, отвечающая следующим требованиям:

Задачу нахождения такой матрицы можно разделить на две части: 1) перемещение, в результате которого камера окажется в начале системы координат и 2) поворот, в результате которого оси камеры будут совпадать с осями мировой системы координат.

12.2.1.1. Часть 1: Перемещение

Чтобы точка p совпадала с началом координат, ее необходимо переместить на –p, поскольку p – p = 0. Так что отвечающая за перемещение часть матрицы преобразования вида задается следующей матрицей:


формула 16

12.2.1.2. Часть 2: Вращение

Выравнивание трех векторов камеры по осям мировой системы координат требует большего объема работ. Нам необходима мтрица поворота A, размером 3 × 3, которая выравнивает правый вектор, верхний вектор и вектор взгляда камеры с осями X, Y и Z мировой системы координат. Такая матрица должна удовлетворять следующим трем выражениям:


формула 17

 

ПРИМЕЧАНИЕ
Мы работаем с матрицами размера 3 × 3 потому что для представления вращения нам не нужны однородные координаты. Позднее мы вернемся к привычным матрицам размера 4 × 4.

Поскольку во всех трех приведенных выше формулах коэффициенты матрицы A одни и те же, можно свести все три формулы в одну, переписав их следующим образом:


формула 18

Существует несколько способов вычисления матрицы A, но легко заметить, что матрица A является инверсией матрицы B потому что BA = BB–1 = I. Поскольку матрица B является ортогональной (ее векторы-строки являются ортонормальными), ее инверсия совпадает с результатом транспонирования. Следовательно, преобразование, выравнивающее векторы ориентации по осям мировой системы координат, выглядит так:


формула 19

12.2.1.3. Комбинирование обеих частей

Теперь мы дополняем A до матрицы 4 × 4 и комбинируем матрицу перемещения с матрицей вращения, получая матрицу преобразования вида V:


формула 20

Мы вычисляем эту матрицу с помощью метода Camera::getViewMatrix:

void Camera::getViewMatrix(D3DXMATRIX* V)
{
     // Делаем оси камеры ортогональными
     D3DXVec3Normalize(&_look, &_look);

     D3DXVec3Cross(&_up, &_look, &_right);
     D3DXVec3Normalize(&_up, &_up);

     D3DXVec3Cross(&_right, &_up, &_look);
     D3DXVec3Normalize(&_right, &_right);

     // Строим матрицу вида:
     float x = -D3DXVec3Dot(&_right, &_pos);
     float y = -D3DXVec3Dot(&_up, &_pos);
     float z = -D3DXVec3Dot(&_look, &_pos);

     (*V)(0, 0) = _right.x;
     (*V)(0, 1) = _up.x;
     (*V)(0, 2) = _look.x;
     (*V)(0, 3) = 0.0f;

     (*V)(1, 0) = _right.y;
     (*V)(1, 1) = _up.y;
     (*V)(1, 2) = _look.y;
     (*V)(1, 3) = 0.0f;

     (*V)(2, 0) = _right.z;
     (*V)(2, 1) = _up.z;
     (*V)(2, 2) = _look.z;
     (*V)(2, 3) = 0.0f;

     (*V)(3, 0) = x;
     (*V)(3, 1) = y;
     (*V)(3, 2) = z;
     (*V)(3, 3) = 1.0f;
}

Возможно, вы не понимаете для чего предназначены первые строки кода данной функции. После нескольких поворотов из-за погрешности округления в операциях с плавающей точкой оси камеры могут стать неортогональными. Поэтому каждый раз при вызове данной функции мы заново вычисляем верхний и правый вектор на основании вектора взгляда, чтобы гарантировать, что все три вектора будут ортогональными. Новый ортогональный верхний вектор вычисляется по формуле up = look × right. Затем вычисляется новый ортогональный правый вектор по формуле right = up × look.

12.2.2. Вращение относительно произвольной оси

Чтобы реализовать методы для поворота нашей камеры, нам необходима возможность вращать ее относительно произвольной оси. Для этой цели библиотека D3DX предоставляет следующую функцию:

D3DXMATRIX *D3DXMatrixRotationAxis(
     D3DXMATRIX *pOut,      // возвращает матрицу вращения
     CONST D3DXVECTOR3 *pV, // ось вращения
     FLOAT Angle            // угол поворота в радианах
);

Рис. 12.3. Вращение относительно произвольной оси

Рис. 12.3. Вращение относительно произвольной оси, определенной вектором A


Предположим, мы хотим выполнить поворот на π/2 радиан вокруг оси, заданной вектором (0.707, 0.707, 0). Для этого надо написать:

D3DXMATRIX R;
D3DXVECTOR3 axis(0.707f, 0.707f, 0.0f);
D3DXMatrixRotationAxis(&R, &axis, D3DX_PI / 2.0f);

Формулу, по которой функция D3DXMatrixRotationAxis вычисляет матрицу вращения вы найдете в книге Эрика Ленджела «Mathematics for 3D Game Programming & Computer Graphics».

12.2.3. Наклон, рыскание и вращение

Поскольку векторы ориентации описывают ориентацию камеры относительно мировой системы координат, мы должны вычислять как они изменяются при наклоне, рыскании и вращении камеры. Сделать это очень просто. Взгляните на рис. 12.4, 12.5 и 12.6, где изображен наклон, рыскание и вращение камеры соответственно.


Рис. 12.4. Наклон

Рис. 12.4. Наклон, или поворот относительно правого вектора камеры

Рис. 12.5. Рыскание

Рис. 12.5. Рыскание, или поворот относительно верхнего вектора камеры

Рис. 12.6. Вращение

Рис. 12.6. Вращение, или поворот относительно вектора взгляда камеры


Как видите при наклоне камеры мы должны повернуть верхний вектор и вектор взгляда относительно правого вектора на заданный угол. Аналогично при рыскании мы поворачиваем правый вектор и вектор взгляда относительно верхнего вектора на указанный угол. А при вращении камеры мы поворачиваем на заданный угол верхний и правый векторы относительно вектора взгляда.

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

Реализация методов для наклона, рыскания и вращения соответствует приведенному выше описанию. Однако для типа камеры LANDOBJECT добавлен ряд ограничений. В частности, неестественно выглядит отклонение от заданного курса при наклоне или вращении наземного объекта. Поэтому при рыскании в модели LANDOBJECT мы выполняем поворот относительно оси Y мировой системы координат, а не относительно верхнего вектора камеры. Кроме того, мы полностью запрещаем вращение наземных объектов. Помните об этом, когда будете использовать класс Camera в своих собственных приложениях; мы предлагаем вам только пример.

Код реализующий наклон, рыскание и вращение камеры выглядит так:

void Camera::pitch(float angle)
{
     D3DXMATRIX T;
     D3DXMatrixRotationAxis(&T, &_right, angle);

     // Поворот векторов _up и _look относительно вектора _right
     D3DXVec3TransformCoord(&_up,&_up, &T);
     D3DXVec3TransformCoord(&_look,&_look, &T);
}

void Camera::yaw(float angle)
{
     D3DXMATRIX T;

     // Для наземных объектов выполняем вращение
     // вокруг мировой оси Y (0, 1, 0)
     if(_cameraType == LANDOBJECT)
          D3DXMatrixRotationY(&T, angle);

     // Для летающих объектов выполняем вращение
     // относительно верхнего вектора
     if(_cameraType == AIRCRAFT)
          D3DXMatrixRotationAxis(&T, &_up, angle);

     // Поворот векторов _right и _look относительно
     // вектора _up или оси Y
     D3DXVec3TransformCoord(&_right, &_right, &T);
     D3DXVec3TransformCoord(&_look, &_look, &T);
}

void Camera::roll(float angle)
{
     // Вращение только для летающих объектов
     if(_cameraType == AIRCRAFT)
     {
          D3DXMATRIX T;
          D3DXMatrixRotationAxis(&T, &_look, angle);

          // Поворот векторов _up и _right относительно
          // вектора _look
          D3DXVec3TransformCoord(&_right, &_right, &T);
          D3DXVec3TransformCoord(&_up, &_up, &T);
     }
}

12.2.4. Ходьба, сдвиг и полет

Говоря о ходьбе мы подразумеваем перемещение в направлении взгляда (то есть вдоль вектора взгляда). Сдвиг — это перемещение в сторону относительно направления взгляда, то есть перемещение вдоль правого вектора. Ну и когда мы говорим о полете, подраумевается перемещение вдоль верхнего вектора. Чтобы переместиться вдоль одной из этих осей мы просто прибавляем к вектору местоположения камеры вектор заданной длины, указывающий в том же направлении, что и ось, вдоль которой перемещается камера (рис. 12.7).


Рис. 12.7. Перемещение вдоль векторов ориентации камеры

Рис. 12.7. Перемещение вдоль векторов ориентации камеры


Как и в случае с поворотами камеры, для наземных объектов мы вносим ряд ограничений. К примеру, объекты LANDOBJECT не могут находиться в воздухе, даже если верхний вектор изменяется в результате движения вперед или сдвига вбок. Следовательно, мы должны ограничить их перемещение плоскостью XZ. Но, поскольку наземные объекты могут изменять свою высоту, взбираясь на лестницы или холмы, мы предоставляем метод Camera::setPosition, позволяющий вручную разместить камеру в требуемом месте и на требуемой высоте.

Код реализации ходьбы, сдвига и полета выглядит следующим образом:

void Camera::walk(float units)
{
     // Для наземных объектов перемещение только в плоскости xz
     if(_cameraType == LANDOBJECT)
          _pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;

     if(_cameraType == AIRCRAFT)
          _pos += _look * units;
}

void Camera::strafe(float units)
{
     // Для наземных объектов перемещение только в плоскости xz
     if(_cameraType == LANDOBJECT)
          _pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;

     if(_cameraType == AIRCRAFT)
          _pos += _right * units;
}

void Camera::fly(float units)
{
     if(_cameraType == AIRCRAFT)
          _pos += _up * units;
}

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz