Освещенность и материалы. Построения объектов
Но одних материалов и источников света недостаточно для того, чтобы объекты сцены были освещены. При использовании освещения необходимо определить нормаль к каждой грани трехмерного примитива. Нормаль представляет собой вектор, расположенный перпендикулярно одной их сторон выводимого примитива.
Именно с помощью нормалей рассчитывается освещенность объекта (граней). Как правило, нормали задаются в каждой вершине примитива.
Поэтому необходимо корректно изменить формат вершин и набор 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; |
Следует заметить, что для правильной освещенности граней объектов все вектора, участвующие в расчете освещенности, должны быть нормированы (длина вектора равна единице). Библиотека Direct3D располагает функцией 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; |