Для построения реалистичного изображения недостаточно только удалить невидимые
При расчете освещенности грани в компьютерной графике учитывают следующие типы отражения света от поверхности: рассеянное, диффузное, зеркальное. Интенсивность освещения
При наличии в сцене точечного источника света, интенсивность диффузного отражения пропорциональна косинусу угла между нормалью к поверхности и направлением на источник света. В этом случае для вычисления интенсивности диффузного отражения применяют закон косинусов Ламберта: $$I=I_{p}k_{d}cos \theta$$, где $$I_{p}$$ - интенсивность точечного источника света, $$k_{d} \in [0,1]$$ - коэффициент диффузного отражения, $$\theta$$ - угол падения, рассчитываемый как угол между направлением на источник света и нормалью к поверхности. Если вектора нормализованы, т.е. |, то $$\cos \theta =(\overrightarrow N \overrightarrow L)$$ - скалярное произведение векторов, и уравнение можно записать в виде $$I_p k_d (\overline N \cdot \overline L)$$.

Свойством диффузного отражения является равномерность по всем направлениям отраженного света. Поэтому такие объекты имеют одинаковую яркость, вне зависимости от угла обзора. К примеру, матовые поверхности обладают таким свойством. Предположим, что у нас имеется два полигона, одинаково ориентированы относительно источника света, но расположенные на разных расстояниях от него. Если вычислить значения интенсивностей освещенности для каждого полигона, по приведенной формуле, то они окажутся одинаковыми. Это означает, что объекты, расположенные на разном расстоянии от источника света, будут освещаться независимо от их местоположения в трехмерной сцене. В этом случае интенсивность света должна быть обратнопропорциональна квадрату расстояния от источника до d - расстояние от центра проекции (наблюдателя) до поверхности, k - некоторая константа, k>=1.
Зеркальное отражение можно получить от любой блестящей поверхности. Осветите ярким светом обычное яблоко, и световой блик возникнет в результате зеркального отражения, а свет, отраженный от остальной части яблока, появится вследствие диффузного отражения. Следует отметить, что в том месте, где находится световой блик, яблоко будет казаться не красным, а, скорее всего – белым, т.е. окрашенным в цвет падающего света. Если изменить положение наблюдателя, то световой блик тоже сместится. Это объясняется тем, что блестящие поверхности отражают свет неодинаково по всем направлениям. От идеально зеркальной поверхности свет отражается только в том направлении, для которого углы падения и отражения совпадают. Зеркально отраженный свет можно будет увидеть, если угол между вектором отражения (на рисунке обозначен R ) и вектором наблюдения (на рисунке обозначен S ) равен нулю.

Для неидеальных отражающих поверхностей интенсивность отраженного света резко падает с ростом $$\alpha.$$ В модели, предложенной Фонгом, быстрое убывание интенсивности света, описывается функцией $$cos^{n}\alpha$$, где $$n \in [1,200]$$ в зависимости от вида поверхности. Для идеальной отражающей поверхности $$n\to\infty$$. Интенсивность зеркального отражения света по модели Фонга записывается как $$I=I_{p}*k_{S}*cos^{n} \alpha$$, где $$I_{p}$$ - интенсивность источника света, $$k_{S} \in [0,1]$$ - коэффициент зеркального отражения. Если вектора и нормированы, то формула может быть преобразована к виду:$$I=I_{pks} (\overline R \cdot \overline S)^n.$$
Интенсивность зеркального света обратно пропорциональна расстоянию от источника до грани. Для практических задач здесь используют также модель линейного затухания
света, которая выражается
следующей формулой:$$I=\frac{I_{pks} (\overline R \cdot \overline S)}{d+k}^n,$$
где d - расстояние от наблюдателя до поверхности, k - некоторая константа.
Таким образом, интенсивность грани складывается из трех составляющих: рассеянного света, диффузного света и зеркального света. Формула расчета общей интенсивности с учетом расстояния от наблюдателя до освещенной грани и трех составляющих света записывается как$$I=I_ak_a+\frac{I_p}{d+k}(k_d (\overline L \cdot \overline N)+k_s (\overline R \cdot \overline S)^n).$$
Для более реалистичного отображения объектов сцены в библиотеке определены понятия материал и свет. Материал в задает свойства поверхности отображаемых примитивов. С помощью материала определяется как будет отражаться от поверхности примитива свет. Материал и свет (в контексте ) используются совместно. Одно без другого работать не будет.
Для работы с материалом предусмотрен D3DMATERIAL9, который содержит следующие поля.
| C++ | typedef struct _D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; {рассеянный свет, исходящий от материала}
D3DCOLORVALUE Ambient; {определяет окружающий свет}
D3DCOLORVALUE Specular; {определяет отражающий (зеркальный) свет}
D3DCOLORVALUE Emissive; {определяет излучающий свет материала}
float Power; {мощность отражения}
} D3DMATERIAL9;
|
| Pascal | TD3DMaterial9 = packed record
Diffuse: TD3DColorValue; {рассеянный свет, исходящий от материала}
Ambient: TD3DColorValue; {определяет окружающий свет}
Specular: TD3DColorValue; {определяет отражающий (зеркальный) свет}
Emissive: TD3DColorValue; {определяет излучающий свет материала}
Power: Single; {мощность отражения}
end;
|
Задавая параметры структуры D3DMATERIAL9, тем самым мы определяем свойства поверхности отображаемого объекта. Процесс создания и использование (назначение) материала включает в себя следующие шаги:
D3DMATERIAL9 ;Программно эти шаги реализуются таким образом:
| C++ | D3DMATERIAL9 material; ZeroMemory( material, sizeof(D3DMATERIAL9) ); material.Diffuse=D3DXCOLOR(0.7f, 0.0f, 0.0f, 0.0f); material.Ambient=D3DXCOLOR(0.2f, 0.0f, 0.0f, 0.0f); material.Specular=D3DXCOLOR(0.1f, 0.0f, 0.0f, 0.0f); ... device->SetMaterial( material ); |
| Pascal | var material: TD3DMaterial9; ... ZeroMemory(@material,SizeOf(TD3DMaterial9)); material.Diffuse:=D3DXColor(0.7, 0, 0, 0); // r, g, b, a material.Ambient:=D3DXColor(0.2, 0, 0, 0); material.Specular:=D3DXColor(0.1, 0, 0, 0); ... device.SetMaterial(material); |
Метод SetMaterial вызывается при выводе объектов (в процедуре рендеринга). Кроме определения свойств материала необходимо задать источники света. Библиотека поддерживает три типа источника света:



Свет в состоит из трех составляющих: рассеянного, окружающего, зеркального, которые независимо друг от друга принимают участие в вычислении освещения граней. Они напрямую взаимодействуют с тремя цветами, определенными в свойствах материала. Результат такого сочетания (взаимодействия) и есть конечный цвет освещения сцены. В сценах могут присутствовать до восьми различных по свойствам источников света. Для инициализации источника света в необходимо проделать следующие шаги:
D3DLIGHT9 ;Структура D3DLIGHT9 содержит следующие поля:
| C++ | typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type; {тип источника света}
D3DCOLORVALUE Diffuse; {рассеянный свет, излучающий источником}
D3DCOLORVALUE Specular; {зеркальный свет, излучающий источником}
D3DCOLORVALUE Ambient; {окружающий свет, излучающий источником}
D3DVECTOR Position; {положение источника в сцене} для точечного
D3DVECTOR Direction; {направление падающего света (вектор)}
float Range; {максимальное расстояние освещения} для точечного
float Falloff; {используется в прожекторном источнике}
float Attenuation0; {определяют закон изменения освещения}
float Attenuation1; {от параметра расстояния}
float Attenuation2; {1/(A0+A1*D+A2*D^2),D–расстояние от источника}
float Theta; {внутренний угол источника света}
float Phi; {внешний угол, [0…pi]}
} D3DLIGHT9;
|
| Pascal | TD3DLight9 = packed record
_Type: TD3DLightType; {тип источника света}
Diffuse: TD3DColorValue; {рассеянный свет, излучающий источником}
Specular: TD3DColorValue; {зеркальный свет, излучающий источником}
Ambient: TD3DColorValue; {окружающий свет, излучающий источником}
Position: TD3DVector; {положение источника в сцене} для точечного
Direction: TD3DVector; {направление падающего света (вектор)}
Range: Single; {максимальное расстояние освещения} для точечного
Falloff: Single; {используется в прожекторном источнике}
Attenuation0: Single; {определяют закон изменения освещения}
Attenuation1: Single; {от параметра расстояния}
Attenuation2: Single; {1/(A0+A1*D+A2*D^2),D–расстояние от источника}
Theta: Single; {внутренний угол источника света}
Phi: Single; {внешний угол, [0…pi]}
end;
|
Тип источника задается указанием одной из трех констант:
D3DLIGHT_POINT, // точечный источник D3DLIGHT_SPOT, // источник-прожектор D3DLIGHT_DIRECTIONAL. // направленный источник
Ниже приведен пример установки направленного источника освещения в сцене.
| C++ | // создание и заполнение полей структуры, установка типа источника D3DLIGHT9 light; ZeroMemory(light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Direction = D3DXVECTOR3(0.0f, 0.0f,1.0f); ... // разрешаем работы с освещенностью device->SetRenderState( D3DRS_LIGHTING, TRUE ); ... // установка и включение первого (и единственного) источника света device->SetLight( 0, light ); device->LightEnable( 0, TRUE ); |
| Pascal | var light: TD3DLight9; ... // создание и заполнение полей структуры, установка типа источника ZeroMemory(@light,SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_DIRECTIONAL; light.Diffuse:=D3DXColor(1,0,0,0); light.Direction:=D3DXVector3(0,0,1); // разрешаем работы с освещенностью device.SetRenderState(D3DRS_LIGHTING, 1); ... // установка и включение первого (и единственного) источника света device.SetLight(0,light); device.LightEnable(0,true); |
Но одних материалов и источников света недостаточно для того, чтобы объекты сцены были освещены. При использовании освещения необходимо определить нормаль к каждой грани трехмерного примитива. Нормаль представляет собой вектор, расположенный перпендикулярно одной их сторон выводимого примитива.

Именно с помощью нормалей рассчитывается освещенность объекта (граней). Как правило, нормали задаются в каждой вершине примитива.

Поэтому необходимо корректно изменить формат вершин и набор FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
FLOAT nx, ny, nz;
};
VERTEX3D data[];
#define MY_FVF (D3DFVF_XYZ | D3DFVF_NORMAL);
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
nx, ny, nz: Single;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_NORMAL;
var
data: array of Vertex3D;
|
Следует заметить, что для правильной освещенности располагает функцией D3DXVec3Normalize(), которая позволяет привести вектор к единичной длине.
| C++ | D3DXVECTOR3 direction = D3DXVECTOR3(0.0f, 0.0f,1.0f); D3DXVec3Normalize( (D3DXVECTOR3*)light.Direction, direction ); |
| Pascal | D3DXVec3Normalize( light.Direction, D3DXVector3(0, 0, 1) ); |
Кроме того, чтобы все нормали вершин после серии преобразований автоматически имели длину единица, можно установить переменную состояния D3DRS_NORMALIZENORMALS в значение "истина".
| C++ | device->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE ); |
| Pascal | device.SetRenderState(D3DRS_NORMALIZENORMALS, 1 ); |
Для того, чтобы источник в сцене был активирован нужно проделать следующие шаги. Сначала нужно вызвать процедуру установки (регистрации) нужного источника света. Это реализуется через вызов метода SetLight() интерфейса IDirect3DDevice9. Данный метод имеет два параметра: номер источника (лампы), и второй – указатель на переменную типа D3DLIGHT9. Второй шаг состоит в вызове метода LightEnable() интерфейса IDirect3DDevice9 для включения/выключения нужного источника света. Данный метод имеет два параметра: номер источника и второй – булевская
Ниже приведен пример освещения направленным источником света треугольной грани, у которой нормали в каждой вершине одинаковы. При этом положение камеры (наблюдателя) задано точкой (2,2,-2), а направление лучей света – (-2,-2,2).

Как видно в каждом положении грань освещена одинаково (нормали в каждой вершине равны (0, 0, -1) ). Изменив хотя бы одну нормаль вершины, можно добиться того, что грань будет освещаться уже неравномерно. Пусть одна из вершин будет теперь иметь нормаль (1,0,-1). Результат освещенности грани при таких изменениях нормали показан ниже.

Ниже приведены примеры заполнения "нужных" полей для точечного и прожекторного источников света.
Пример для точечного источника света.
| C++ | D3DLIGHT9 light; ZeroMemory( light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_POINT; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Position = D3DXVECTOR3(2.0f, 2.0f, -2.0f); light.Attenuation0 = 1.0f; light.Range = 100; |
| Pascal | var light: TD3DLight9; ... ZeroMemory(@light, SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_POINT; light.Diffuse:=D3DXColor(1,1,0,0); light.Position:=D3DXVector3(2,2,-2); light.Attenuation0:=1; light.Range:=100; |
Пример для прожекторного источника света.
| C++ | D3DLIGHT9 light; ZeroMemory( light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_SPOT; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Position = D3DXVECTOR3(2.0f, 2.0f, -2.0f); D3DXVECTOR3 direction = D3DXVECTOR3(-2.0f, -2.0f, 2.0f); D3DXVec3Normalize( (D3DXVECTOR3*)light.Direction, direction ); light.Attenuation0 = 1.0f; light.Range = 100; light.Phi = D3DX_PI/2; light.Theta = D3DX_PI /3; light.Falloff = 1.0f; |
| Pascal | var light: TD3DLight9; ... ZeroMemory(@light, SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_SPOT; light.Diffuse:=D3DXColor(1,1,0,0); light.Position:=D3DXVector3(2,2,-2); D3DXVec3Normalize(light.Direction, D3DXVector3(-2,-2,2)); light.Attenuation0:=1; light.Range:=100; light.Phi:=pi/2; light.Theta:=pi/3; light.Falloff:=1; |
Библиотека располагает рядом встроенных функций для построения простых стандартных трехмерных примитивов (куб, цилиндр, сфера, тор):
D3DXCreatePolygon // полигонD3DXCreateBox // параллелограммD3DXCreateCylinder // цилиндрD3DXCreateSphere // сфераD3DXCreateTorus // торD3DXCreateTeapot // чайникПри создании объектов таким способом вершины "получают" формат, в котором присутствует положение (D3DFVF_XYZ) и нормаль (D3DFVF_NORMAL). Существует возможность задавать уровень детализации при создании перечисленных выше трехмерных примитивов. Следует заметить, что нормаль к каждой вершине созданного объекта вычисляется автоматически. Рассмотрим их функции создания и визуализации. Для работы с подобными примитивами необходимо объявить переменную ID3DXMesh, в которой и будет "храниться" трехмерный объект. Визуализация созданного объекта осуществляется вызовом метода DrawSubset интерфейса ID3DXMesh.
Функция создания полигона (правильного многоугольника):
D3DXCreatePolygon( ссылка на устройство вывода, длина стороны полигона, количество сторон полигона, результат, указатель на смежные треугольники);
Ниже приведен пример создания и визуализации трехмерного объекта – полигона.
| C++ | // объявление переменной LPD3DXMESH polygon; // создание объекта полигон D3DXCreatePolygon( device, 0.5, 10, polygon, NULL); // визуализация polygon->DrawSubset(0); |
| Pascal | // объявление переменной var polygon: ID3DXMesh; // создание объекта полигон D3DXCreatePolygon(device, 0.5, 10, polygon, nil); // визуализация polygon.DrawSubset(0); |
Ниже приведены примеры построения полигонов с разными значениями второго и третьего параметра рассмотренной функции.
| Длина стороны = 0.5 Количество сторон = 10 |
Длина стороны = 2 Количество сторон = 3 |
Длина стороны = 0.1 Количество сторон = 36 |
![]() |
![]() |
![]() |
Функция вывода параллелепипеда
D3DXCreateBox( ссылка на устройство вывода, ширина, высота, глубина, результат, указатель на смежные треугольники);
Ниже приведен пример создания и визуализации трехмерного объекта – полигона.
| C++ | // объявление переменной LPD3DXMESH box; // создание объекта D3DXCreateBox( device, 1.0f, 0.5f, 2.0f, box, NULL); // визуализация box->DrawSubset(0); |
| Pascal | // объявление переменной var box: ID3DXMesh; // создание объекта D3DXCreateBox(device, 1, 0.5, 2, box, nil); // визуализация box.DrawSubset(0); |
Примеры вывода параллелограмма с различными длинами сторон
| ширина = 1 высота = 0.5 глубина = 2 |
ширина = 1 высота = 1 глубина = 1 |
ширина = 0.5 высота = 2 глубина = 1 |
![]() |
![]() |
![]() |
Функция построения цилиндра
D3DXCreateCylinder( ссылка на устройство радиус первого основания радиус второго основания высота цилиндра количество разбиений "по радиусу" количество разбиений "по длине" результат указатель на смежные треугольники);

| C++ | LPD3DXMESH cylinder; D3DXCreateCylinder(device, 0.2f, 0.2f, 1, 16, 3, cylinder, NULL); cylinder->DrawSubset(0); |
| Pascal | var cylinder: ID3DXMesh; D3DXCreateCylinder(device, 0.2, 0.2, 1, 16, 3, cylinder, nil); cylinder.DrawSubset(0); |
| Первое основание = 0.2 Второе основание = 0.2 Разбиений по радиусу = 16 |
Первое основание = 0.4 Второе основание = 0.2 Разбиений по радиусу = 16 |
Первое основание = 0.4 Второе основание = 0 Разбиений по радиусу = 6 |
![]() |
![]() |
![]() |
Функция построения сферы:
D3DXCreateSphere( ссылка на устройство, радиус сферы, разбиений "по радиусу", // число апельсиновых долек разбиений "вдоль", // результат, указатель на смежные треугольники);
| Разбиений по радиусу = 6, вдоль = 32 | Разбиений по радиусу = 32, вдоль = 6 |
![]() |
![]() |
| C++ | LPD3DXMESH sphere; D3DXCreateSphere(device, 1.0f, 16, 16, sphere, NULL); sphere->DrawSubset(0); |
| Pascal | var sphere: ID3DXMesh; D3DXCreateSphere(device, 1, 16, 16, sphere, nil); sphere.DrawSubset(0); |
Функция построения тора:
D3DXCreateTorus( ссылка на устройство, внутренний радиус тора, внешний радиус тора, количество разбиений в поперечном сечении, количество разбиений по кругу, результат, указатель на смежные треугольники);
| Разбиений в сечение = 6 Разбиений по кругу = 32 |
Разбиений в сечение = 32 Разбиений по кругу = 6 |
![]() |
![]() |
| C++ | LPD3DXMESH torus; D3DXCreateTorus(device, 0.2f, 0.8f, 32, 6, torus, NULL); torus->DrawSubset(0); |
| Pascal | var torus: ID3DXMesh; D3DXCreateTorus(device, 0.2, 0.8, 32, 6, torus, nil); torus.DrawSubset(0); |
Функция построения чайника:
D3DXCreateTeapot( ссылка на устройство, результат, указатель на смежные треугольники);
Это единственный примитив, для которого отсутствует возможность задать уровень детализации.

| C++ | LPD3DXMESH teapot; D3DXCreateTeapot(device, teapot, NULL); teapot->DrawSubset(0); |
| Pascal | var teapot: ID3DXMesh; D3DXCreateTeapot(device, teapot, nil); teapot.DrawSubset(0); |
Ниже приведены примеры освещения трехмерного объекта точечным источником света с различными характеристиками.
light.Ambient = D3DXCOLOR(0.3f, 0,0,0); |
![]() |
light.Diffuse = D3DXCOLOR(0.5f, 0.0, 0,0); light.Ambient = D3DXCOLOR(0.2f, 0,0,0); |
![]() |
light.Diffuse = D3DXCOLOR(0.5, 0.0, 0,0); light.Ambient = D3DXCOLOR(0.2, 0,0,0); light.Specular = D3DXCOLOR(0.3,0,0.0,0); |
![]() |
Следует заметить, что зеркальное освещение требует больших вычислений, чем другие типы освещения, поэтому библиотека Direct3DM предоставляет возможность отключать его (по умолчанию оно выключено). Для включения зеркального освещения необходимо установить константу состояния D3DRS_SPECULARENABLE в значение "истина".
| C++ | device->SetRenderState( D3DRS_SPECULARENABLE, TRUE ); |
| Pascal | device.SetRenderState(D3DRS_SPECULARENABLE, 1); |
Как мы уже говорили, библиотека
| C++ | D3DMATERIAL9 material; D3DLIGHT9 light0, light1, light2; ZeroMemory( material, sizeof(D3DMATERIAL9) ); material.Diffuse=D3DXCOLOR(1.0f, 1.0f, 1.0f, 0.0f); ZeroMemory( light0, sizeof(D3DLIGHT9) ); light0.Type = D3DLIGHT_POINT; light0.Diffuse = D3DXCOLOR(0.5f, 0.0f, 0.0f, 0.0f); light0.Position = D3DXVECTOR3(4.0f, 0.0f, 0.0f); … ZeroMemory( light1, sizeof(D3DLIGHT9) ); light1.Type = D3DLIGHT_POINT; light1.Diffuse = D3DXCOLOR(0.0f, 0.5f, 0.0f, 0.0f); light1.Position = D3DXVECTOR3(0.0f, 0.0f, -4.0f); … ZeroMemory( light2, sizeof(D3DLIGHT9) ); light2.Type = D3DLIGHT_POINT; light2.Diffuse = D3DXCOLOR(0.0f, 0.0f, 0.5f, 0.0f); light2.Position = D3DXVECTOR3(-4.0f, 0.0f, 0.0f); device->SetRenderState( D3DRS_LIGHTING, TRUE ); ... // процедура рендеринга device->SetLight( 0, light0 ); device->LightEnable( 0, TRUE ); device->SetLight( 1, light1 ); device->LightEnable( 1, TRUE ); device->SetLight( 2, light2 ); device->LightEnable( 2, TRUE ); |
| Pascal | var material: TD3DMaterial9; light0, light1, light2: TD3DLight9; ZeroMemory(@material,SizeOf(TD3DMaterial9)); material.Diffuse:=D3DXColor(1,1,1,0); light0._Type:=D3DLIGHT_POINT; light0.Diffuse:=D3DXColor(0.5, 0.0, 0, 0); light0.Position:=D3DXVector3(4,0,0); … light1._Type:=D3DLIGHT_POINT; light1.Diffuse:=D3DXColor(0.0, 0.5, 0, 0); light1.Position:=D3DXVector3(0,0,-4); … light2._Type:=D3DLIGHT_POINT; light2.Diffuse:=D3DXColor(0.0, 0.0, 0.5, 0); light2.Position:=D3DXVector3(-4,0,0); … device.SetRenderState(D3DRS_LIGHTING, 1); // процедура рендеринга device.SetLight(0,light0); device.LightEnable(0,true); device.SetLight(1,light1); device.LightEnable(1,true); device.SetLight(2,light2); device.LightEnable(2,true); |

Сложные (содержащие десятки тысяч вершин) объекты, как правило, создаются в профессиональных трехмерных редакторах (3D Studio, Maya, содержит средства для загрузки и визуализации моделей, содержащихся в x-файлах. Х-файл – файл, содержащий в себе данные о трехмерном объекте (координаты вершин, материалы, нормали, текстуры и анимацию). Вначале необходимо объявить нужные переменные.
| C++ | LPD3DXMESH mesh; |
| Pascal | var mesh: ID3DXMesh; |
Для загрузки трехмерного объекта (модели) из Х-файла предназначена функция
D3DXLoadMeshFromX( путь до файла на диске, набор флагов, указатель на устройство вывода, информация о смежных треугольниках, данные о материалах модели, данные об эффектах модели, количество материалов в модели, результат ).
В простейшем случае (если модель не содержит информации о материалах, текстурах, смежности граней) параметры с четвертого по седьмой включительно выставляют в NULL. Ниже приведен пример загрузки модели из Х-файла.
| C++ | D3DXLoadMeshFromX("ship.x", D3DXMESH_MANAGED,
device, NULL, NULL, NULL, NULL, mesh);
|
| Pascal | D3DXLoadMeshFromX('ship.x', D3DXMESH_MANAGED,
device, nil, nil, nil, nil, mesh);
|
Для визуализации трехмерной модели из Х-файла необходимо вызвать метод DrawSubset интерфейса ID3DXMesh, где в качестве параметра передается номер выводимой группы (подмножества) модели. Если модель не разбита на группы, то параметр метода равен нулю.
| C++ | mesh->DrawSubset(0); |
| Pascal | mesh.DrawSubset(0); |
Для того чтобы трехмерная модель выглядела более реалистично, можно добавить в сцену источники света и определить материалы.

Принцип текстурирования трехмерных объектов ничем не отличается от двумерного аналога. Для каждой треугольной грани необходимо задать текстурные координаты. Единственное отличие от двумерного варианта заключается в формате вершин и наборе FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
FLOAT u, v;
};
#define MY_FVF (D3DFVF_XYZ | D3DFVF_TEX1);
...
VERTEX3D points[3] = {
{ -1.0f,-1.0f, 0.0f, 0.0f, 1.0f, },
{ 0.0f, 1.0f, 0.0f, 0.5f, 0.0f, },
{ 1.0f,-1.0f, 0.0f, 1.0f, 1.0f, }
}
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
u, v: Single;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_TEX1;
var
points: array [0..2] of Vertex3D = (
(x: -1; y: -1; z: 0; u: 0; v: 1),
(x: 0; y: 1; z: 0; u: 0.5; v: 0),
(x: 1; y: -1; z: 0; u: 1; v: 1)
);
|
Ниже показан пример вывода куба, у которого все грани покрыты различными текстурами. Для этого можно объявить массив для хранения текстур из шести элементов типа IDirect3DTexture9.
| C++ | LPDIRECT3DTEXTURE9 textures[6];
LPCSTR files[] = {
{"bricks.bmp"}, {"colors.bmp"}, {"Lake.bmp"},
{"stone_wall.bmp"}, {"wall.bmp"}, {"fur.jpg"} };
...
for (int i=0; i<6; i++)
D3DXCreateTextureFromFile( device, files[i], textures[i] );
|
| Pascal | var
textures: array [0..5] of IDirect3DTexture9;
files: array [0..5] of AnsiString = (
('bricks.bmp'), ('colors.bmp'), ('Lake.bmp'),
('stone_wall.bmp'), ('wall.bmp'), ('fur.jpg') );
...
for i:=0 to 5 do
D3DXCreateTextureFromFile(device, PChar(files[i]), textures[i]);
|
Теперь если куб у нас храниться в виде 12 независимых треугольных граней, то его вывод может быть осуществлен с помощью следующего кода.
| C++ | for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | for i:=0 to 5 do begin device.SetTexture(0, textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже приведены примеры (кадры анимации) вращения такого куба.

Рассмотрим один из способов построения полупрозрачных трехмерных объектов. Данный метод прозрачности применим только для выпуклых объектов. Идея его заключается в следующем. Вначале выводятся те
| C++ | device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | device.SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); for i:=0 to 5 do begin device.SetTexture(0,textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже показан пример вывода "внутренних" сторон куба.

Включение полупрозрачности с нужными параметрами смешивания цветов.
| C++ | device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR ); device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR ); |
| Pascal | device.SetRenderState(D3DRS_ALPHABLENDENABLE, 1); device.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR); device.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); |
Переключение механизма отсечения граней на противоположный. Теперь необходимо не выводить грани, которые перечислены против часовой стрелки (задние грани). И вызов функции отображения треугольников.
| C++ | device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | device.SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); for i:=0 to 5 do begin device.SetTexture(0,textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже приведен пример вывода полупрозрачного куба, внутри которого размещена трехмерная модель цветка.

Для придания трехмерной сцене еще большей реалистичности можно добавить тени, которые отбрасывают объекты при освещении их некоторым источником света. Алгоритмы затенения в случае точечных источников света схожи с алгоритмами удаления невидимых поверхностей, с единственным лишь отличием. В алгоритмах удаления невидимых граней определяются поверхности, которые можно увидеть из точки положения наблюдателя, а в алгоритмах затенения выделяются поверхности, которые можно "увидеть" из местоположения источника света. Грани, видимые как из точки наблюдения, так и из источника света, не лежат в тени. Те же поверхности, которые видимы из точки наблюдения, но не видимы из источника света, находятся в тени.
Рассмотрим один из простейших и очень быстрых алгоритмов построения теней. Он предназначен для просчета резких теневых участков от полигональных объектов на ровную поверхность (плоскость). Конечно, этот алгоритм очень ограничен в возможностях, но в то же время он прост для реализации. Основная идея метода заключается в том, что для каждого полигона (треугольной грани) сцены, создается еще один полигон, который будет представлять собой тень первого. Все вершины тени будут находиться в пределах плоскости, на которую производится проекция. Алгоритм построения тени будет содержать следующие шаги:
Пусть у нас источник света находится в точке L=(Lx,Ly,Lz), треугольная грань объекта имеет координаты вершин P=(Px,Py,Pz), Q=(Qx,Qy,Qz), R=(Rx,Ry,Rz), а плоскость проецирования (затенения) $$\alpha$$ задается неявным уравнением: Ax+By+Cz+D=0. Требуется определить координаты проекций вершин P,Q,R на плоскость $$\alpha.$$ Обозначим через M=(Mx,My,Mz),N=(Nx,Ny,Nz),O=(Ox,Oy,Oz) - проекции точек P,Q,R соответственно на плоскость $$\alpha.$$ Найдем координаты точки M. Так как точка M лежит на луче, то будет справедливы следующие уравнения$$\left\{ \begin{array}{ccc} Mx=Lx+t(Px-Lx) \\ My=Ly+t(Py-Ly) \\ Mz=Lz+t(Pz-Lz) \end{array} \right,$$
для некоторого параметра t.
С другой стороны точка M лежит в плоскости $$\alpha,$$ поэтому верно уравнение: A*Mx+B*My+C*Mz+D=0.
Подставим выражения для Mx,My,Mz в уравнение плоскости: A*(Lx+t(Px-Lx))+B*(Ly+t(Py-Ly))+C*(Lz+t(Pz-Lz))+D=0. Это линейное уравнение относительно параметра t, решая которое находим, что$$t^{.}=-\frac{A*Lx+B*Ly+C*Lz+D}{A(Px-Lx)+B(Py-Ly)+C(Pz-Lz)}.$$
Если $$t^{.} 0$$, то координаты точки пересечения луча и плоскости вычисляются по следующим формулам:$$\left\{ \begin{array}{ccc} Mx=Lx+t^{.}(Px-Lx) \\ My=Ly+t^{.}(Py-Ly) \\ Mz=Lz+t^{.}(Pz-Lz) \end{array}\right.$$
Аналогичные шаги проделываем для двух других вершин полигона Q и R. Следует заметить, что при вычислении тени на плоскость y=0, параметр $$t^{.}=\frac{Ly}{Ly-Py}$$.
Рассмотренный метод имеет несколько "подводных камней". Тень будет прорисовываться даже в том случае, если источник света находится между полигоном и плоскостью проецирования или же источник света расположен вообще по другую сторону от плоскости. Поэтому такие ситуации необходимо отслеживать и корректно обрабатывать.

Ниже приводится пример отбрасывания тени от трехмерного объекта (птицы) на плоскость, покрытую текстурой. В одном случае "тень" является непрозрачной, а в другом – прозрачной.

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

Разберем построение трехмерной поверхности, представляющей собой функцию двух переменных y=f(x,z). Пусть для простоты у нас область определения функции f(x,z) ограничена единичным квадратом, т.е. 0<=x<=1, 0<=z<=1. Визуализация рассмотренных поверхностей на растровых устройствах может быть осуществлена путем кусочно-линейной аппроксимации треугольными гранями. Такая триангуляционная поверхность может быть получена следующим образом. Параметрическое пространство x,z (единичный квадрат) дискретизируется по координате x с шагом dx, по координате z – с шагом dz. Для каждой пары значений (x,z) вычисляется точка на поверхности y=f(x,z). Полученный таким образом массив точек триангулируется регулярной сеткой.

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


Разберем один из способов построения ландшафтов, который базируется на так называемой карте высот. Карта высот представляет собой двумерный массив, где каждый элемент (пиксель) определяет высоту точки в сетке треугольников. Как правило, значение высоты ранжируется в диапазоне от 0 до 255. Поэтому для хранения подобных карт высот используют изображения в оттенках серого цвета ( grayscale image ). Более светлые участки на таких изображениях соответствуют более высоким значениям точек, а темные участки определяют низкие значения высот. Кроме этого подбирается текстура, схожая по "характеру" с ландшафтом. Ниже приведен пример построения ландшафта по карте высот.
Для построения реалистичного изображения недостаточно только удалить невидимые
При расчете освещенности грани в компьютерной графике учитывают следующие типы отражения света от поверхности: рассеянное, диффузное, зеркальное. Интенсивность освещения
При наличии в сцене точечного источника света, интенсивность диффузного отражения пропорциональна косинусу угла между нормалью к поверхности и направлением на источник света. В этом случае для вычисления интенсивности диффузного отражения применяют закон косинусов Ламберта: $$I=I_{p}k_{d}cos \theta$$, где $$I_{p}$$ - интенсивность точечного источника света, $$k_{d} \in [0,1]$$ - коэффициент диффузного отражения, $$\theta$$ - угол падения, рассчитываемый как угол между направлением на источник света и нормалью к поверхности. Если вектора нормализованы, т.е. |, то $$\cos \theta =(\overrightarrow N \overrightarrow L)$$ - скалярное произведение векторов, и уравнение можно записать в виде $$I_p k_d (\overline N \cdot \overline L)$$.

Свойством диффузного отражения является равномерность по всем направлениям отраженного света. Поэтому такие объекты имеют одинаковую яркость, вне зависимости от угла обзора. К примеру, матовые поверхности обладают таким свойством. Предположим, что у нас имеется два полигона, одинаково ориентированы относительно источника света, но расположенные на разных расстояниях от него. Если вычислить значения интенсивностей освещенности для каждого полигона, по приведенной формуле, то они окажутся одинаковыми. Это означает, что объекты, расположенные на разном расстоянии от источника света, будут освещаться независимо от их местоположения в трехмерной сцене. В этом случае интенсивность света должна быть обратнопропорциональна квадрату расстояния от источника до d - расстояние от центра проекции (наблюдателя) до поверхности, k - некоторая константа, k>=1.
Зеркальное отражение можно получить от любой блестящей поверхности. Осветите ярким светом обычное яблоко, и световой блик возникнет в результате зеркального отражения, а свет, отраженный от остальной части яблока, появится вследствие диффузного отражения. Следует отметить, что в том месте, где находится световой блик, яблоко будет казаться не красным, а, скорее всего – белым, т.е. окрашенным в цвет падающего света. Если изменить положение наблюдателя, то световой блик тоже сместится. Это объясняется тем, что блестящие поверхности отражают свет неодинаково по всем направлениям. От идеально зеркальной поверхности свет отражается только в том направлении, для которого углы падения и отражения совпадают. Зеркально отраженный свет можно будет увидеть, если угол между вектором отражения (на рисунке обозначен R ) и вектором наблюдения (на рисунке обозначен S ) равен нулю.

Для неидеальных отражающих поверхностей интенсивность отраженного света резко падает с ростом $$\alpha.$$ В модели, предложенной Фонгом, быстрое убывание интенсивности света, описывается функцией $$cos^{n}\alpha$$, где $$n \in [1,200]$$ в зависимости от вида поверхности. Для идеальной отражающей поверхности $$n\to\infty$$. Интенсивность зеркального отражения света по модели Фонга записывается как $$I=I_{p}*k_{S}*cos^{n} \alpha$$, где $$I_{p}$$ - интенсивность источника света, $$k_{S} \in [0,1]$$ - коэффициент зеркального отражения. Если вектора и нормированы, то формула может быть преобразована к виду:$$I=I_{pks} (\overline R \cdot \overline S)^n.$$
Интенсивность зеркального света обратно пропорциональна расстоянию от источника до грани. Для практических задач здесь используют также модель линейного затухания
света, которая выражается
следующей формулой:$$I=\frac{I_{pks} (\overline R \cdot \overline S)}{d+k}^n,$$
где d - расстояние от наблюдателя до поверхности, k - некоторая константа.
Таким образом, интенсивность грани складывается из трех составляющих: рассеянного света, диффузного света и зеркального света. Формула расчета общей интенсивности с учетом расстояния от наблюдателя до освещенной грани и трех составляющих света записывается как$$I=I_ak_a+\frac{I_p}{d+k}(k_d (\overline L \cdot \overline N)+k_s (\overline R \cdot \overline S)^n).$$
Для более реалистичного отображения объектов сцены в библиотеке определены понятия материал и свет. Материал в задает свойства поверхности отображаемых примитивов. С помощью материала определяется как будет отражаться от поверхности примитива свет. Материал и свет (в контексте ) используются совместно. Одно без другого работать не будет.
Для работы с материалом предусмотрен D3DMATERIAL9, который содержит следующие поля.
| C++ | typedef struct _D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; {рассеянный свет, исходящий от материала}
D3DCOLORVALUE Ambient; {определяет окружающий свет}
D3DCOLORVALUE Specular; {определяет отражающий (зеркальный) свет}
D3DCOLORVALUE Emissive; {определяет излучающий свет материала}
float Power; {мощность отражения}
} D3DMATERIAL9;
|
| Pascal | TD3DMaterial9 = packed record
Diffuse: TD3DColorValue; {рассеянный свет, исходящий от материала}
Ambient: TD3DColorValue; {определяет окружающий свет}
Specular: TD3DColorValue; {определяет отражающий (зеркальный) свет}
Emissive: TD3DColorValue; {определяет излучающий свет материала}
Power: Single; {мощность отражения}
end;
|
Задавая параметры структуры D3DMATERIAL9, тем самым мы определяем свойства поверхности отображаемого объекта. Процесс создания и использование (назначение) материала включает в себя следующие шаги:
D3DMATERIAL9 ;Программно эти шаги реализуются таким образом:
| C++ | D3DMATERIAL9 material; ZeroMemory( material, sizeof(D3DMATERIAL9) ); material.Diffuse=D3DXCOLOR(0.7f, 0.0f, 0.0f, 0.0f); material.Ambient=D3DXCOLOR(0.2f, 0.0f, 0.0f, 0.0f); material.Specular=D3DXCOLOR(0.1f, 0.0f, 0.0f, 0.0f); ... device->SetMaterial( material ); |
| Pascal | var material: TD3DMaterial9; ... ZeroMemory(@material,SizeOf(TD3DMaterial9)); material.Diffuse:=D3DXColor(0.7, 0, 0, 0); // r, g, b, a material.Ambient:=D3DXColor(0.2, 0, 0, 0); material.Specular:=D3DXColor(0.1, 0, 0, 0); ... device.SetMaterial(material); |
Метод SetMaterial вызывается при выводе объектов (в процедуре рендеринга). Кроме определения свойств материала необходимо задать источники света. Библиотека поддерживает три типа источника света:



Свет в состоит из трех составляющих: рассеянного, окружающего, зеркального, которые независимо друг от друга принимают участие в вычислении освещения граней. Они напрямую взаимодействуют с тремя цветами, определенными в свойствах материала. Результат такого сочетания (взаимодействия) и есть конечный цвет освещения сцены. В сценах могут присутствовать до восьми различных по свойствам источников света. Для инициализации источника света в необходимо проделать следующие шаги:
D3DLIGHT9 ;Структура D3DLIGHT9 содержит следующие поля:
| C++ | typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type; {тип источника света}
D3DCOLORVALUE Diffuse; {рассеянный свет, излучающий источником}
D3DCOLORVALUE Specular; {зеркальный свет, излучающий источником}
D3DCOLORVALUE Ambient; {окружающий свет, излучающий источником}
D3DVECTOR Position; {положение источника в сцене} для точечного
D3DVECTOR Direction; {направление падающего света (вектор)}
float Range; {максимальное расстояние освещения} для точечного
float Falloff; {используется в прожекторном источнике}
float Attenuation0; {определяют закон изменения освещения}
float Attenuation1; {от параметра расстояния}
float Attenuation2; {1/(A0+A1*D+A2*D^2),D–расстояние от источника}
float Theta; {внутренний угол источника света}
float Phi; {внешний угол, [0…pi]}
} D3DLIGHT9;
|
| Pascal | TD3DLight9 = packed record
_Type: TD3DLightType; {тип источника света}
Diffuse: TD3DColorValue; {рассеянный свет, излучающий источником}
Specular: TD3DColorValue; {зеркальный свет, излучающий источником}
Ambient: TD3DColorValue; {окружающий свет, излучающий источником}
Position: TD3DVector; {положение источника в сцене} для точечного
Direction: TD3DVector; {направление падающего света (вектор)}
Range: Single; {максимальное расстояние освещения} для точечного
Falloff: Single; {используется в прожекторном источнике}
Attenuation0: Single; {определяют закон изменения освещения}
Attenuation1: Single; {от параметра расстояния}
Attenuation2: Single; {1/(A0+A1*D+A2*D^2),D–расстояние от источника}
Theta: Single; {внутренний угол источника света}
Phi: Single; {внешний угол, [0…pi]}
end;
|
Тип источника задается указанием одной из трех констант:
D3DLIGHT_POINT, // точечный источник D3DLIGHT_SPOT, // источник-прожектор D3DLIGHT_DIRECTIONAL. // направленный источник
Ниже приведен пример установки направленного источника освещения в сцене.
| C++ | // создание и заполнение полей структуры, установка типа источника D3DLIGHT9 light; ZeroMemory(light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Direction = D3DXVECTOR3(0.0f, 0.0f,1.0f); ... // разрешаем работы с освещенностью device->SetRenderState( D3DRS_LIGHTING, TRUE ); ... // установка и включение первого (и единственного) источника света device->SetLight( 0, light ); device->LightEnable( 0, TRUE ); |
| Pascal | var light: TD3DLight9; ... // создание и заполнение полей структуры, установка типа источника ZeroMemory(@light,SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_DIRECTIONAL; light.Diffuse:=D3DXColor(1,0,0,0); light.Direction:=D3DXVector3(0,0,1); // разрешаем работы с освещенностью device.SetRenderState(D3DRS_LIGHTING, 1); ... // установка и включение первого (и единственного) источника света device.SetLight(0,light); device.LightEnable(0,true); |
Но одних материалов и источников света недостаточно для того, чтобы объекты сцены были освещены. При использовании освещения необходимо определить нормаль к каждой грани трехмерного примитива. Нормаль представляет собой вектор, расположенный перпендикулярно одной их сторон выводимого примитива.

Именно с помощью нормалей рассчитывается освещенность объекта (граней). Как правило, нормали задаются в каждой вершине примитива.

Поэтому необходимо корректно изменить формат вершин и набор FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
FLOAT nx, ny, nz;
};
VERTEX3D data[];
#define MY_FVF (D3DFVF_XYZ | D3DFVF_NORMAL);
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
nx, ny, nz: Single;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_NORMAL;
var
data: array of Vertex3D;
|
Следует заметить, что для правильной освещенности располагает функцией D3DXVec3Normalize(), которая позволяет привести вектор к единичной длине.
| C++ | D3DXVECTOR3 direction = D3DXVECTOR3(0.0f, 0.0f,1.0f); D3DXVec3Normalize( (D3DXVECTOR3*)light.Direction, direction ); |
| Pascal | D3DXVec3Normalize( light.Direction, D3DXVector3(0, 0, 1) ); |
Кроме того, чтобы все нормали вершин после серии преобразований автоматически имели длину единица, можно установить переменную состояния D3DRS_NORMALIZENORMALS в значение "истина".
| C++ | device->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE ); |
| Pascal | device.SetRenderState(D3DRS_NORMALIZENORMALS, 1 ); |
Для того, чтобы источник в сцене был активирован нужно проделать следующие шаги. Сначала нужно вызвать процедуру установки (регистрации) нужного источника света. Это реализуется через вызов метода SetLight() интерфейса IDirect3DDevice9. Данный метод имеет два параметра: номер источника (лампы), и второй – указатель на переменную типа D3DLIGHT9. Второй шаг состоит в вызове метода LightEnable() интерфейса IDirect3DDevice9 для включения/выключения нужного источника света. Данный метод имеет два параметра: номер источника и второй – булевская
Ниже приведен пример освещения направленным источником света треугольной грани, у которой нормали в каждой вершине одинаковы. При этом положение камеры (наблюдателя) задано точкой (2,2,-2), а направление лучей света – (-2,-2,2).

Как видно в каждом положении грань освещена одинаково (нормали в каждой вершине равны (0, 0, -1) ). Изменив хотя бы одну нормаль вершины, можно добиться того, что грань будет освещаться уже неравномерно. Пусть одна из вершин будет теперь иметь нормаль (1,0,-1). Результат освещенности грани при таких изменениях нормали показан ниже.

Ниже приведены примеры заполнения "нужных" полей для точечного и прожекторного источников света.
Пример для точечного источника света.
| C++ | D3DLIGHT9 light; ZeroMemory( light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_POINT; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Position = D3DXVECTOR3(2.0f, 2.0f, -2.0f); light.Attenuation0 = 1.0f; light.Range = 100; |
| Pascal | var light: TD3DLight9; ... ZeroMemory(@light, SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_POINT; light.Diffuse:=D3DXColor(1,1,0,0); light.Position:=D3DXVector3(2,2,-2); light.Attenuation0:=1; light.Range:=100; |
Пример для прожекторного источника света.
| C++ | D3DLIGHT9 light; ZeroMemory( light, sizeof(D3DLIGHT9) ); light.Type = D3DLIGHT_SPOT; light.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 0.0f); light.Position = D3DXVECTOR3(2.0f, 2.0f, -2.0f); D3DXVECTOR3 direction = D3DXVECTOR3(-2.0f, -2.0f, 2.0f); D3DXVec3Normalize( (D3DXVECTOR3*)light.Direction, direction ); light.Attenuation0 = 1.0f; light.Range = 100; light.Phi = D3DX_PI/2; light.Theta = D3DX_PI /3; light.Falloff = 1.0f; |
| Pascal | var light: TD3DLight9; ... ZeroMemory(@light, SizeOf(TD3DLight9)); light._Type:=D3DLIGHT_SPOT; light.Diffuse:=D3DXColor(1,1,0,0); light.Position:=D3DXVector3(2,2,-2); D3DXVec3Normalize(light.Direction, D3DXVector3(-2,-2,2)); light.Attenuation0:=1; light.Range:=100; light.Phi:=pi/2; light.Theta:=pi/3; light.Falloff:=1; |
Библиотека располагает рядом встроенных функций для построения простых стандартных трехмерных примитивов (куб, цилиндр, сфера, тор):
D3DXCreatePolygon // полигонD3DXCreateBox // параллелограммD3DXCreateCylinder // цилиндрD3DXCreateSphere // сфераD3DXCreateTorus // торD3DXCreateTeapot // чайникПри создании объектов таким способом вершины "получают" формат, в котором присутствует положение (D3DFVF_XYZ) и нормаль (D3DFVF_NORMAL). Существует возможность задавать уровень детализации при создании перечисленных выше трехмерных примитивов. Следует заметить, что нормаль к каждой вершине созданного объекта вычисляется автоматически. Рассмотрим их функции создания и визуализации. Для работы с подобными примитивами необходимо объявить переменную ID3DXMesh, в которой и будет "храниться" трехмерный объект. Визуализация созданного объекта осуществляется вызовом метода DrawSubset интерфейса ID3DXMesh.
Функция создания полигона (правильного многоугольника):
D3DXCreatePolygon( ссылка на устройство вывода, длина стороны полигона, количество сторон полигона, результат, указатель на смежные треугольники);
Ниже приведен пример создания и визуализации трехмерного объекта – полигона.
| C++ | // объявление переменной LPD3DXMESH polygon; // создание объекта полигон D3DXCreatePolygon( device, 0.5, 10, polygon, NULL); // визуализация polygon->DrawSubset(0); |
| Pascal | // объявление переменной var polygon: ID3DXMesh; // создание объекта полигон D3DXCreatePolygon(device, 0.5, 10, polygon, nil); // визуализация polygon.DrawSubset(0); |
Ниже приведены примеры построения полигонов с разными значениями второго и третьего параметра рассмотренной функции.
| Длина стороны = 0.5 Количество сторон = 10 |
Длина стороны = 2 Количество сторон = 3 |
Длина стороны = 0.1 Количество сторон = 36 |
![]() |
![]() |
![]() |
Функция вывода параллелепипеда
D3DXCreateBox( ссылка на устройство вывода, ширина, высота, глубина, результат, указатель на смежные треугольники);
Ниже приведен пример создания и визуализации трехмерного объекта – полигона.
| C++ | // объявление переменной LPD3DXMESH box; // создание объекта D3DXCreateBox( device, 1.0f, 0.5f, 2.0f, box, NULL); // визуализация box->DrawSubset(0); |
| Pascal | // объявление переменной var box: ID3DXMesh; // создание объекта D3DXCreateBox(device, 1, 0.5, 2, box, nil); // визуализация box.DrawSubset(0); |
Примеры вывода параллелограмма с различными длинами сторон
| ширина = 1 высота = 0.5 глубина = 2 |
ширина = 1 высота = 1 глубина = 1 |
ширина = 0.5 высота = 2 глубина = 1 |
![]() |
![]() |
![]() |
Функция построения цилиндра
D3DXCreateCylinder( ссылка на устройство радиус первого основания радиус второго основания высота цилиндра количество разбиений "по радиусу" количество разбиений "по длине" результат указатель на смежные треугольники);

| C++ | LPD3DXMESH cylinder; D3DXCreateCylinder(device, 0.2f, 0.2f, 1, 16, 3, cylinder, NULL); cylinder->DrawSubset(0); |
| Pascal | var cylinder: ID3DXMesh; D3DXCreateCylinder(device, 0.2, 0.2, 1, 16, 3, cylinder, nil); cylinder.DrawSubset(0); |
| Первое основание = 0.2 Второе основание = 0.2 Разбиений по радиусу = 16 |
Первое основание = 0.4 Второе основание = 0.2 Разбиений по радиусу = 16 |
Первое основание = 0.4 Второе основание = 0 Разбиений по радиусу = 6 |
![]() |
![]() |
![]() |
Функция построения сферы:
D3DXCreateSphere( ссылка на устройство, радиус сферы, разбиений "по радиусу", // число апельсиновых долек разбиений "вдоль", // результат, указатель на смежные треугольники);
| Разбиений по радиусу = 6, вдоль = 32 | Разбиений по радиусу = 32, вдоль = 6 |
![]() |
![]() |
| C++ | LPD3DXMESH sphere; D3DXCreateSphere(device, 1.0f, 16, 16, sphere, NULL); sphere->DrawSubset(0); |
| Pascal | var sphere: ID3DXMesh; D3DXCreateSphere(device, 1, 16, 16, sphere, nil); sphere.DrawSubset(0); |
Функция построения тора:
D3DXCreateTorus( ссылка на устройство, внутренний радиус тора, внешний радиус тора, количество разбиений в поперечном сечении, количество разбиений по кругу, результат, указатель на смежные треугольники);
| Разбиений в сечение = 6 Разбиений по кругу = 32 |
Разбиений в сечение = 32 Разбиений по кругу = 6 |
![]() |
![]() |
| C++ | LPD3DXMESH torus; D3DXCreateTorus(device, 0.2f, 0.8f, 32, 6, torus, NULL); torus->DrawSubset(0); |
| Pascal | var torus: ID3DXMesh; D3DXCreateTorus(device, 0.2, 0.8, 32, 6, torus, nil); torus.DrawSubset(0); |
Функция построения чайника:
D3DXCreateTeapot( ссылка на устройство, результат, указатель на смежные треугольники);
Это единственный примитив, для которого отсутствует возможность задать уровень детализации.

| C++ | LPD3DXMESH teapot; D3DXCreateTeapot(device, teapot, NULL); teapot->DrawSubset(0); |
| Pascal | var teapot: ID3DXMesh; D3DXCreateTeapot(device, teapot, nil); teapot.DrawSubset(0); |
Ниже приведены примеры освещения трехмерного объекта точечным источником света с различными характеристиками.
light.Ambient = D3DXCOLOR(0.3f, 0,0,0); |
![]() |
light.Diffuse = D3DXCOLOR(0.5f, 0.0, 0,0); light.Ambient = D3DXCOLOR(0.2f, 0,0,0); |
![]() |
light.Diffuse = D3DXCOLOR(0.5, 0.0, 0,0); light.Ambient = D3DXCOLOR(0.2, 0,0,0); light.Specular = D3DXCOLOR(0.3,0,0.0,0); |
![]() |
Следует заметить, что зеркальное освещение требует больших вычислений, чем другие типы освещения, поэтому библиотека Direct3DM предоставляет возможность отключать его (по умолчанию оно выключено). Для включения зеркального освещения необходимо установить константу состояния D3DRS_SPECULARENABLE в значение "истина".
| C++ | device->SetRenderState( D3DRS_SPECULARENABLE, TRUE ); |
| Pascal | device.SetRenderState(D3DRS_SPECULARENABLE, 1); |
Как мы уже говорили, библиотека
| C++ | D3DMATERIAL9 material; D3DLIGHT9 light0, light1, light2; ZeroMemory( material, sizeof(D3DMATERIAL9) ); material.Diffuse=D3DXCOLOR(1.0f, 1.0f, 1.0f, 0.0f); ZeroMemory( light0, sizeof(D3DLIGHT9) ); light0.Type = D3DLIGHT_POINT; light0.Diffuse = D3DXCOLOR(0.5f, 0.0f, 0.0f, 0.0f); light0.Position = D3DXVECTOR3(4.0f, 0.0f, 0.0f); … ZeroMemory( light1, sizeof(D3DLIGHT9) ); light1.Type = D3DLIGHT_POINT; light1.Diffuse = D3DXCOLOR(0.0f, 0.5f, 0.0f, 0.0f); light1.Position = D3DXVECTOR3(0.0f, 0.0f, -4.0f); … ZeroMemory( light2, sizeof(D3DLIGHT9) ); light2.Type = D3DLIGHT_POINT; light2.Diffuse = D3DXCOLOR(0.0f, 0.0f, 0.5f, 0.0f); light2.Position = D3DXVECTOR3(-4.0f, 0.0f, 0.0f); device->SetRenderState( D3DRS_LIGHTING, TRUE ); ... // процедура рендеринга device->SetLight( 0, light0 ); device->LightEnable( 0, TRUE ); device->SetLight( 1, light1 ); device->LightEnable( 1, TRUE ); device->SetLight( 2, light2 ); device->LightEnable( 2, TRUE ); |
| Pascal | var material: TD3DMaterial9; light0, light1, light2: TD3DLight9; ZeroMemory(@material,SizeOf(TD3DMaterial9)); material.Diffuse:=D3DXColor(1,1,1,0); light0._Type:=D3DLIGHT_POINT; light0.Diffuse:=D3DXColor(0.5, 0.0, 0, 0); light0.Position:=D3DXVector3(4,0,0); … light1._Type:=D3DLIGHT_POINT; light1.Diffuse:=D3DXColor(0.0, 0.5, 0, 0); light1.Position:=D3DXVector3(0,0,-4); … light2._Type:=D3DLIGHT_POINT; light2.Diffuse:=D3DXColor(0.0, 0.0, 0.5, 0); light2.Position:=D3DXVector3(-4,0,0); … device.SetRenderState(D3DRS_LIGHTING, 1); // процедура рендеринга device.SetLight(0,light0); device.LightEnable(0,true); device.SetLight(1,light1); device.LightEnable(1,true); device.SetLight(2,light2); device.LightEnable(2,true); |

Сложные (содержащие десятки тысяч вершин) объекты, как правило, создаются в профессиональных трехмерных редакторах (3D Studio, Maya, содержит средства для загрузки и визуализации моделей, содержащихся в x-файлах. Х-файл – файл, содержащий в себе данные о трехмерном объекте (координаты вершин, материалы, нормали, текстуры и анимацию). Вначале необходимо объявить нужные переменные.
| C++ | LPD3DXMESH mesh; |
| Pascal | var mesh: ID3DXMesh; |
Для загрузки трехмерного объекта (модели) из Х-файла предназначена функция
D3DXLoadMeshFromX( путь до файла на диске, набор флагов, указатель на устройство вывода, информация о смежных треугольниках, данные о материалах модели, данные об эффектах модели, количество материалов в модели, результат ).
В простейшем случае (если модель не содержит информации о материалах, текстурах, смежности граней) параметры с четвертого по седьмой включительно выставляют в NULL. Ниже приведен пример загрузки модели из Х-файла.
| C++ | D3DXLoadMeshFromX("ship.x", D3DXMESH_MANAGED,
device, NULL, NULL, NULL, NULL, mesh);
|
| Pascal | D3DXLoadMeshFromX('ship.x', D3DXMESH_MANAGED,
device, nil, nil, nil, nil, mesh);
|
Для визуализации трехмерной модели из Х-файла необходимо вызвать метод DrawSubset интерфейса ID3DXMesh, где в качестве параметра передается номер выводимой группы (подмножества) модели. Если модель не разбита на группы, то параметр метода равен нулю.
| C++ | mesh->DrawSubset(0); |
| Pascal | mesh.DrawSubset(0); |
Для того чтобы трехмерная модель выглядела более реалистично, можно добавить в сцену источники света и определить материалы.

Принцип текстурирования трехмерных объектов ничем не отличается от двумерного аналога. Для каждой треугольной грани необходимо задать текстурные координаты. Единственное отличие от двумерного варианта заключается в формате вершин и наборе FVF флагов.
| C++ | struct VERTEX3D
{
FLOAT x, y, z;
FLOAT u, v;
};
#define MY_FVF (D3DFVF_XYZ | D3DFVF_TEX1);
...
VERTEX3D points[3] = {
{ -1.0f,-1.0f, 0.0f, 0.0f, 1.0f, },
{ 0.0f, 1.0f, 0.0f, 0.5f, 0.0f, },
{ 1.0f,-1.0f, 0.0f, 1.0f, 1.0f, }
}
|
| Pascal | type
Vertex3D = packed record
x, y, z: Single;
u, v: Single;
end;
const
MY_FVF = D3DFVF_XYZ or D3DFVF_TEX1;
var
points: array [0..2] of Vertex3D = (
(x: -1; y: -1; z: 0; u: 0; v: 1),
(x: 0; y: 1; z: 0; u: 0.5; v: 0),
(x: 1; y: -1; z: 0; u: 1; v: 1)
);
|
Ниже показан пример вывода куба, у которого все грани покрыты различными текстурами. Для этого можно объявить массив для хранения текстур из шести элементов типа IDirect3DTexture9.
| C++ | LPDIRECT3DTEXTURE9 textures[6];
LPCSTR files[] = {
{"bricks.bmp"}, {"colors.bmp"}, {"Lake.bmp"},
{"stone_wall.bmp"}, {"wall.bmp"}, {"fur.jpg"} };
...
for (int i=0; i<6; i++)
D3DXCreateTextureFromFile( device, files[i], textures[i] );
|
| Pascal | var
textures: array [0..5] of IDirect3DTexture9;
files: array [0..5] of AnsiString = (
('bricks.bmp'), ('colors.bmp'), ('Lake.bmp'),
('stone_wall.bmp'), ('wall.bmp'), ('fur.jpg') );
...
for i:=0 to 5 do
D3DXCreateTextureFromFile(device, PChar(files[i]), textures[i]);
|
Теперь если куб у нас храниться в виде 12 независимых треугольных граней, то его вывод может быть осуществлен с помощью следующего кода.
| C++ | for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | for i:=0 to 5 do begin device.SetTexture(0, textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже приведены примеры (кадры анимации) вращения такого куба.

Рассмотрим один из способов построения полупрозрачных трехмерных объектов. Данный метод прозрачности применим только для выпуклых объектов. Идея его заключается в следующем. Вначале выводятся те
| C++ | device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | device.SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); for i:=0 to 5 do begin device.SetTexture(0,textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже показан пример вывода "внутренних" сторон куба.

Включение полупрозрачности с нужными параметрами смешивания цветов.
| C++ | device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR ); device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR ); |
| Pascal | device.SetRenderState(D3DRS_ALPHABLENDENABLE, 1); device.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR); device.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); |
Переключение механизма отсечения граней на противоположный. Теперь необходимо не выводить грани, которые перечислены против часовой стрелки (задние грани). И вызов функции отображения треугольников.
| C++ | device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
for (int i=0; i<6; i++) {
device->SetTexture(0, textures[i]);
device->DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2);
}
|
| Pascal | device.SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); for i:=0 to 5 do begin device.SetTexture(0,textures[i]); device.DrawPrimitive(D3DPT_TRIANGLELIST, i*6, 2); end; |
Ниже приведен пример вывода полупрозрачного куба, внутри которого размещена трехмерная модель цветка.

Для придания трехмерной сцене еще большей реалистичности можно добавить тени, которые отбрасывают объекты при освещении их некоторым источником света. Алгоритмы затенения в случае точечных источников света схожи с алгоритмами удаления невидимых поверхностей, с единственным лишь отличием. В алгоритмах удаления невидимых граней определяются поверхности, которые можно увидеть из точки положения наблюдателя, а в алгоритмах затенения выделяются поверхности, которые можно "увидеть" из местоположения источника света. Грани, видимые как из точки наблюдения, так и из источника света, не лежат в тени. Те же поверхности, которые видимы из точки наблюдения, но не видимы из источника света, находятся в тени.
Рассмотрим один из простейших и очень быстрых алгоритмов построения теней. Он предназначен для просчета резких теневых участков от полигональных объектов на ровную поверхность (плоскость). Конечно, этот алгоритм очень ограничен в возможностях, но в то же время он прост для реализации. Основная идея метода заключается в том, что для каждого полигона (треугольной грани) сцены, создается еще один полигон, который будет представлять собой тень первого. Все вершины тени будут находиться в пределах плоскости, на которую производится проекция. Алгоритм построения тени будет содержать следующие шаги:
Пусть у нас источник света находится в точке L=(Lx,Ly,Lz), треугольная грань объекта имеет координаты вершин P=(Px,Py,Pz), Q=(Qx,Qy,Qz), R=(Rx,Ry,Rz), а плоскость проецирования (затенения) $$\alpha$$ задается неявным уравнением: Ax+By+Cz+D=0. Требуется определить координаты проекций вершин P,Q,R на плоскость $$\alpha.$$ Обозначим через M=(Mx,My,Mz),N=(Nx,Ny,Nz),O=(Ox,Oy,Oz) - проекции точек P,Q,R соответственно на плоскость $$\alpha.$$ Найдем координаты точки M. Так как точка M лежит на луче, то будет справедливы следующие уравнения$$\left\{ \begin{array}{ccc} Mx=Lx+t(Px-Lx) \\ My=Ly+t(Py-Ly) \\ Mz=Lz+t(Pz-Lz) \end{array} \right,$$
для некоторого параметра t.
С другой стороны точка M лежит в плоскости $$\alpha,$$ поэтому верно уравнение: A*Mx+B*My+C*Mz+D=0.
Подставим выражения для Mx,My,Mz в уравнение плоскости: A*(Lx+t(Px-Lx))+B*(Ly+t(Py-Ly))+C*(Lz+t(Pz-Lz))+D=0. Это линейное уравнение относительно параметра t, решая которое находим, что$$t^{.}=-\frac{A*Lx+B*Ly+C*Lz+D}{A(Px-Lx)+B(Py-Ly)+C(Pz-Lz)}.$$
Если $$t^{.} 0$$, то координаты точки пересечения луча и плоскости вычисляются по следующим формулам:$$\left\{ \begin{array}{ccc} Mx=Lx+t^{.}(Px-Lx) \\ My=Ly+t^{.}(Py-Ly) \\ Mz=Lz+t^{.}(Pz-Lz) \end{array}\right.$$
Аналогичные шаги проделываем для двух других вершин полигона Q и R. Следует заметить, что при вычислении тени на плоскость y=0, параметр $$t^{.}=\frac{Ly}{Ly-Py}$$.
Рассмотренный метод имеет несколько "подводных камней". Тень будет прорисовываться даже в том случае, если источник света находится между полигоном и плоскостью проецирования или же источник света расположен вообще по другую сторону от плоскости. Поэтому такие ситуации необходимо отслеживать и корректно обрабатывать.

Ниже приводится пример отбрасывания тени от трехмерного объекта (птицы) на плоскость, покрытую текстурой. В одном случае "тень" является непрозрачной, а в другом – прозрачной.

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

Разберем построение трехмерной поверхности, представляющей собой функцию двух переменных y=f(x,z). Пусть для простоты у нас область определения функции f(x,z) ограничена единичным квадратом, т.е. 0<=x<=1, 0<=z<=1. Визуализация рассмотренных поверхностей на растровых устройствах может быть осуществлена путем кусочно-линейной аппроксимации треугольными гранями. Такая триангуляционная поверхность может быть получена следующим образом. Параметрическое пространство x,z (единичный квадрат) дискретизируется по координате x с шагом dx, по координате z – с шагом dz. Для каждой пары значений (x,z) вычисляется точка на поверхности y=f(x,z). Полученный таким образом массив точек триангулируется регулярной сеткой.

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


Разберем один из способов построения ландшафтов, который базируется на так называемой карте высот. Карта высот представляет собой двумерный массив, где каждый элемент (пиксель) определяет высоту точки в сетке треугольников. Как правило, значение высоты ранжируется в диапазоне от 0 до 255. Поэтому для хранения подобных карт высот используют изображения в оттенках серого цвета ( grayscale image ). Более светлые участки на таких изображениях соответствуют более высоким значениям точек, а темные участки определяют низкие значения высот. Кроме этого подбирается текстура, схожая по "характеру" с ландшафтом. Ниже приведен пример построения ландшафта по карте высот.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.