В данном разделе мы рассмотрим принципы построения трехмерных сцен с помощью графической библиотеки . Вначале необходимо познакомиться со схемой работы графического конвейера. Графический конвейер представляет собой некое аппаратно-программное устройство, которое переводит объекты, описанные в трехмерном пространстве XYZ, с учетом положения наблюдателя, во множество пикселей на экране вашего монитора. Ниже приведена блок-схема работы графического конвейера.

"Жизнь" вершин начинается, когда они попадают в графический конвейер из приложения. На первом шаге графического конвейера все вершины объектов подвергаются аффинным преобразованиям - вращению, масштабированию и перемещению. За этот шаг отвечает блок трансформации и освещения либо же вершинный шейдер. По сути, выполняют они одну и ту же работу по преобразованию вершин, но блок трансформации и освещения жестко задает правила обработки, в то время как вершинный шейдер является программируемым элементом на данной стадии. Здесь же происходит расчет освещенности в вершинах в зависимости от количества, места положения и типа источников света. Вершинный шейдер получает на вход одну вершину, содержащую координаты в локальной системе координат, и выдает ее же, но уже трансформированную в координатах системы наблюдателя (камеры).
Отсечение невидимых граней "удаляет" все треугольники сцены, которые расположены нелицевыми сторонами к камере (наблюдателю). Программист может указать
Пользователь может установить дополнительные плоскости отсечения, которые будут отсекать те вершины и треугольники, которые находятся в отрицательном полупространстве плоскостей.
Отсечение по видимому объему задает пирамиду видимости, и примитивы, которые находятся вне этой пирамиды, будут отсекаться.
На следующем шаге графического конвейера производится так называемое однородное преобразование, при котором x, y, z координаты вершин делятся на четвертую компоненту, называемую однородным множителем. После этого преобразования координаты треугольников будут нормализованы, а объем (пирамида) видимости трансформируется в единичный ортогональный куб.
Теперь нормализованные координаты отображаются в пространство экрана. На этом шаге "жизнь" вершин в графическом конвейере заканчивается.
Следующий этап начинается с растеризации треугольников и получении из них массива пикселей. Причем каждый растеризуемый пиксель обладает теми же атрибутами (цвет, текстурные координаты и т.д.) что и вершина после обработки вершинным шейдером. Значения же атрибутов пикселя вычисляются на основе линейной интерполяции вершин примитива.
Шаг мультитекстурирования может производиться либо с помощью механизма работы с текстурами, либо же с помощью пиксельных шейдеров. Пиксельные шейдеры можно рассматривать как некий программируемый механизм обработки пикселей. Причем способ и результат такой обработки зависит только от программиста. Именно с помощью пиксельных шейдеров получают визуальные эффекты, сравнимые с киноэффектами (моделирование воды, травяной поверхности, меха, микрорельефное и попиксельное освещение и др.).
После блока мультитекстурирования наступает фаза различных тестов, после успешного прохождения которых, пиксель попадает в буфер кадра (место в видеопамяти) и отображается на экране либо в заменяющую его текстуру.
В трехмерной графике пространственные объекты, заданные в непрерывном виде, как правило, аппроксимируют (приближают) множеством треугольников. Именно треугольник представляет собой элементарный примитив, с помощью которого описываются все элементы сцены, в том числе и те, которые имеют гладкую форму (сфера, цилиндр, параметрические поверхности и др.).
Процесс формирования изображения должен учитывать две главные сущности. Это визуализируемый объект сцены и наблюдатель. Объект существует в пространстве независимо от кого-либо. Наблюдатель же представляет собой средство формирования изображения наблюдаемых объектов. Именно наблюдатель формирует изображение. Наблюдатель и наблюдаемый объект существуют в одном и том же трехмерном мире, а создаваемое при этом изображение получается двухмерным. Суть процесса формирования изображения состоит в том, чтобы, зная положение наблюдателя и положение объекта, описать (синтезировать) получаемое при этом двухмерное изображение (проекцию).
В большинстве графических библиотек присутствуют следующие сущности трехмерной сцены:
При этом моделируемая трехмерная сцена может описываться тысячами, а иногда миллионами вершин. Для каждой вершины (точки) примитива должны выполняться вычисления по одним и тем же формулам.
Как мы знаем, точка в 3D графике задается, как правило, набором из 4-х значений (x,y,z,w). Реальные координаты точки в пространстве будут (x/w, y/w, z/w), компонент w является масштабом. Обычно для точек w=1. Вектор – направленный отрезок. Вектора равны если у них одинаковая длина и направление. Вектора также задаются в виде линейного массива 1х4 (x,y,z,w), но компонент w у векторов равен 0. Для выполнения преобразований с вершинами используют матричный подход. Матрица размерности MxN – прямоугольная таблица, имеющая M строк, N столбцов и заполненная элементами одного типа. В 3D графике используют, как правило, матрицы размерности 4х4. Таким образом преобразование точки в пространстве сводится к умножению вектор-строки размерности 4 на матрицу преобразования размером 4х4:
Так, например, умножение всех вершин объекта на одну из матриц вращения приведет к вращению этого объекта вокруг оси Ox, Oy или Oz соответственно. Для сложной трансформации объекта можно использовать последовательные преобразования, которые выражаются в перемножении (конкатенации) соответствующих матриц элементарных преобразований. Таким образом, можно сначала рассчитать единую (общую) матрицу преобразования (перемножить между собой все элементарные Vi *MatRot1(…)*MatTrans(…)*MatRot2(…), где Vi – координаты вершин объекта, MatRot1 – матрица поворота вокруг своей оси, MatTrans – матрица перемещения, MatRot2 – матрица вращения по кругу.
Вам не придется самостоятельно запоминать и вычислять все матрицы преобразования "вручную", т.к. во всех библиотеках 3D графики они предусмотрены. В библиотеке определен матричный тип D3DXMATRIX – это структура, которая содержит 4х4 элементов.
Элемент матрицы с индексами ij указывает на значение, хранящееся в строке с номером i и столбце с номером j. Например, элемент _32 указывает на значение m[3][2]. Обращаться к элементам матрицы (читать, записывать) можно двумя способами.
| C++ | D3DXMATRIX m; m._11 = …; или m.m[1][1] = …; |
| Pascal | var m: TD3DXMatrix; … m._11 := …; или m.m[1,1] = …; |
Библиотека содержит богатый набор процедур и функций по работе с матрицами. Приведем основные функции для работы с матрицами.
| C++ | D3DXMATRIX m; // Создание единичной матрицы D3DXMatrixIdentity(m); // Создание матрицы перемещения D3DXMatrixTranslation(m, dx, dy, dz); // Создание матрицы масштабирования D3DXMatrixScaling(m, kx, ky, kz); // Создание матриц вращения D3DXMatrixRotationX(m, angleX); D3DXMatrixRotationY(m, angleY); D3DXMatrixRotationZ(m, angleZ); |
| Pascal | var m: TD3DXMatrix; // Создание единичной матрицы D3DXMatrixIdentity(m); // Создание матрицы перемещения D3DXMatrixTranslation(m, dx, dy, dz); // Создание матрицы масштабирования D3DXMatrixScaling(m, kx, ky, kz); // Создание матриц вращения D3DXMatrixRotationX(m, angleX); D3DXMatrixRotationY(m, angleY); D3DXMatrixRotationZ(m, angleZ); |
При работе с матрицами следует учитывать, что преобразования масштабирования и вращения производятся относительно начала координат. Для комбинирования (умножения) двух матриц существует функция D3DXMatrixMultiply(), которая помещает результат перемножения двух матриц, которые передаются в качестве второго и третьего аргументов, в первый. К примеру, мы хотим повернуть объект вокруг оси Oy на угол 30 градусов и переместить его на вектор (1,2,3). Для этого можно воспользоваться правилом композиции двух элементарных преобразований с помощью матрицы поворота и матрицы перемещения.
| C++ | D3DXMATRIX matRotY, matTrans, matRes; D3DXMatrixRotationY( matRotY, 30*D3DX_PI/180 ); D3DXMatrixTranslation( matTrans, 1, 2, 3 ); D3DXMatrixMultiply(matRes, matRotY, matTrans); |
| Pascal | var matRotY, matTrans, matRes: TD3XDMatrix; D3DXMatrixRotationY( matRotY, 30*pi/180 ); D3DXMatrixTranslation( matTrans, 1, 2, 3 ); D3DXMatrixMultiply( matRes, matRotY, matTrans ); |
Если поменять местами матрицы matRotY и matTrans в функции D3DXMatrixMultiply, то результат преобразования будет иной (сначала объект будет перемещен, а затем повернут). Вообще функция D3DXMatrixMultiply сама возвращает результат перемножения двух матриц, и поэтому ее можно использовать как параметр в матричных операциях. Ниже приведен пример такой возможности.
| C++ | D3DXMATRIX matRotY, matTrans;
D3DXMatrixRotationY( matRotY, … );
D3DXMatrixTranslation( matTrans, … );
device->SetTransform(D3DTS_VIEW,
D3DXMatrixMultiply(NULL, matRotY, matTrans));
|
| Pascal | var
matRotY, matTrans, matRes: TD3XDMatrix;
D3DXMatrixRotationY( matRotY, … );
D3DXMatrixTranslation( matTrans, … );
device.SetTransform(D3DTS_VIEW,
D3DXMatrixMultiply(nil, matRotY, matTrans)^);
|
Для визуализации некоторого объекта (получение его проекции) нам необходимы следующие данные:
В терминах систем координат процесс получения проекции может быть описан следующей упрощенной блок схемой:

Как известно из курса линейной алгебры, переход от одной системы координат к другой эквивалентен смене базиса. Когда нужно преобразовать объект из одного базиса в другой, нужно умножить все вершины объекта на соответствующую матрицу приведения базиса.
Библиотека позволяет работать как в левосторонней системе координат, так и в правосторонней. Далее будем рассматривать все примеры для левосторонней системы координат.
Локальная система координат (локальное пространство) определяет исходные координаты объекта, т.е. в тех в которых он задан. Моделирование объекта в локальной (собственной) системе координат удобнее, чем напрямую в мировой системе координат. Локальная система позволяет описывать объект, не обращая внимания на положение, размер, ориентацию других объектов в мировой системе координат.
После того как заданы все объекты в своих собственных (локальных) системах координат, необходимо привязать их к общей мировой системе координат. Процесс трансформации координат объектов, заданных в локальных системах в мировую (общую) называют мировым преобразованием ( world transform ). Обычно используют 3 типа преобразований: перемещение, масштабирование, вращение. Мировое преобразование описывается с помощью матрицы, используя метод SetTransform интерфейса IDirect3DDevice9. Для применения мирового преобразования метод SetTransform вызывается с параметром D3DTS_WORLD. Предположим, что у нас в сцене присутствуют два объекта: куб и сфера. Мы собираемся отобразить куб в точке с координатами (-3, 2, 6), а сферу в точке (5, 0, -2) мировой системы координат. Это можно проделать с помощью следующих шагов.
| C++ | D3DXMATRIX matCube, matSphere; D3DXMatrixTranslation(matCube, -3.0f, 2.0f, 6.0f); pDirect3DDevice->SetTransform(D3DTS_WORLD, matCube); drawCube(); D3DXMatrixTranslation(matSphere, 5.0f, 0.0f, -2.0f); pDirect3DDevice->SetTransform(D3DTS_WORLD, matSphere); drawSphere(); |
| Pascal | var matCube, matSphere: TD3DXMatrix; … D3DXMatrixTranslation(matCube, -3.0, 2.0, 6.0); device.SetTransform(D3DTS_WORLD, matCube); drawCube; D3DXMatrixTranslation(matShpere, 5.0, 0.0, -2.0); device.SetTransform(D3DTS_WORLD, matSphere); drawSphere; |
Построение проекции весьма сложная и очень неэффективная операция когда камера имеет произвольное положение и направление в мировой системе координат. Чтобы упростить эту ситуацию, мы перемещаем камеру в начало мировой системы координат и поворачиваем ее так, чтобы "взгляд" камеры был направлен в положительную сторону оси Z, как показано на рисунке ниже.

D3DXMatrixLookAtLH(), прототип которой показан ниже:
D3DXMatrixLookAtLH ( matView, // результат Eye, // положение камеры в мировой системе координат At, // точка в мировой системе, куда направлена камера (взгляд) Up // вектор, указывающий "где верх" в мировой системе координат ).
Для установки матрицы вида (преобразование в пространство камеры) используется метод SetTransform с первым параметром D3DTS_VIEW.
Например, поместить камеру в точку (0,0,-3) и направить "взгляд" наблюдателя в начало системы координат, можно с помощью такого кода:
| C++ | D3DXMATRIX matView; D3DXVECTOR3 positionCamera, targetPoint, worldUp; positionCamera = D3DXVECTOR3(0,0,-3); targetPoint = D3DXVECTOR3(0,0,0); worldUp = D3DXVECTOR3(0,1,0); D3DXMatrixLookAtLH(matView, positionCamera, targetPoint, worldUp); device->SetTransform(D3DTS_VIEW, matView); |
| Pascal | var matView: TD3DMatrix;
positionCamera, targetPoint, worldUp : TD3DXVector3;
…
positionCamera := D3DXVector3(0, 0, -3);
targetPoint := D3DXVector3(0, 0, 0);
worldUp := D3DXVector3(0, 1, 0);
D3DXMatrixLookAtLH(matView, positionCamera, targetPoint,
worldUp);
device.SetTransform(D3DTS_VIEW, matView);
|
Таким образом, все объекты сцены будут теперь описаны в системе координат камеры (наблюдателя). Если положение и параметры камеры не меняются в течение работы приложения, то вызов функции SetTransform с параметром D3DTS_VIEW производится один раз.

Последним этапом преобразования при получении 2D изображения является D3DXMatrixPerspectiveFovLH(), для ортогональных же проекций нужна функция D3DXMatrixOrthoOffCenterLH(). Мы рассмотрим примеры работы с перспективной проекцией. Матрица перспективной проекции задает положение передней и задней отсекающих плоскостей, искажения, имитирующие перспективу. Прототип функции D3DXMatrixPerspectiveFovLH() приведен ниже.
D3DXMatrixPerspectiveFovLH( matProj, // результат fov, // вертикальный угол обзора aspect, // отношение ширины окна к высоте zn, // расстояние до передней отсекающей плоскости zf // расстояние до задней отсекающей плоскости )
Ниже приведены иллюстрации, которые поясняют значение последних четырех аргументов данной функции.

Для установки перспективной матрицы необходимо вызвать метод SetTransform с первым параметром D3DTS_PROJECTION. Приведенный ниже пример, создает и устанавливает
| C++ | D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH(matProj, D3DX_PI/2, width/height, 1, 100); device->SetTransform(D3DTS_PROJECTION, matProj); |
| Pascal | var matProj: TD3DXMatrix; D3DXMatrixPerspectiveFovLH(matProj, pi/2, Width/Height, 1, 100); device.SetTransform(D3DTS_PROJECTION, matProj); |
Если параметры проецирования не меняются в течение работы приложения, то вызов функции SetTransform с параметром D3DTS_PROJECTION выполняется единожды.
Таким образом, конвейер преобразований вершин объекта может быть описан следующей блок-схемой:

Каждая вершина трехмерной сцены подвергается следующему преобразованию V' = V*matWorld*matView*matProj, где V – исходная вершина, V' - преобразованная вершина.
Если теперь мы попытаемся выводить объекты сцены, то они будут рисоваться в том порядке, в котором воспроизводятся. При этом нарисованные позже примитивы будут "лежать" поверх ранее выведенных. Для адекватной визуализации трехмерных объектов в графических библиотеках предусмотрен так называемый буфер глубины или z-буфер, который представляет собой двумерный массив, хранящий для каждого растеризуемого пикселя значение координаты z. Рассмотрим принцип работы буфера глубины на примере левосторонней системы координат. В начале в z-буфер заносятся (очищается) максимально возможные значения z, а буфер регенерации заполняется значениями пикселей, соответствующими фону. Затем каждая
(x,y) ;z(x,y) меньше чем значение в z-буфере в позиции (x,y) то в z-буфер заносится значение z-координаты растеризуемой точки, а в буфер регенерации помещается обрабатываемый пиксель.Если выполняется условие шага 2, это означает, что точка многоугольника расположена ближе к наблюдателю, чем точка, значение яркости которой находится в данный момент в позиции (x,y) буфера регенерации.

Единственный недостаток рассмотренного алгоритма – необходимость дополнительной памяти для хранения z-буфера, однако простая и эффективная реализация позволяет использовать этот алгоритм во многих приложениях. Для инициализации буфера глубины в библиотеке достаточно заполнить два поля структуры D3DPRESENT_PARAMETERS:
| C++ | D3DPRESENT_PARAMETERS params; … ZeroMemory( params, sizeof(params) ); … params.EnableAutoDepthStencil = true; params.AutoDepthStencilFormat = D3DFMT_D16; … |
| Pascal | var params: TD3DPresentParameters; … ZeroMemory( @params, SizeOf(params) ); … params.EnableAutoDepthStencil := true; params.AutoDepthStencilFormat := D3DFMT_D16; … |
Программист может не беспокоиться о линейных размерах буфера глубины, т.к. система автоматически определяет размеры. Для работы с буфером глубины необходимо также установить константу режима D3DRS_ZENABLE в значение "истина".
| C++ | device->SetRenderState (D3DRS_ZENABLE, D3DZB_TRUE); |
| Pascal | device.SetRenderState(D3DRS_ZENABLE, 1); |
Чтобы отключить z-буфер, нужно установить данную константу D3DRS_ZENABLE в значение "ложь".
| C++ | device->SetRenderState (D3DRS_ZENABLE, D3DZB_FALSE); |
| Pascal | device.SetRenderState(D3DRS_ZENABLE, 0); |
Теперь в процессе вывода примитивов на экран, при каждом обновлении буфера кадра, необходимо производить очистку, как буфера кадра, так и буфера глубины. Это производится с помощью вызова метода Clear интерфейса IDirect3DDevice9.
| C++ | device->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );
|
| Pascal | device.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(255,255,255), 1.0, 0 );
|
Предпоследний параметр метода Clear задает значение, которым будем заполнен буфер глубины при очистке. Значение единица соответствует бесконечно удаленной точке трехмерного пространства; значение ноль – определяет самую близкую точку.
В первую очередь при визуализации трехмерных объектов, необходимо изменить формат вершины и набор FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
…
};
VERTEX3D data[];
#define MY_FVF (D3DFVF_XYZ | …);
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
…
end;
const
MY_FVF = D3DFVF_XYZ or …;
var
data: array of Vertex3D;
|
Единственное отличие от двумерного случая будет заключаться в наличии операций с матрицами для преобразования объектов. Функции для визуализации трехмерных объектов будут такими же, какие мы использовали в двумерном случае: DrawPrimitive, DrawPrimitiveUP, DrawIndexedPrimitive, DrawIndexedPrimitiveUP. В качестве примера разберем построение, вращающего вокруг одной из координатных осей, цветного треугольника. Запишем шаги, которые мы должны проделать.
FVF флагов;Приведем программные строки для каждого шага алгоритма.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
DWORD color;
};
#define MY_FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE);
...
VERTEX3D points[3] = {
{ -1.0f,-1.0f, 0.0f, 0x00ff0000, },
{ 0.0f, 1.0f, 0.0f, 0x0000ff00, },
{ 1.0f,-1.0f, 0.0f, 0x000000ff, }
}
...
D3DXMATRIX matWorld, matView, matProj;
D3DXMatrixLookAtLH( matView, D3DXVECTOR3 ( 0.0f, 0.0f,-5.0f ),
D3DXVECTOR3 ( 0.0f, 0.0f, 0.0f ),
D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f ) );
device->SetTransform( D3DTS_VIEW, matView );
D3DXMatrixPerspectiveFovLH( matProj, D3DX_PI/4, 1.0f, 1.0f,
100.0f );
device->SetTransform( D3DTS_PROJECTION, matProj );
...
D3DXMatrixRotationY( matWorld, angle );
device->SetTransform( D3DTS_WORLD, matWorld );
device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
color: DWORD;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_DIFFUSE;
var
points: array [0..2] of Vertex3D = (
(x: -1; y: -1; z: 0; color: $000000ff),
(x: 0; y: 1; z: 0; color: $0000ff00),
(x: 1; y: -1; z: 0; color: $00ff0000)
);
matWorld, matView, matProj: TD3DMatrix;
...
D3DXMatrixLookAtLH(matView,
D3DXVector3(0,0,-5),
D3DXVector3(0,0,0),
D3DXVector3(0,1,0));
device.SetTransform(D3DTS_VIEW, matView);
D3DXMatrixPerspectiveFovLH(matProj, PI/4, 1, 1, 100);
device.SetTransform(D3DTS_PROJECTION, matProj);
...
D3DXMatrixRotationY(matWorld, angle);
device.SetTransform(D3DTS_WORLD, matWorld);
device.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
|
Ниже приведен пример нескольких кадров из анимации.

Библиотека располагает механизмом отсечения граней. Идея заключается в том, что можно указать SetRenderState(D3DRS_CULLMODE, <значение>) интерфейса IDirect3DDevice9. В качестве второго параметра может выступать одна из трех констант: D3DCULL_NONE – механизм отбраковки граней выключен;
D3DCULL_CW – отбраковываются грани, вершины которых перечислены по часовой стрелке;
D3DCULL_CCW – отбраковываются грани, вершины которых перечислены против часовой стрелки.
Так, на представленном ниже рисунке грань (треугольник) задается перечислением своих вершин: v0, v1, v2 – по часовой стрелке; v0, v2, v1 – против часовой стрелки.
В данном разделе мы рассмотрим принципы построения трехмерных сцен с помощью графической библиотеки . Вначале необходимо познакомиться со схемой работы графического конвейера. Графический конвейер представляет собой некое аппаратно-программное устройство, которое переводит объекты, описанные в трехмерном пространстве XYZ, с учетом положения наблюдателя, во множество пикселей на экране вашего монитора. Ниже приведена блок-схема работы графического конвейера.

"Жизнь" вершин начинается, когда они попадают в графический конвейер из приложения. На первом шаге графического конвейера все вершины объектов подвергаются аффинным преобразованиям - вращению, масштабированию и перемещению. За этот шаг отвечает блок трансформации и освещения либо же вершинный шейдер. По сути, выполняют они одну и ту же работу по преобразованию вершин, но блок трансформации и освещения жестко задает правила обработки, в то время как вершинный шейдер является программируемым элементом на данной стадии. Здесь же происходит расчет освещенности в вершинах в зависимости от количества, места положения и типа источников света. Вершинный шейдер получает на вход одну вершину, содержащую координаты в локальной системе координат, и выдает ее же, но уже трансформированную в координатах системы наблюдателя (камеры).
Отсечение невидимых граней "удаляет" все треугольники сцены, которые расположены нелицевыми сторонами к камере (наблюдателю). Программист может указать
Пользователь может установить дополнительные плоскости отсечения, которые будут отсекать те вершины и треугольники, которые находятся в отрицательном полупространстве плоскостей.
Отсечение по видимому объему задает пирамиду видимости, и примитивы, которые находятся вне этой пирамиды, будут отсекаться.
На следующем шаге графического конвейера производится так называемое однородное преобразование, при котором x, y, z координаты вершин делятся на четвертую компоненту, называемую однородным множителем. После этого преобразования координаты треугольников будут нормализованы, а объем (пирамида) видимости трансформируется в единичный ортогональный куб.
Теперь нормализованные координаты отображаются в пространство экрана. На этом шаге "жизнь" вершин в графическом конвейере заканчивается.
Следующий этап начинается с растеризации треугольников и получении из них массива пикселей. Причем каждый растеризуемый пиксель обладает теми же атрибутами (цвет, текстурные координаты и т.д.) что и вершина после обработки вершинным шейдером. Значения же атрибутов пикселя вычисляются на основе линейной интерполяции вершин примитива.
Шаг мультитекстурирования может производиться либо с помощью механизма работы с текстурами, либо же с помощью пиксельных шейдеров. Пиксельные шейдеры можно рассматривать как некий программируемый механизм обработки пикселей. Причем способ и результат такой обработки зависит только от программиста. Именно с помощью пиксельных шейдеров получают визуальные эффекты, сравнимые с киноэффектами (моделирование воды, травяной поверхности, меха, микрорельефное и попиксельное освещение и др.).
После блока мультитекстурирования наступает фаза различных тестов, после успешного прохождения которых, пиксель попадает в буфер кадра (место в видеопамяти) и отображается на экране либо в заменяющую его текстуру.
В трехмерной графике пространственные объекты, заданные в непрерывном виде, как правило, аппроксимируют (приближают) множеством треугольников. Именно треугольник представляет собой элементарный примитив, с помощью которого описываются все элементы сцены, в том числе и те, которые имеют гладкую форму (сфера, цилиндр, параметрические поверхности и др.).
Процесс формирования изображения должен учитывать две главные сущности. Это визуализируемый объект сцены и наблюдатель. Объект существует в пространстве независимо от кого-либо. Наблюдатель же представляет собой средство формирования изображения наблюдаемых объектов. Именно наблюдатель формирует изображение. Наблюдатель и наблюдаемый объект существуют в одном и том же трехмерном мире, а создаваемое при этом изображение получается двухмерным. Суть процесса формирования изображения состоит в том, чтобы, зная положение наблюдателя и положение объекта, описать (синтезировать) получаемое при этом двухмерное изображение (проекцию).
В большинстве графических библиотек присутствуют следующие сущности трехмерной сцены:
При этом моделируемая трехмерная сцена может описываться тысячами, а иногда миллионами вершин. Для каждой вершины (точки) примитива должны выполняться вычисления по одним и тем же формулам.
Как мы знаем, точка в 3D графике задается, как правило, набором из 4-х значений (x,y,z,w). Реальные координаты точки в пространстве будут (x/w, y/w, z/w), компонент w является масштабом. Обычно для точек w=1. Вектор – направленный отрезок. Вектора равны если у них одинаковая длина и направление. Вектора также задаются в виде линейного массива 1х4 (x,y,z,w), но компонент w у векторов равен 0. Для выполнения преобразований с вершинами используют матричный подход. Матрица размерности MxN – прямоугольная таблица, имеющая M строк, N столбцов и заполненная элементами одного типа. В 3D графике используют, как правило, матрицы размерности 4х4. Таким образом преобразование точки в пространстве сводится к умножению вектор-строки размерности 4 на матрицу преобразования размером 4х4:
Так, например, умножение всех вершин объекта на одну из матриц вращения приведет к вращению этого объекта вокруг оси Ox, Oy или Oz соответственно. Для сложной трансформации объекта можно использовать последовательные преобразования, которые выражаются в перемножении (конкатенации) соответствующих матриц элементарных преобразований. Таким образом, можно сначала рассчитать единую (общую) матрицу преобразования (перемножить между собой все элементарные Vi *MatRot1(…)*MatTrans(…)*MatRot2(…), где Vi – координаты вершин объекта, MatRot1 – матрица поворота вокруг своей оси, MatTrans – матрица перемещения, MatRot2 – матрица вращения по кругу.
Вам не придется самостоятельно запоминать и вычислять все матрицы преобразования "вручную", т.к. во всех библиотеках 3D графики они предусмотрены. В библиотеке определен матричный тип D3DXMATRIX – это структура, которая содержит 4х4 элементов.
Элемент матрицы с индексами ij указывает на значение, хранящееся в строке с номером i и столбце с номером j. Например, элемент _32 указывает на значение m[3][2]. Обращаться к элементам матрицы (читать, записывать) можно двумя способами.
| C++ | D3DXMATRIX m; m._11 = …; или m.m[1][1] = …; |
| Pascal | var m: TD3DXMatrix; … m._11 := …; или m.m[1,1] = …; |
Библиотека содержит богатый набор процедур и функций по работе с матрицами. Приведем основные функции для работы с матрицами.
| C++ | D3DXMATRIX m; // Создание единичной матрицы D3DXMatrixIdentity(m); // Создание матрицы перемещения D3DXMatrixTranslation(m, dx, dy, dz); // Создание матрицы масштабирования D3DXMatrixScaling(m, kx, ky, kz); // Создание матриц вращения D3DXMatrixRotationX(m, angleX); D3DXMatrixRotationY(m, angleY); D3DXMatrixRotationZ(m, angleZ); |
| Pascal | var m: TD3DXMatrix; // Создание единичной матрицы D3DXMatrixIdentity(m); // Создание матрицы перемещения D3DXMatrixTranslation(m, dx, dy, dz); // Создание матрицы масштабирования D3DXMatrixScaling(m, kx, ky, kz); // Создание матриц вращения D3DXMatrixRotationX(m, angleX); D3DXMatrixRotationY(m, angleY); D3DXMatrixRotationZ(m, angleZ); |
При работе с матрицами следует учитывать, что преобразования масштабирования и вращения производятся относительно начала координат. Для комбинирования (умножения) двух матриц существует функция D3DXMatrixMultiply(), которая помещает результат перемножения двух матриц, которые передаются в качестве второго и третьего аргументов, в первый. К примеру, мы хотим повернуть объект вокруг оси Oy на угол 30 градусов и переместить его на вектор (1,2,3). Для этого можно воспользоваться правилом композиции двух элементарных преобразований с помощью матрицы поворота и матрицы перемещения.
| C++ | D3DXMATRIX matRotY, matTrans, matRes; D3DXMatrixRotationY( matRotY, 30*D3DX_PI/180 ); D3DXMatrixTranslation( matTrans, 1, 2, 3 ); D3DXMatrixMultiply(matRes, matRotY, matTrans); |
| Pascal | var matRotY, matTrans, matRes: TD3XDMatrix; D3DXMatrixRotationY( matRotY, 30*pi/180 ); D3DXMatrixTranslation( matTrans, 1, 2, 3 ); D3DXMatrixMultiply( matRes, matRotY, matTrans ); |
Если поменять местами матрицы matRotY и matTrans в функции D3DXMatrixMultiply, то результат преобразования будет иной (сначала объект будет перемещен, а затем повернут). Вообще функция D3DXMatrixMultiply сама возвращает результат перемножения двух матриц, и поэтому ее можно использовать как параметр в матричных операциях. Ниже приведен пример такой возможности.
| C++ | D3DXMATRIX matRotY, matTrans;
D3DXMatrixRotationY( matRotY, … );
D3DXMatrixTranslation( matTrans, … );
device->SetTransform(D3DTS_VIEW,
D3DXMatrixMultiply(NULL, matRotY, matTrans));
|
| Pascal | var
matRotY, matTrans, matRes: TD3XDMatrix;
D3DXMatrixRotationY( matRotY, … );
D3DXMatrixTranslation( matTrans, … );
device.SetTransform(D3DTS_VIEW,
D3DXMatrixMultiply(nil, matRotY, matTrans)^);
|
Для визуализации некоторого объекта (получение его проекции) нам необходимы следующие данные:
В терминах систем координат процесс получения проекции может быть описан следующей упрощенной блок схемой:

Как известно из курса линейной алгебры, переход от одной системы координат к другой эквивалентен смене базиса. Когда нужно преобразовать объект из одного базиса в другой, нужно умножить все вершины объекта на соответствующую матрицу приведения базиса.
Библиотека позволяет работать как в левосторонней системе координат, так и в правосторонней. Далее будем рассматривать все примеры для левосторонней системы координат.
Локальная система координат (локальное пространство) определяет исходные координаты объекта, т.е. в тех в которых он задан. Моделирование объекта в локальной (собственной) системе координат удобнее, чем напрямую в мировой системе координат. Локальная система позволяет описывать объект, не обращая внимания на положение, размер, ориентацию других объектов в мировой системе координат.
После того как заданы все объекты в своих собственных (локальных) системах координат, необходимо привязать их к общей мировой системе координат. Процесс трансформации координат объектов, заданных в локальных системах в мировую (общую) называют мировым преобразованием ( world transform ). Обычно используют 3 типа преобразований: перемещение, масштабирование, вращение. Мировое преобразование описывается с помощью матрицы, используя метод SetTransform интерфейса IDirect3DDevice9. Для применения мирового преобразования метод SetTransform вызывается с параметром D3DTS_WORLD. Предположим, что у нас в сцене присутствуют два объекта: куб и сфера. Мы собираемся отобразить куб в точке с координатами (-3, 2, 6), а сферу в точке (5, 0, -2) мировой системы координат. Это можно проделать с помощью следующих шагов.
| C++ | D3DXMATRIX matCube, matSphere; D3DXMatrixTranslation(matCube, -3.0f, 2.0f, 6.0f); pDirect3DDevice->SetTransform(D3DTS_WORLD, matCube); drawCube(); D3DXMatrixTranslation(matSphere, 5.0f, 0.0f, -2.0f); pDirect3DDevice->SetTransform(D3DTS_WORLD, matSphere); drawSphere(); |
| Pascal | var matCube, matSphere: TD3DXMatrix; … D3DXMatrixTranslation(matCube, -3.0, 2.0, 6.0); device.SetTransform(D3DTS_WORLD, matCube); drawCube; D3DXMatrixTranslation(matShpere, 5.0, 0.0, -2.0); device.SetTransform(D3DTS_WORLD, matSphere); drawSphere; |
Построение проекции весьма сложная и очень неэффективная операция когда камера имеет произвольное положение и направление в мировой системе координат. Чтобы упростить эту ситуацию, мы перемещаем камеру в начало мировой системы координат и поворачиваем ее так, чтобы "взгляд" камеры был направлен в положительную сторону оси Z, как показано на рисунке ниже.

D3DXMatrixLookAtLH(), прототип которой показан ниже:
D3DXMatrixLookAtLH ( matView, // результат Eye, // положение камеры в мировой системе координат At, // точка в мировой системе, куда направлена камера (взгляд) Up // вектор, указывающий "где верх" в мировой системе координат ).
Для установки матрицы вида (преобразование в пространство камеры) используется метод SetTransform с первым параметром D3DTS_VIEW.
Например, поместить камеру в точку (0,0,-3) и направить "взгляд" наблюдателя в начало системы координат, можно с помощью такого кода:
| C++ | D3DXMATRIX matView; D3DXVECTOR3 positionCamera, targetPoint, worldUp; positionCamera = D3DXVECTOR3(0,0,-3); targetPoint = D3DXVECTOR3(0,0,0); worldUp = D3DXVECTOR3(0,1,0); D3DXMatrixLookAtLH(matView, positionCamera, targetPoint, worldUp); device->SetTransform(D3DTS_VIEW, matView); |
| Pascal | var matView: TD3DMatrix;
positionCamera, targetPoint, worldUp : TD3DXVector3;
…
positionCamera := D3DXVector3(0, 0, -3);
targetPoint := D3DXVector3(0, 0, 0);
worldUp := D3DXVector3(0, 1, 0);
D3DXMatrixLookAtLH(matView, positionCamera, targetPoint,
worldUp);
device.SetTransform(D3DTS_VIEW, matView);
|
Таким образом, все объекты сцены будут теперь описаны в системе координат камеры (наблюдателя). Если положение и параметры камеры не меняются в течение работы приложения, то вызов функции SetTransform с параметром D3DTS_VIEW производится один раз.

Последним этапом преобразования при получении 2D изображения является D3DXMatrixPerspectiveFovLH(), для ортогональных же проекций нужна функция D3DXMatrixOrthoOffCenterLH(). Мы рассмотрим примеры работы с перспективной проекцией. Матрица перспективной проекции задает положение передней и задней отсекающих плоскостей, искажения, имитирующие перспективу. Прототип функции D3DXMatrixPerspectiveFovLH() приведен ниже.
D3DXMatrixPerspectiveFovLH( matProj, // результат fov, // вертикальный угол обзора aspect, // отношение ширины окна к высоте zn, // расстояние до передней отсекающей плоскости zf // расстояние до задней отсекающей плоскости )
Ниже приведены иллюстрации, которые поясняют значение последних четырех аргументов данной функции.

Для установки перспективной матрицы необходимо вызвать метод SetTransform с первым параметром D3DTS_PROJECTION. Приведенный ниже пример, создает и устанавливает
| C++ | D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH(matProj, D3DX_PI/2, width/height, 1, 100); device->SetTransform(D3DTS_PROJECTION, matProj); |
| Pascal | var matProj: TD3DXMatrix; D3DXMatrixPerspectiveFovLH(matProj, pi/2, Width/Height, 1, 100); device.SetTransform(D3DTS_PROJECTION, matProj); |
Если параметры проецирования не меняются в течение работы приложения, то вызов функции SetTransform с параметром D3DTS_PROJECTION выполняется единожды.
Таким образом, конвейер преобразований вершин объекта может быть описан следующей блок-схемой:

Каждая вершина трехмерной сцены подвергается следующему преобразованию V' = V*matWorld*matView*matProj, где V – исходная вершина, V' - преобразованная вершина.
Если теперь мы попытаемся выводить объекты сцены, то они будут рисоваться в том порядке, в котором воспроизводятся. При этом нарисованные позже примитивы будут "лежать" поверх ранее выведенных. Для адекватной визуализации трехмерных объектов в графических библиотеках предусмотрен так называемый буфер глубины или z-буфер, который представляет собой двумерный массив, хранящий для каждого растеризуемого пикселя значение координаты z. Рассмотрим принцип работы буфера глубины на примере левосторонней системы координат. В начале в z-буфер заносятся (очищается) максимально возможные значения z, а буфер регенерации заполняется значениями пикселей, соответствующими фону. Затем каждая
(x,y) ;z(x,y) меньше чем значение в z-буфере в позиции (x,y) то в z-буфер заносится значение z-координаты растеризуемой точки, а в буфер регенерации помещается обрабатываемый пиксель.Если выполняется условие шага 2, это означает, что точка многоугольника расположена ближе к наблюдателю, чем точка, значение яркости которой находится в данный момент в позиции (x,y) буфера регенерации.

Единственный недостаток рассмотренного алгоритма – необходимость дополнительной памяти для хранения z-буфера, однако простая и эффективная реализация позволяет использовать этот алгоритм во многих приложениях. Для инициализации буфера глубины в библиотеке достаточно заполнить два поля структуры D3DPRESENT_PARAMETERS:
| C++ | D3DPRESENT_PARAMETERS params; … ZeroMemory( params, sizeof(params) ); … params.EnableAutoDepthStencil = true; params.AutoDepthStencilFormat = D3DFMT_D16; … |
| Pascal | var params: TD3DPresentParameters; … ZeroMemory( @params, SizeOf(params) ); … params.EnableAutoDepthStencil := true; params.AutoDepthStencilFormat := D3DFMT_D16; … |
Программист может не беспокоиться о линейных размерах буфера глубины, т.к. система автоматически определяет размеры. Для работы с буфером глубины необходимо также установить константу режима D3DRS_ZENABLE в значение "истина".
| C++ | device->SetRenderState (D3DRS_ZENABLE, D3DZB_TRUE); |
| Pascal | device.SetRenderState(D3DRS_ZENABLE, 1); |
Чтобы отключить z-буфер, нужно установить данную константу D3DRS_ZENABLE в значение "ложь".
| C++ | device->SetRenderState (D3DRS_ZENABLE, D3DZB_FALSE); |
| Pascal | device.SetRenderState(D3DRS_ZENABLE, 0); |
Теперь в процессе вывода примитивов на экран, при каждом обновлении буфера кадра, необходимо производить очистку, как буфера кадра, так и буфера глубины. Это производится с помощью вызова метода Clear интерфейса IDirect3DDevice9.
| C++ | device->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );
|
| Pascal | device.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(255,255,255), 1.0, 0 );
|
Предпоследний параметр метода Clear задает значение, которым будем заполнен буфер глубины при очистке. Значение единица соответствует бесконечно удаленной точке трехмерного пространства; значение ноль – определяет самую близкую точку.
В первую очередь при визуализации трехмерных объектов, необходимо изменить формат вершины и набор FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
…
};
VERTEX3D data[];
#define MY_FVF (D3DFVF_XYZ | …);
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
…
end;
const
MY_FVF = D3DFVF_XYZ or …;
var
data: array of Vertex3D;
|
Единственное отличие от двумерного случая будет заключаться в наличии операций с матрицами для преобразования объектов. Функции для визуализации трехмерных объектов будут такими же, какие мы использовали в двумерном случае: DrawPrimitive, DrawPrimitiveUP, DrawIndexedPrimitive, DrawIndexedPrimitiveUP. В качестве примера разберем построение, вращающего вокруг одной из координатных осей, цветного треугольника. Запишем шаги, которые мы должны проделать.
FVF флагов;Приведем программные строки для каждого шага алгоритма.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
DWORD color;
};
#define MY_FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE);
...
VERTEX3D points[3] = {
{ -1.0f,-1.0f, 0.0f, 0x00ff0000, },
{ 0.0f, 1.0f, 0.0f, 0x0000ff00, },
{ 1.0f,-1.0f, 0.0f, 0x000000ff, }
}
...
D3DXMATRIX matWorld, matView, matProj;
D3DXMatrixLookAtLH( matView, D3DXVECTOR3 ( 0.0f, 0.0f,-5.0f ),
D3DXVECTOR3 ( 0.0f, 0.0f, 0.0f ),
D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f ) );
device->SetTransform( D3DTS_VIEW, matView );
D3DXMatrixPerspectiveFovLH( matProj, D3DX_PI/4, 1.0f, 1.0f,
100.0f );
device->SetTransform( D3DTS_PROJECTION, matProj );
...
D3DXMatrixRotationY( matWorld, angle );
device->SetTransform( D3DTS_WORLD, matWorld );
device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
color: DWORD;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_DIFFUSE;
var
points: array [0..2] of Vertex3D = (
(x: -1; y: -1; z: 0; color: $000000ff),
(x: 0; y: 1; z: 0; color: $0000ff00),
(x: 1; y: -1; z: 0; color: $00ff0000)
);
matWorld, matView, matProj: TD3DMatrix;
...
D3DXMatrixLookAtLH(matView,
D3DXVector3(0,0,-5),
D3DXVector3(0,0,0),
D3DXVector3(0,1,0));
device.SetTransform(D3DTS_VIEW, matView);
D3DXMatrixPerspectiveFovLH(matProj, PI/4, 1, 1, 100);
device.SetTransform(D3DTS_PROJECTION, matProj);
...
D3DXMatrixRotationY(matWorld, angle);
device.SetTransform(D3DTS_WORLD, matWorld);
device.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
|
Ниже приведен пример нескольких кадров из анимации.

Библиотека располагает механизмом отсечения граней. Идея заключается в том, что можно указать SetRenderState(D3DRS_CULLMODE, <значение>) интерфейса IDirect3DDevice9. В качестве второго параметра может выступать одна из трех констант: D3DCULL_NONE – механизм отбраковки граней выключен;
D3DCULL_CW – отбраковываются грани, вершины которых перечислены по часовой стрелке;
D3DCULL_CCW – отбраковываются грани, вершины которых перечислены против часовой стрелки.
Так, на представленном ниже рисунке грань (треугольник) задается перечислением своих вершин: v0, v1, v2 – по часовой стрелке; v0, v2, v1 – против часовой стрелки.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.