Михаил Краснов - Графика DirectX в Delphi
XPos := NewXPos; ZPos := NewZPos;
end;
end;
// Обработка клавиатуры
Result := DIKeyboard.GetDevicestate(SizeOf(KeyBuffer), @KeyBuffer);
if KeyBuffer[DIK_ESCAPE] and $80 <> 0 then begin // Esc
Close;
Exit;
end;
// Нажата клавиша "вправо", вычисляем новое положение в пространстве
if KeyBuffer[DIK_RIGHT] and $80 <> 0 then begin
XPos := XPos - sin(RotY) * 0.05;
ZPos := ZPos - cos(RotY) * 0.05;
end;
// Нажата клавиша "влево"
if KeyBuffer[DIK_LEFT] and $80 <> 0 then begin
XPos := XPos + sin(RotY) * 0.05;
ZPos := ZPos + cos(RotY) * 0.05;
end;
// Нажата клавиша "вниз"
if KeyBuffer[DIK_DOWN] and $80 о 0 then begin
XPos := XPos + sin(RotY - Pi / 2) * 0.05;
ZPos := ZPos + cos(RotY - Pi / 2) * 0.05;
end;
// Нажата клавиша "вверх" if KeyBuffer[DIK_UP] and $80 <> 0 then begin
XPos := XPos + sin(RotY + Pi / 2) * 0.05;
ZPos := ZPos + cos(RotY + Pi / 2) * 0.05;
end;
// Нажата клавиша "F", показывать ли значение FPS
if KeyBuffer[DIK_F] and $80 <> 0 then begin
flgFPS := not flgFPS; // Обращение значения флага
Sleep (50); // Маленькая пауза
end;
// Клавиша <Page Up>, голову задираем вверх
if KeyBuffer[DIK_PRIOR] and $80 <> 0 then begin
Lookupdown := Lookupdown + 0.05;
if Lookupdown > 1 then Lookupdown := 1;
end;
// Клавиша <Page Down>, голову опускаем вниз
if KeyBuffer[DIK_NEXT] and $80 <> 0 then begin
Lookupdown := Lookupdown - 0.05;
if Lookupdown < -1 then Lookupdown := -1;
end;
end;
Обратите внимание, что при перемещении с помощью мыши осуществляется проверка, нет ли на пути движения препятствия, стены комнаты или ящика. При нажатии клавиш такую проверку не произвожу, и игрок свободно проходит через все препятствия. Опускаю я проверку, чтобы определить, сильно ли она замедляет работу программы.
Для проверки того, свободен ли путь, я применяю самый простой метод: в заднем буфере сцена воспроизводится в новой точке, взгляд наблюдателя при этом повернут в направлении движения. Глаз наблюдателя опускаем ближе к полу, и выясняем цвет точки, расположенной прямо по ходу движения. Поскольку пол окрашивается красным, а препятствия и фон - синим, то синий цвет контрольной точки означает, что игрок вплотную подошел к непреодолимому препятствию или выходит за границу сектора:
function TfrmD3D.TestRender (const XPos, ZPos, RotY : Single) : BOOL;
var
i : Integer; matView : TD3DMatrix; d3dlr : TD3DLOCKED_RECT;
dwDstPitch : DWORD; DWColor : DWORD;
В : Byte; // Доля синего пиксела контрольной точки
begin
В := 0; // Предотвращение замечаний компилятора
// Смотрим на сцену из новой точки, по вертикали - ближе к полу
SetViewMatrix(matView, D3DVector(XPos, 0,1, ZPos),
D3DVector(XPos + cos(RotY), 0.1,
ZPos -sin(RotY)), ZVector); // Упрощенное воспроизведение сцены
with FD3DDevice do begin
Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER,
$000000FF, 1.0, 0); BeginScene;
// Отключаем источники света
-SetRenderState(D3DRS_LIGHTING, DWORD (False)); // Использовать диффузный компонент описания вершин SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
SetTransform(D3DTS_VIEW, matView);
SetStreamSource(0, FD3DVB, SizeOf(TNormDiffTextVertex));
SetVertexShader(D3DFVF_NORMDIFFTEXTVERTEX); SetTransform(D3DTS_WORLD, IdentityMatrix);
end;
// Рисуем только комнату
for i := 0 to NumTriangles - 1 do with FD3DDevice do
DrawPrimitive(D3DPT_TRIANGLELIST, 1*3, 1);
with FD3DDevice do begin
EndScene;
// Получаем доступ к заднему буферу
GetBackBuffer (О, D3DBACKBUFFER_TYPE_MONO, FD3SurfBack);
SetRenderState(D3DRS_LIGHTING, DWORD (True));
end;
// Запираем задний буфер
FD3SurfBack.LockRect (d3dlr, nil, D3DLOCK_READONLY);
dwDstPitch := d3dlr.Pitch;
// Определяем долю синего в контрольной точке case
FD3DfmtFullscreen of D3DFMT_X8R8G8B8 : begin
DWColor := PDWORD (DWORD(d3dlr.pBits) + TestPointY * dwDstPitch +
TestPointX * 4)^; В := DWColor and $lf;
end;
D3DFMT_R5G6B5 : begin
DWColor := PDWORD (DWORD(d3dlr.pBits) + TestPointY * dwDstPitch +
TestPointX * 2}Л; В := DWColor and $lf;
end;
end;
FDSSurfBack.UnLockRect;
// Нет синего цвета, значит можно пройти
Result := not (В <> 0);
end;
Синий цвет взят мною в качестве контрольного, поскольку для его вырезки требуется минимум операций. Замечу, что код этой функции можно дополнительно оптимизировать, например, вполне можно обойтись без использования промежуточной переменной DWColor.
Если установлен 24-битный режим, соответствующий формату D3DFMT_R8G8B8, то Х-координату контрольной точки надо умножить на 3, именно столько байт отводится для одного пиксела в этом режиме.
Контрольная точка для определения столкновения с препятствиями берется одна - посередине экрана по горизонтали, на 10 пикселов выше нижней границы экрана:
ScreenWidth := GetSystemMetrics(SM_CXSCREEN);
ScreenHeight := GetSystemMetrics(SM_CYSCREEN);
TestPointX := ScreenWidth div 2;
TestPointY := DWORD{ScreenHeight - 10);
Используемый здесь алгоритм - самый простой, но, конечно, не самый совершенный. На его основе мы можем построить и более быстрые алгоритмы. Например, можно определенным цветом прорисовать только области, доступные для нахождения, и тогда достаточно легко определять цвет точки "под ногами". В этом случае описание мира усложняется, поскольку он описывается дважды, и требуется отдельный буфер. Вознаграждением будет то, что перемещения игрока при использовании такого алгоритма будет легко сделать пространственными, различая по оттенкам высоту препятствий. Поскольку положение игрока в пространстве является дискретным, можно воспользоваться массивом, значения элементов которого содержат данные о возможности нахождения игрока в определенной точке пространства, сведения о включении для этой точки фильтрации текстур, а также данные о занимаемой высоте.
Пример упрощен во многих отношениях, и, конечно, далек по своему качеству от профессиональных творений. Мастера должны с большой иронией смотреть на наши первые шаги в захватывающем мире программирования трехмерных игр, но ведь нам, например, не требуется кулинарного образования для того, чтобы приготовить себе обед, ведь так? И то, что профессиональным кулинарам может не понравиться наше кушанье, не означает, что мы должны оставаться голодными. На этом простом примере мы должны убедиться, что можем написать игру в принципе, дальше же нам предстоит совершенствоваться. Но эта тема иной книги, а данная книга на этих словах заканчивается.
Что вы узнали в этой главе
Direct3D располагает массой средств, позволяющих добиться высококачественных изображений, и в настоящей главе рассмотрена только небольшая их доля, например туман и источник света.
В заключительной главе мы познакомились с важными примерами, иллюстрирующими использование текстуры в пространственных построениях.
Закончилась глава примером простого движка трехмерной игры.
Заключение
Надеюсь, книга принесла вам пользу и удовольствие, но, возможно, у вас появились и замечания, которыми обязательно поделитесь со мной: [email protected]
С данной книгой вы начали знакомство с DirectX, но это только вводное руководство, которое охватывает лишь небольшую толику огромной темы. В ней, например, нет ни слова о библиотеке Direct3DX, а это очень важная тема. Отказался я от ее рассмотрения постольку, поскольку для программ, написанных на Delphi, доступна она лишь в виде дополнительных и нестандартных файлов. Тем не менее после прочтения книги вам легко будет разобраться с этой библиотекой, существенно упрощающей многие действия, такие, например, как загрузка текстур.
Книга закончилась, но тема ее не закрыта вместе с ней, и, наверняка, вы пожелаете узнать нечто большее. Я хочу указать вам места, где вы найдете еще массу информации. Помимо адресов, указанных во введении, приведу еще несколько полезных ссылок.
ПРИЛОЖЕНИЕ 1
Глоссарий
2D Graphics
Двумерная графика. Действие в такой графике происходит на одной плоскости.
3D Graphics
Трехмерная графика. Графическое отображение на дисплее трехмерной сцены.
Alpha
Альфа. Коэффициент прозрачности. В описание цвета может входить специальная составляющая, называемая альфа-каналом.
Alpha blending
Альфа-смешение. Смешивание значений цветов исходного и результирующего пикселов для достижения эффекта прозрачности и просвечивания.
Alpha channel
Альфа-канал. Массив значений, определяющих способ объединения пикселов изображения-источника с изображением-приемником. Альфа-буфер может использоваться для реализации прозрачности, размытия границ и создания тумана.
Ambient
Окружающая среда. Источник света, который светит одинаково во всех направлениях, все объекты освещаются с равной интенсивностью.
API (Application Programming Interface)
Интерфейс прикладного программирования. Спецификация набора функций, которой должны придерживаться разработчики программного обеспечения для совместимости своих программ с соответствующей операционной системой.
Back buffer
Вторичный буфер. Видеобуфер для подготовки следующего кадра анимационной последовательности, в которой первоначально осуществляется воспроизведение. Готовый вторичный буфер заменяет первичный (Front buffer) и, таким образом, выводится на экран.
BitBLT (Bit BLock Transfer)
Графическая операция, при которой прямоугольная область пикселов копируется между различными участками памяти с учетом требований графической памяти.
Bitmap
Прямоугольный битовый массив. Чаще всего применяется для хранения образа растра.