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

Трехмерная математика

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

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

Матричная алгебра

Нет, этот раздел не посвящен Киану Ривзу и его следующему фильму, в котором он застревает внутри калькулятора. Матричная алгебра (matrix math) — это раздел линейной алгебры помогающий упростить и сократить отдельные вычисления. В данном случае это вычисления трехмерных преобразований, о которых я уже упоминал.

Поскольку каждый трехмерный объект состоит из множества вершин, работа Direct3D заключается в преобразовании этих вершин в координаты, пригодные для визуализации графики на экране. Чтобы создать сцену в каждом кадре вы должны преобразовать тысячи вершин. Это достаточно серьезная математика — достаточно чтобы перехватило дух у любого преподавателя математики.

ВНИМАНИЕ!
Обратите внимание, что матричная алгебра используется только когда вы работаете с координатами в трехмерном пространстве. Если вы используете преобразованные координаты (координаты в экранном пространстве), вам не надо применять к ним никаких дополнительных преобразований.

Direct3D работает с преобразованиями с помощью матриц. Матрица (matrix) — это таблица чисел в которой каждый элемент имеет определенное назначение. В рассматриваемом сейчас случае числа представляют преобразования, которые вы хотите применить к вершине. Комбинируя все необходимые вычисления в упакованную форму, такую как матрица, вы значительно упрощаете математику.

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

Матрицы могут быть любого размера, но сейчас мы сосредоточимся на матрицах размера 4 × 4, то есть такие у которых четыре строки и четыре столбца. Direct3D хранит матрицы в виде структуры D3DMATRIX:

typedef struct _D3DMATRIX {
    D3DVALUE _11, _12, _13, _14;
    D3DVALUE _21, _22, _23, _24;
    D3DVALUE _31, _32, _33, _34;
    D3DVALUE _41, _42, _43, _44;
} D3DMATRIX;

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

Каждое преобразование, которое вы будете использовать, имеет собственную матрицу, за исключением вращения для которого необходимы три матрицы (по одной для каждой оси). Это означает, что вам необходимы пять матриц преобразования: вращение относительно оси X, вращение относительно оси Y, вращение относительно оси Z, перемещение и масштабирование. Первый набор функций используется для инициализации матриц вращения:

D3DXMATRIX *D3DXMatrixRotationX(
     D3DXMATRIX *pOut, // итоговая матрица
     FLOAT Angle);     // угол поворота вокруг оси X 

D3DXMATRIX *D3DXMatrixRotationY(
     D3DXMATRIX *pOut, // итоговая матрица
     FLOAT Angle);     // угол поворота вокруг оси Y

D3DXMATRIX *D3DXMatrixRotationZ(
     D3DXMATRIX *pOut, // итоговая матрица
     FLOAT Angle);     // угол поворота вокруг оси Z 

 

ПРИМЕЧАНИЕ
Обратите внимание, что все функции работы с матрицами в библиотеке D3DX также возвращают указатель на D3DXMATRIX. Это указатель на итоговую матрицу и его наличие позволяет использовать функции работы с матрицами в выражениях, как в приведенном ниже примере:
D3DXMATRIX matMatrix, matResult;
matResult = D3DXMatrixRotationZ(&matMatrix, 1.57f);

Передав каждой из перечисленных выше функций пустую матрицу и значение угла поворота (в радианах, представляющее угол поворота относительно соответствующей оси), вы получите матрицу, заполненную необходимыми для работы значениями.

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

D3DXMATRIX *D3DXMatrixTranslation(
     D3DXMATRIX *pOut, // итоговая матрица
     FLOAT x,          // координата по оси X
     FLOAT y,          // координата по оси Y
     FLOAT z);         // координата по оси Z

Координаты — это фактические смещения объекта относительно начала системы координат. Перемещение используется для преобразования объекта из координат его локального пространства в мировые координаты. Теперь представляем функцию, которая масштабирует объект по его осям:

D3DXMATRIX *D3DXMatrixScaling(
     D3DXMATRIX *pOut, // итоговая матрица
     FLOAT sx,         // масштаб по X
     FLOAT sy,         // масштаб по Y
     FLOAT sz);        // масштаб по Z

Обычный масштаб объекта равен 1.0. Чтобы увеличить размер объекта в два раза, укажите значение 2.0, а чтобы сделать объект вполовину меньше его оригинального размера, задайте значение 0.5. Вы также будете использовать специальный тип матрицы, называаемый единичной матрицей (identity matrix). Большая часть элементов такой матрицы равна нулю, а некоторые равны единице. Будучи примененной к другой матрице, единичная матрица не оказывает никакого эффекта и оставляет значения элементов результата теми же самыми, что и в оригинале. Единичная матрица очень полезна, когда вы хотите скомбинировать две матрицы, но не желаете изменять оригиналы.

Для создания единичной матрицы можете использовать следующую функцию (которая получает в своем единственном параметре матрицу):

D3DXMATRIX *D3DXMatrixIdentity(D3DXMATRIX *pOut);

Хотя прототипы функций не очень сложны, вот несколько примеров:

D3DXMATRIX matXRot, matYRot, matZRot;
D3DXMATRIX matTrans, matScale;

// Задаем поворот на 45 градусов (.785 радиан)
D3DXMatrixRotationX(&matXRot, 0.785f);
D3DXMatrixRotationY(&matYRot, 0.785f);
D3DXMatrixRotationZ(&matZRot, 0.785f);

// Задаем перемещение в точку 100,200,300
D3DXMatrixTranslation(&matTrans, 100.0f, 200.0f, 300.0f);

// Увеличиваем размер объекта в два раза по всем осям
D3DXMatrixScaling(&matScale, 2.0f, 2.0f, 2.0f);

Комбинирование матриц

После заполнения различных матриц значениями, используемыми для преобразований, вы можете применить их к каждой вершине. Фактически, вы можете сделать еще проще, скомбинировать отдельные матрицы, содержащие значения для перемещения, вращения и масштабирования в единую матрицу, перемножив их между собой. Эта процедура называется конкатенацией матриц (matrix concatenation), и является основой оптимизации всех матричных вычислений.

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

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

D3DXMATRIX *D3DXMatrixMultiply(
     D3DXMATRIX       *pOut, // Итоговая матрица
     CONST D3DXMATRIX *pM1,  // Исходная матрица 1
     CONST D3DXMATRIX *pM2); // Исходная матрица 2

Вы передаете две матрицы в параметрах pM1 и pM2 и получаете итоговую матрицу pOut, вычисленную как результат умножения первых двух матриц. Остановимся на примере матриц перемещения, масштабирования и вращения, созданных в предыдущем разделе, и скомбинируем их вместе в одну матрицу, представляющую все преобразования.

D3DXMATRIX matResult; // Итоговая матрица

// Очищаем итоговую матрицу, делая ее единичной
D3DXMatrixIdentity(&matResult);

// Умножаем на матрицу масштабирования
D3DXMatrixMultiply(&matResult, &matResult, &matScale);

// Умножаем на матрицы вращения
D3DXMatrixMultiply(&matResult, &matResult, &matXRot);
D3DXMatrixMultiply(&matResult, &matResult, &matYRot);
D3DXMatrixMultiply(&matResult, &matResult, &matZRot);

// Умножаем на матрицу перемещения
D3DXMatrixMultiply(&matResult, &matResult, &matTrans);

Другой метод перемножения матриц между собой — использование оператора умножения (*) объекта D3DXMATRIX, как показано ниже:

matResult = matXRot * matYRot * matZRot * matTrans;

 

ПРИМЕЧАНИЕ
Расширения D3DXMATRIX, такие как оператор умножения, позволяют работать с матрицами так, как будто они являются единым значением. Вы можете складывать, вычитать, умножать и даже делить матрицы используя показанный метод как будто матрица — это отдельное число.

Обратите внимание, что порядок комбинирования матриц очень важен. В предыдущем примере я комбинировал их в следующем порядке: масштабирование, поворот вокруг оси X, поворот вокруг оси Y, поворот вокруг оси Z и перемещение. Если вы будете комбинировать матрицы в любом другом порядке, полученная в результате матрица будет отличаться и может в дальнейшем привести к некоторым нежелательным результатам.

Переход от локальных координат к координатам вида

Чтобы вершина могла использоваться для визуализации грани, она должна быть преобразована из локальных координат (непреобразованных координат) в мировые координаты. Мировые координаты надо преобразовать в координаты вида, а их в свою очередь спроецировать в двухмерные координаты (преобразованные координаты).

Вы преобразуете локальные координаты в мировые координаты с помощью матрицы мирового преобразования (world transformation matrix) или, для краткости, мировой матрицы (world matrix). Эта матрица содержит преобразования, применяемые для размещения объекта в трехмерном мире. Вторая матрица преобразования, которая используется для преобразования объекта в координаты вида, называется матрицей вида (viewing matrix). Последняя, матрица проекции (projection matrix), используется для преобразования трехмерных координат из координат вида в преобразованную вершину, которая применяется для визуализации графики.

Конструируя мировую матрицу и матрицу вида вы должны уделить самое пристальное внимание порядку в котором вы комбинируете отдельные матрицы. Для мирового преобразования вы комбинируете отдельные матрицы преобразований в следующем порядке:

R = S * X * Y * Z * T

R — это итоговая матрица, S — это матрица масштабирования, X — это матрица вращения вокруг оси X, Y — это матрица вращения относительно оси Y, Z — это матрица вращения относительно оси Z и T — это матрица перемещения. В матрице вида отдельные матрицы преобразования должны комбинироваться в следующем порядке (используя только перемещение и вращение):

R = T * X * Y * Z
ПРИМЕЧАНИЕ
Работая с преобразованием вида вы должны использовать обратные значения местоположения точки просмотра для ориентации объектов в поле зрения. Это делается потому что точка просмотра в действительности зафиксирована в точке с координатами 0, 0, 0. Когда вы «перемещаете» точку просмотра, на самом деле все объекты мира перемещаются вокруг вас. Например, если вы хотите переместиться на 10 единиц вперед, вместо этого вам надо переместить все мировые объекты на 10 единиц к вам. Если вы хотите переместить взгляд на 10 градусов левее, надо повернуть все мировые объекты на 10 градусов вправо относительно вас.

Матрица проекции — это особый случай и работать с ней несколько труднее. Создавая матрицу проекции вы должны учесть множество вещей, поскольку она не имеет дела с такими преобразованиями, как перемещение, масштабирование или вращение. Я буду использовать библиотеку D3DX, которая помогает создавать матрицу проекции, о чем я расскажу позже в этой главе в разделе «Преобразование проекции».

На рис. 2.10 показан путь вершины через различные преобразования к заключительному набору координат для рисования.


Рис. 2.10. Непреобразованная вершина проходит через различные матрицы преобразований для получения итоговых координат визуализации

Рис. 2.10. Непреобразованная вершина проходит через различные матрицы преобразований для получения итоговых координат визуализации



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

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