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. Теперь будет выводиться только одна сторона куба, отсечение задней стороны примитивов не производится. Обратите внимание, что когда квадрат поворачивается к зрителю задней стороной, он выводится черным, т. е. совершенно не окрашиваемым.
Кубик в нашем примере вращается вокруг двух осей одновременно:
SetRotateXMatrix(matRotateX, Angle);
SetRotateYMatrix(matRotateY, Angle);
FD3DDevice.SetTransform(D3DTS_WORLD, MatrixMul(matRotateX, matRotateY));
FD3DDevice.DrawPrimitive(D3DPT__TRIANGLELIST, 0, 12);
На рисунке куб получился крупнее, чем при работе приложения. Для того, чтобы увеличить изображение, можно просто "приблизить" глаз наблюдателя:
SetViewMatrixfmatView, D3DVector(0, 0, -2),
D3DVector(0, 0, 0), D3DVector(0, I, 0));
Есть и другой способ: действительно увеличить объект. Для этого в матрицу трансформаций надо добавить матрицу масштабирования, по главной диагонали которой стоят числа, отличные от единицы и равные масштабным множителям по трем осям отдельно. Попробуйте сейчас увеличить кубик в два раза:
procedure TfrmD3D.DrawScene;
var
matView, matProj : TD3DMatrix;
matRotateX, matRotateY : TD3DMatrix;
niatScale : TD3DMatrix; // Добавилась матрица масштабирования
begin
SetRotateXMatrix(matRotateX, Angle);
SetRotateYMatrix(matRotateY, Angle);
SetScaleMatrix(matScale, 2.0, 2.0, 2.0); // Увеличиваем в 2 раза
// Добавляем матрицу масштабирования
FD3DDevice.SetTransform(D3DTS_WORLD, MatrixMul(matScale,
MatrixMul(matRotateX, matRotateY)));
Обязательно это сделайте, чтобы увидеть, что куб действительно увеличился. Однако освещение его тоже изменилось. Связано это с тем, что векторы нормалей к вершинам вслед за масштабированием стали увеличенными, и требуется их нормализация. В таких случаях необходимо включить режим автоматической нормализации этих векторов:
SetRenderState(D3DRS NORMALIZENORMALS, DWORD (True));
Буфер глубины