Формат вершин теперь помимо пространственных координат обязан включать вектор нормали (тройку вещественных чисел), а FVF-флаг должен дополниться константой D3DFVF_NORMAL. ЭТО первое новшество в модуле нашего следующего примера, проекта каталога Ех02, где рисуется красивый желтый кубик (рис. 9.3).
Итак, запись описания вершины дополнилась тремя полями:
type
TCUSTOMVERTEX = packed record
X, Y, Z : Single;
nX, nY, nZ : Single; // Вектор нормали end;
const
D3DEVF_CUSTOMVERTEX = D3DFVF_XYZ or D3DFVF_NORMAL;
Буфер содержит 36 вершин, предназначенных для построения куба. Они образуют 12 независимых треугольников, по 2 соприкасающихся треугольника на каждую сторону куба. Все треугольники описываются по часовой стрелке, чтобы при воспроизведении мы могли, для экономии времени, отключить воспроизведение примитивов, перечисляемых в поле зрения против часовой стрелки. То есть стороны куба, повернутые к нам задней стороной, воспроизводить не будем, это обычный прием, применяемый к замкнутым трехмерным фигурам. Нормали для всех вершин, относящихся к одной стороне, задаются одинаковыми, исходя из того, какая сторона куба описывается. Так выглядит описание первого треугольника:
Vertices.X := -0.5;
Vertices.Y := -0.5;
Vertices.Z := -0.5;
Vertices.nX := -1.0;
Inc(Vertices);
При инициализации графической системы вызывается процедура, задающая свойства материала и включающая источник света:
procedure TfrmD3D.SetupLights;
var
Material : TD3DMaterial8;
Light : TD3DLight8;
begin
// Инициализация материала, желтый цвет
Material := InitMaterial(1, 1, 0, 0) ;
// Устанавливаем материал в объекте устройства
FD3DDevice.SetMaterial(Material);
// Инициализация направленного источника, белый свет
Light := InitDirectionalLight(DSDVector(0, 0, 1), 1, 1, 1, 0) ;
// Устанавливаем источник света
FDSDDevice.SetLight(0, Light);
// Включаем источник света
FD3DDevice.LightEnable(0, True);
end;
Материал и источник света являются записями (не СОМ-объекты) и имеют тип TD3DMateriais и TD3DLight8 соответственно. Пользовательская функция InitMaterial заполняет поля структуры материала и получает в качестве аргументов значения ARGB. Отличает эти параметры от привычного их использования, помимо порядка, в котором они перечисляются, то, что это вещественные числа, единица соответствует максимальному значению аргумента.
В примере материал задается желтым, для того, чтобы установить его. При этом используется метод SetMaterial объекта устройства.
Функция InitDirectionalLight заполняет поля структуры, описывающей направленный источник света. Первым аргументом передается вектор, задающий направление лучей света. Напоминаю, что мы наблюдаем сцену с отрицательной стороны оси Z. Чтобы лучи света были параллельны нашему взору, вектор направления задается (0, 0, 1). Следующие три аргумента описывают цветовой фильтр, накладываемый на источник света, обычно источник задается белым. Эти числа также вещественны. Значение последнего аргумента для направленного источника безразлично.
Метод setLight объекта устройства устанавливает источник света на сцене. Первый аргумент, целое число, основанное на нуле, является индексом, идентификатором источника света. Метод только задает источник света, включается же он с помощью отдельного метода, LightEnabie, первый аргумент которого - индекс нужного источника, второй аргумент - булево выражение.
Как я уже говорил, отключается воспроизведение задних сторон треугольников, т. е. тех, чьи вершины перечисляются против часовой стрелки:
SetRenderState(D3DRS__CULLMODE, D3DCULL_CCW);
Совсем не обязательно, чтобы вершины примитива перечислялись именно по часовой стрелке, можно использовать и противоположное направление. Просто желательно, чтобы существовал какой-нибудь определенный порядок перечисления, чтобы можно было отсекать воспроизведение задних сторон. Повторюсь, внутренние стороны кубика нам не видны в любом случае, поэтому и незачем тратить время на их воспроизведение. Также обращаю ваше внимание на то, что связанные треугольники приспособлены для перечисления вершин именно по часовой стрелке.
Есть и еще один важный аспект, который нам необходимо учитывать: DirectSD не может окрашивать примитивы с двух сторон. Замените последний аргумент метода Drawprimitive на 2 и установите значение для режима D3DRs_CULLMODE в D3DCULL_NONE. Теперь будет выводиться только одна сторона куба, отсечение задней стороны примитивов не производится. Обратите внимание, что когда квадрат поворачивается к зрителю задней стороной, он выводится черным, т. е. совершенно не окрашиваемым.
Читать дальше
Конец ознакомительного отрывка
Купить книгу