Графика DirectX в Delphi
Шрифт:
Самый главный для нас метод - воспроизведение частиц системы:
function TParticleSystem.Render : HRESULT;
var
i : Integer;
desc : TDDSURFACEDESC2;
hRet : HRESULT;
begin
ZeroMemory (@desc, SizeOf(desc));
desc.dwSize := SizeOf(desc);
hRet := frmDD.FDDSBack.Lock (nil, desc, DDLOCKJSAIT, 0);
if Failed (hRet) then begin Result := hRet;
Exit;
end;
// Очистка экрана
ZeroMemory (desc.IpSurface,
desc.lPitch * ScreenHeight * (ScreenBitDepth div 8));
//
for i := 0 to Size do
if (Particle[i].Energy > 0) then
PWord (Integer(desc.IpSurface) +
trunc (Particle[i].Y) * desc.lPitch +
trunc (Particle[i].X) * (ScreenBitDepth div 8))^ :=
Particle[i].B or (Particle[i].G shl 5) or (Particle[i].R shl 11);
Result := frmDD.FDDSBack.Unlock(nil) ;
end;
При каждой перерисовке экрана отображается текущее состояние системы:
function TfrmDD.UpdateFraine : HRESULT;
var
hRet : HRESULT;
begin
Result := DD_FALSE;
PS.Calculate; // Пересчитываем положения частиц
// Воспроизведение состояния системы
hRet := PS.Render;
if Failed (hRet) then begin
Result := hRet;
Exit;
end;
Time := Time + 1; // Простейший эмулятор таймера
if Time > 15 then begin // Прошел срок существования системы
PS.Init(DEFAULT_SIZE, DEFAULT_POWER); // Вспышка в новом месте
Time := 0;
end;
Result := DD_OK;
end;
Полупрозрачность
Такой прием часто используется в играх. Автоматизации полупрозрачности DirectDraw не предоставляет, все необходимо делать самому разработчику, попикселно накладывая данные источника и приемника.
В общем случае формула вычисления значения цветовых компонентов выглядит так:
Result = Alpha * srcColor + (1 - Alpha) * destColor
Здесь Alpha - коэффициент прозрачности, принимающий вещественное значение в пределах от нуля до единицы; srcColor - цвет источника; destColor - цвет приемника.
Если Alpha равно нулю, то получаем цвет приемника; если Alpha имеет единичное значение, источник совершенно непрозрачен. Если мы имеем дело с образом, двигающимся по поверхности, то под источником подразумеваем образ, а фон считаем приемником.
Формулу можно оптимизировать. Начнем с того, что избавимся от присутствия двух операций умножения. Перестроим уравнение так, чтобы присутствовала лишь одна из них:
Result = Alpha * srcColor + destColor - Alpha * destColor
ИЛИ
Result = Alpha * (srcColor - destColor) + destColor
Коэффициент прозрачности имеет смысл представлять целым, чтобы все вычисления производить только с целыми числами. Считая Alpha целым в интервале 0 - 256, окончательную формулу расчета составляющей запишем так:
Result = (Alpha * (srcColor - destColor)) / 256 + destColor
Все предваряющие слова сказаны, можем перейти к иллюстрации - проекту каталога Ех18, при
работе которого по знакомому фону перемещается полупрозрачный образ насекомого (рис. 3.8).Массив Pict содержит битовую карту растра:
const
imageWidth = 84;
imageHeight = 80;
Alpha = 127; var
Pict : Array [0..imageWidth - 1, 0..imageHeight - 1] of Word;
ColorKey : Word; // Вспомогательный цветовой ключ
Поверхность образа не выводится на экран, а служит только для заполнения массива pict:
function TfrmDD.Prepare : HRESULT;
var
desc : TDDSURFACEDESC2;
i, j : Integer;
hRet : HRESULT; begin
Result := DD_FALSE;
ZeroMemory (@desc, SizeOf(desc) );
desc.dwSize := SizeOf(desc);
hRet := FDDSImage.Lock (nil, desc, DDLOGK_WAIT, 0);
if Failed (hRet) then begin Result := hRet;
Exit;
end;
// Заполнение массива Pict
for i := 0 to imageWidth - 1 do
for j := 0 to imageHeight - 1 do
Pict [i, j] := PWORD (Integer (desc.IpSurface) + j * desc.lPitch + i * (ScreenBitDepth div 8))^;
ColorKey := Pict [0,0]; // Определяемся с цветовым ключом
Result := FDDSImage.Unlock (nil);
end;
Для простоты в качестве цветового ключа возьмем значение самого первого пиксела образа, считая, что цвет его совпадает с цветом фона. Для ускорения работы примера воспользуемся приемом с частичным обновлением экрана:
function TfrmDD.UpdateFrame : HRESULT;
var
X, Y : Integer; wrkRect : TRECT; hRet : HRESULT;
begin
ThisTickCount := GetTickCount;
if ThisTickCount - LastTickCount > 60 then begin X := 288 + trunc (cos(Angle) * 150);
Y := 208 + trunc (sin(Angle) * 150);
// Старая позиция образа
SetRect (wrkRect, X, Y, X + imageWidth, Y + imageHeight);
Angle := Angle + 0.05;
if Angle > 2 * Pi then Angle := Angle -2 * Pi;
// Вывод полупрозрачного образа в задний буфер
hRet := Blend (288 + trunc (cos(Angle) * 150),
208 + trunc (sin(Angle) * 150)); if Failed (hRet) then begin Result := hRet;
Exit;
end;
// Переключаем страницы hRet := FlipPages;
if Failed (hRet) then begin Result := hRet;
Exit;
end;
// Стираем образ в заднем буфере
hRet := FDDSBack.Blt (@wrkrect, FDDSBackGround, SwrkRect,
DDBLT_WAIT, nil); if Failed (hRet) then begin
Result := hRet;
Exit;
end;
LastTickCount := GetTickCount;
end;
Result := DD_OK;
end;
Итак, осталось рассмотреть собственно функцию вывода полупрозрачного образа: