Графика DirectX в Delphi - Михаил Краснов
Шрифт:
Интервал:
Закладка:
Vertices.X =0.5-1/10-1- Radius * Wl [i] ;
Vertices.Y = 0.5 + Radius * Wl [i];
Vertices.Z = 0;
Vertices.U =1-1/10;
Vertices.V = 1.0;
Inc(Vertices) ;
Vertices.X = 0.5 - (i - 1) / 10 + Radius * Wl [i];
Vertices.Y = 0.5 + Radius * Wl [i] ;
Vertices.Z = 0;
Vertices.U = 1 - (i - 1) /10;
Vertices.V = 1.0;
Inc(Vertices);
end;
В программе предусмотрен режим пошагового разрушения, а по нажатии клавиши <Enter> картинка собирается заново:
procedure TfrmD3D.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
i : Integer;
begin
if Key = VK_ESCAPE then Close else
// Пошаговое разрушение
if Key = VK_INSERT then Radius := Radius +0.05 else
// Пошаговое движение в обратном направлении
if Key = VK_DELETE then Radius := Radius - 0.05 else
// Пробел - быстрое разрушение
if Key = VK_SPACE then Moving := True else
// Ввод - картинка собирается заново
if Key = VK_RETURN then begin
Moving := False; // Прекратить движение
Radius := 0; // Картинка собирается
CenterX := random -0.5; // Координаты точки разлома
CenterY := random - 0.5;
for i := 1 to 10 do begin // Коэффициенты скорости движения
repeat // треугольников, все ненулевые
Wl [i] := random- 0.5; until Wl [i] о 0.0;
repeat
W2 [i] := random- 0.5; until W2 [i] <> 0.0;
repeat
W3 [i] := random - 0.5; until W3 [i] <> 0.0;
repeat
W4 [i] := random - 0.5; until W4 [i] <> 0.0;
end;
end;
end;
Немного повозившись, вы можете добиться разрушения стены по отдельным кирпичикам или другим способам разлома.
У вершин треугольников текстурные координаты могут совпадать. С помощью такого трюка можно добиться интересных эффектов, например, как в проекте каталога Ех10, где исходный растр выводится мозаично (рис. 8.8).
В массиве некоторого предопределенного размера хранятся точки, разбросанные в пределах области вывода.
tуре
TXY = packed record // Координаты точки на плоскости
X, У : Single;
end;
const
SIDES = 20; // Уровень детализации круга
К, SIZE = 5500; // Количество точек
var
points : Array [O..SIZE-1] of TXY; // Массив точек
Radius : Single = 0.03; // Размер отдельной точки
Массив заполняется в начале работы значениями из интервала [-1.0; 1.0]:
procedure TfrmD3D.FormCreate(Sender: TObject) ;
var
hRet : HRESULT;
i : Integer;
begin
Randomize;
for i := 0 to SIZE - 1 do begin // Заполнение массива точек
Points[i].X := random * 2 - 1.0;
Points[i].Y := random * 2 - 1.0;
end;
hRet := InitDSD;
if Failed (hRet) then ErrorOut ('InitD3D', hRet);
hRet := InitVB; // Буферы вершин под (SIDES + 1) вершину
if Failed (hRet) then ErrorOut ('InitVB', hRet);
hRet := InitTexture ('../Mandrill.bmp');
if Failed (hRet) then ErrorOut ('InitTexture1, hRet);
end;
При рисовании отдельного мазка текстурные координаты всех вершин одинаковы и связаны с координатами точки:
function TfrmD3D.DrawCircle (const inX, inY : Single) : HRESULT;
const
Step = 2 * Pi / SIDES;
var
Vertices : ATCustomVertex; hRet : HRESULT;
i : Integer; begin
hRet := FD3DVB.Lock(0, (SIDES + 1) * SizeOf(TCustornVertex),
PByte(Vertices), 0) ;
if Failed(hRet) then begin
Result := hRet;
Exit;
end;
// Первая точка, точка центра мазка
Vertices.X := inX;
Vertices.Y := inY;
Vertices.Z := 0.0;
Vertices.U := (inX +1.0) / 2;
Vertices.V := (inY + 1.0) / 2;
Inc(Vertices);
// Точки, лежащие на краю круга
for i := 0 to SIDES do begin
Vertices.X := inX + sin(i * Step) * Radius; // По часовой стрелке
Vertices.Y := inY + cos(i * Step) * Radius;
Vertices.Z := 0;
Vertices.U := (inX + 1.0) / 2;
Vertices.V := (inY + 1.0) / 2;
Inc(Vertices); end;
hRet := FD3DVB.Unlock; if Failed(hRet) then begin
Result := hRet;
Exit;
end;
// Связанные треугольники выстраиваются в полньм круг
Result := FDSDDevice.DrawPrimitive(D3DPTJTRIANGLEFAN, О, SIDES);
end;
Пользуемся мы этой функцией отдельно для каждого элемента массива:
for i := 0 to SIZE - 1 do begin
hRet := DrawCircle (Points [i].X, Points [i].Y);
Kif FAILED (hRet) then begin
Result := hRet;
Exit;
end;
end;
Размер мазков регулируется. Можно также получить новый набор точек, чтобы подобрать самую эффектную картинку:
procedure TfrmD3D.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
i : Integer;
begin
if Key = VK_ESCAPE then Close else
if Key = VK_INSERT then Radius := Radius + 0.005 else
if Key = VKJ3ELETE then Radius := Radius - 0.005 else
if Key = VKJ3PACE then begin // Заново генерируем набор точек
for i := 0 to SIZE - 1 do begin
Points[i].X := random * 2 - 1.0;
Points[i].Y := random * 2 - 1.0;
end;
end;
end;
Надо приложить совсем немного усилий, и вы можете попробовать себя в качестве художника. Достаточно запустить откомпилированный модуль из каталога Ex11. Работа его похожа на предыдущий, но точки массива теперь генерируются не в пределах всего окна, а в области расположения курсора, при нажатой кнопке мыши:
procedure TfrmD3D.FormMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
i : Integer;
begin
if Down then begin // Нажата ли кнопка мыши
// Сравниваем с предыдущим расположением курсора
if (X о LastX) and (Y <> LastY) then begin
for i := 1 to 20 do begin // Берется 20 точек облачка
// вокруг курсора
NumPoints := (NumPoints + 1) mod SIZE;
// Масштабируем точки для системы координат D3DFVF_XYZ
Points[NumPoints].X := ((X + random (7) - 3)/ ClientWidth) * 2 - 1.0;
Points[NumPoints].Y := ((ClientHeight -
(Y + random (7) - 3)) / ClientHeight) * 2 - 1.0;
LastX := X;
LastY := Y;
end;
end;
end;
end;
Для каждого мазка строится набор из 21 треугольника. Как следствие, имеем низкое значение FPS, уменьшающееся с каждым мазком. При маленьком размере кругов подобный подход неэффективен, и нам стоит поискать альтернативное решение такой задачи.
Альфа-составляющая текстуры
Формат текстуры D3DFMT_A8R8G8B8 позволяет для каждого пиксела образа заавать индивидуальное значение альфа-составляющей, чем можно воспольоваться для получения массы интересных эффектов. Так, проект каталога Ex12 решает задачу, сходную задаче предыдущего примера: курсор при своем вижении по поверхности окна оставляет след постепенно проступающего браза (рис. 8.9).
Но теперь образ проступает точка за точкой. Мы не используем множество римитивов, и работает пример гораздо быстрее предыдущего.
ри заполнении прямоугольника текстуры значение альфа-составляющей ш каждого пиксела задается явно, нулевым значением:
PDWORD (DWORD(d3dlr.pBits) + У * dwDstPitch + X * 4)^ :=
D3DCOLOR_ARGB(0, R, G, В);
То есть все точки образа текстуры первоначально задаются совершенно прозрачными.
При воспроизведении квадрата с наложенной текстурой разрешаем работу с альфа-составляющей привычным для нас способом. Помимо этого необходимо отметить, что значение альфа для каждого пиксела текстуры определяется содержимым ее образа:
with FDSDDevice do begin
SetTexture(0, FD3Texture); // Устанавливаем текстуру
// Операции с цветом пикселов текстуры
SetTextureStageStatefO, D3DTSS__COLOROP, D3DTAJTEXTURE);
// Операции с альфа-компонентом пикселов текстуры
SetTextureStageStatefO, D3DTSS_ALPHAOP, D3DTA_TEXTURE);
// Разрешаем работу с альфа-составляющей
SetRenderState(D3DRS_ALPHABLENDENABLE, DWORD (True));
// Параметры альфа-смешения
SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
end;
// Квадрат, покрытый текстурой
hRet := FD3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
if FAILED(hRet) then begin
Result := hRet;
Exit;
end;
// Выключаем текстуру и альфа-смешение
with FDSDDevice do begin
SetTexture(0, nil) ;
SetRenderState(D3DRS_ALPHABLENDENABLE, DWORD (False));
end;
При движении курсора увеличиваем значение альфа-составляющей для пикселов растра текстуры так, чтобы они стали непрозрачными:
procedure TfrmD3D.FormMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
d3dlr : TD3DLOCKED_RECT;
dwDstPitch : DWORD;
i : Integer; wrkX, wrkY : DWORD;
begin
if Down then begin // Нажата ли кнопка мыши
FD3Texture.LockRect(0, d3dlr, nil, 0);
dwDstPitch := d3dlr.Pitch;
for i := 1 to 50 do begin //50 точек в районе курсора
repeat // Генерируем точку в пределах окна
wrkX := DWORD (X + random (7) - 3);
wrkY := DWORD (ClientHeight - Y + random (7) - 3);
until (wrkX < DWORD (ClientWidth)) and (wrkY < DWORD (ClientHeight))
and (wrkX > 0) and (wrkY > 0);
PDWORD (DWORD(d3dlr.pBits) + wrkY * dwDstPitch + wrkX * 4)^ :=
// Альфа-составляющую для точек задаем равной 255
PDWORD (DWORD(d3dlr.pBics) + wrkY * dwDstPitch + wrkX * 4)" +
SFF000000;
end;
FD3Texture.UnlockRect(0);
end;
end;
Несколько небольших замечаний:
* размер окна я установил равным размеру образа, поэтому нет необходимости соотносить координаты курсора и положение пикселов в растре; преобразование типа используется только для того, чтобы не появлялось замечание компилятора; нельзя допускать попытки записи за пределы прямоугольной области текстуры; при движении курсора по непрозрачным пикселам полагаемся на то, что исключение, связанное с переполнением, возникать не будет.
Проверка границ генерируемых значений точек необходима еще для того, чтобы при нахождении курсора вблизи границ окна не появлялся след на его противоположном краю. Однако эта проверка может оказать плохую службу нашему приложению: если удерживать кнопку мыши и переместить курсор за пределы окна, приложение зацикливается на безуспешной попытке сгенерировать точки в указанных пределах. Чтобы вы убедились, как важно проследить за подобными вещами, в этом примере я оставлю брешь, а в следующем устраню ее: достаточно добавить проверку нахождения курсора в пределах клиентской области окна.
Мультитекстурирование
Для помещения на объект нескольких текстур одновременно можно воспользоваться простейшим способом альфа-смешения: воспроизводить несколько раз один и тот же полупрозрачный объект с наложением различных текстур.