// Подготовка к рисованию стен
with FD3DDevice do begin
// Учитывать освещение
SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
// Проводить интерполяцию текстур
SetTextureStageState(0,D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
SetTextureStageState(0,D3DTSS_MINFILTER, D3DTEXF_LINEAR);
// He учитывать диффузию треугольников окружения
SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
SetRenderState(D3DRS_AMBIENT, $OOOFOFOF);
// Задаем белый материал
SetMaterial(MaterialWhite);
// Направляем потоки на буфер вершин описания мира
SetStreamSource(0, FD3DVB, SizeOf(TNormDiffTextVertex));
SetVertexShader(D3DFVF_NORMDIFFTEXTVERTEX);
// Координаты треугольников заданы в глобальной системе координат
SetTransform(D3DTS_WORLD, IdentityMatrix);
end;
// Цикл вывода треугольников окружения
for i := 0 to NumTriangles - 1 do with FDSDDevice do begin
// Устанавливаем нужную текстуру в соответствии с описанием
SetTexture(0, FD3DTextures[World [i].NumTexture]);
DrawPrimitive(D3DPT_TRIANGLELIST, i * 3, 1); // Вывод треугольника
end;
FD3Ddevice.SetTexture(0, nil); // Текстура больше не используется
end;
Обратите внимание, что в этом примере задано интерполирование текстур, так называемая билинейная фильтрация. Сделано это для того, чтобы при приближении к ящику и стенам не проявлялась блочность текстур, а изображение не становилось бы крупнозернистым.
Учтите, что использование интерполяции существенно снижает работу приложения, поэтому обычно ее включают только при приближении к поверхности, покрытой текстурой. Другой способ достижения мелкой зернистости - использование чередующихся текстур. В зависимости от расстояния до объекта на него накладываются текстуры различной детализации.
Также я должен напомнить, что для оптимизации работы приложения следует применять запомненные блоки состояний.
Три человечка, присутствующие на сцене, перемещаются по различным законам. Первый, одетый в красную футболку, беспрерывно кружит вокруг центра комнаты:
procedure TfrmD3D.MoveManl;
begin
// Поворот вокруг вертикальной оси
SetRotateYMatrix (rotManl, Angle + Pi);
// Перемещение по кругу
transManl._41 := cos (-Angle) / 2;
transManl._43 := sin(-Angle) / 2;
// Опорная трансформация первого человечка
matManl := MatrixMul(transManl, MatrixMul(rotManl, matWrkl));
Второй человечек пересекает комнату, появляясь из одной стены и исчезая в противоположной:
procedure TfrmD3D.MoveMan2;
begin
// Изменение Х-координаты
transMan2._41 := transMan2._41 - 0.01;
// При прохождении комнаты процесс начинается сначала
if transMan2._41 < -3.1 then transMan2._41 := 3.1;
matMan2 := MatrixMul(transMan2, matWrk2);
Третий человечек назойливо преследует игрока, перемещается в направлении к наблюдателю, всегда разворачиваясь к нему лицом:
procedure TfrmD3D.MoveMan3;
var
wrkAngle : Single;
distX, distZ : Single;
begin
// Расстояния до игрока
distX := XPos - МапЗРозХ;
distZ := ZPos - ManSPosZ;
// Вычисляем угол поворота человечка
if distZ < 0
then wrkAngle := arctan (distX / distZ) - Pi / 2 else
wrkAngle := arctan (distX / distZ) + Pi / 2; // Разворот человечка лицом к игроку
SetRotateYMatrix (rotMan3, wrkAngle);
// Если человечек удален от зрителя, то двигается,в его направлении
if (abs(distX) > 0.02) and (abs (distZ) > 0.02) then begin
МапЗРозХ := МаnЗРозХ + distX / 20; // Новое положение человечка
Man3PosZ := Man3PosZ + distZ / 20;
transMan3._41 := МаnЗРозХ;
transMan3._43 := Man3PosZ;
end;
// Опорная матрица третьего человечка
matMan3 := MatrixMul(transManS, MatrixMul(rotMan3, matWrk2));
Для упрощения вычислений я позволяю человечкам проходить сквозь препятствия и друг через друга. Код предотвращения этих ситуаций очень прост, и вы можете самостоятельно дополнить его отслеживанием таких ситуаций.
Для вывода значения FPS треугольники символов цифр и точки объединены мною в один файл numbers.txt. Процедура piaceLetter определяет в потоке положение и количество треугольников для нужного символа:
procedure TfrmD3D.DrawLetters;
var
i : Integer; nS, nW : Integer;
begin
with FDSDDevice do begin
// Некоторые треугольники построены против часовой
SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
//Не тратить время на освещение
SetRenderState(D3DRS_LIGHTING, DWORD (False));
// Направляем поток на буфер символов
SetStreamSource(0, FD3DVBLetter, SizeOf(TNormVertex));
SetVertexShader(D3DFVF_NORMVERTEX);
end;
// Цикл вывода в пространстве символов FPS for i := 1 to Length(FpsOut) do begin
// Получаем положение треугольников символа
PiaceLetter (FpsOut[i], nS, nW);
// Сдвигаемся в пространстве для вывода очередного символа
LetTrans._41 := i * 0.1;
FD3DDevice.SetTransform(D3DTS_WORLD, LetTrans);
FD3DDevice.DrawPrimitive(D3DPTJTRIANGLELIST, nS, nW);
end;
// Возвращаем обычные установки with FD3DDevice do begin
SetRenderState(D3DRS_COLLMODE, D3DCULL_CCW);
SetRenderState(D3DRS_LIGHTING, DWORD (True) ) ;
end;
end;
Символы выводятся "подвешенными" в воздухе, что выглядит красиво и загадочно.