KnigaRead.com/

Михаил Краснов - Графика DirectX в Delphi

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Михаил Краснов, "Графика DirectX в Delphi" бесплатно, без регистрации.
Перейти на страницу:

for Index := 0 to MaxParticles do

with Particle [Index] do begin

Speed := 1 + round (random (3)) ;

Angle : = random * 2 * Pi;

X := random (ScreenWidth - 1) + 1;

Y := random (ScreenHeight - 1) + 1;

Decay := random;

HalfLife := random / 20;

AngleAdjustment := random / 20;

end;


При каждом обновлении экрана отслеживаются новые позиции частиц и усредняются цвета пикселов, подобно предыдущему примеру:


for Index := 0 to ParticleCount do

with Particle [Index] do begin

Decay := Decay - HalfLife; // Уменьшить время жизни

// Срок существования прошел, появляется новая точка

if Decay <= 0 then begin

Decay := 1;

X := mouseX; // В позиции курсора

Y := mouseY;

end;


Angle := Angle + AngleAdjustment; // Движение по спирали

If Angle >= 2 * Pi then Angle := 0; //От переполнения

X := X + round (cos(Angle) * Speed); // Новая позиция

Y := Y + round (sin(Angle) * Speed);

// Точка, ушедшая за границу экрана

if (X > ScreenWidth - 2) or (X < 2) then begin

X := mouseX; // Переместить в позицию курсора

Y : = mouseY;

Angle := random * 2 * Pi;

end

else if (Y > ScreenHeight - 2) or (Y < 2) then begin

X := mouseX;

Y := mouseY;

Angle := random '* 2 * Pi;

end;


// "Отображение" точки

Pict [X, Y] := Speed * 16 + 186;

end;


// Эффект размытости for Index := 1 to BlurFactor do for X := 2 to ScreenWidth - 2 do

for Y := 2 to (ScreenHeight - 2) do begin

// Усреднение значения девяти соседних элементов Accum := 0;

Accum := Accum + Pict [X, Y] +

Pict[X, Y + 1] + Pict[X, Y - 1] +

Pict[X + 1, Y] + Pict[X - 1, Y] +

Pict[X + 1, Y + 1] + Pict[X - 1, Y - 1] +

Pict[X + 1, Y - 1] + Pict[X - 1, Y + 1];

Accum := Accum div 9; // Усреднение значений

// соседних пикселов

Pict [X, Y] :=' Accum;

end;


Чтобы изображение не съеживалось с течением времени, как в предыдущем примере, закрашиваясь черным цветом, граничные точки экрана заполняются ненулевыми значениями:


for Index := 0 to ScreenWidth - 1 do begin

Pict[Index, 0] := 127;

Pict[Index, ScreenHeight - 1] := 127;

Pict[Index, 1] := 127;

Pict[Index, ScreenHeight - 2] := 127;

end;


for Index := 0 to ScreenHeight - 1 do begin

PictfO, Index] := 127;

Pict[ScreenWidth - 1, Index] := 127;

Pict[l, Index] := 127;

Pict[ScreenWidth - 2, Index] := 127;

end;



С помощью клавиш <Ноте> и <End> можно менять количество частиц, а с помощью клавиш <Page Up> и <Page Down> - управлять степенью усреднения пикселов.

Пример может работать при разных разрешениях и глубине цвета экрана. Обратите внимание, что при его очистке размер блока в таких случаях задается исходя из значения текущей глубины:


ZeroMemory (desc. IpSurface, desc.lPitch * ScreenHeight * (ScreenBitDepth div 8) ) ;

Также здесь нельзя использовать значение ширины экрана вместо lPitch, т. к. из-за выравнивания памяти это могут быть разные значения. Ширина поверхности "подгоняется" к границам параграфов, т. е. должна быть кратна 4-м байтам.

Массивы в видеопамять приходится переносить медленным способом - поэлементно. Одна ячейка массива занимает байт, при разрешении экрана в 16 разрядов на пиксел массив скопируется только в первую половину памяти поверхности. Если же вы в своем приложении не собираетесь менять разрешение, то вполне можете копировать массив целиком, одной командой CopyMemory.

Поскольку значения в массиве pict лежат в пределах диапазона типа Byte, то для 16-битного режима картинка получится не очень выразительной и отображается оттенками одного цвета.

Сохранение растровых изображений

Наверняка перед вами рано или поздно встанет задача сохранения получающихся картинок. Если вы попытаетесь их скопировать в буфер обмена для дальнейшей вставки в рисунок графического редактора, то обнаружите проблему с 256-цветными приложениями. Картинки будут искажаться, поскольку палитра таких изображений будет отличной от палитры рисунка.

Я приведу простейшее решение проблемы, основанное на использовании объекта класса TBitmap. В предыдущем примере обработчик формы нажатия клавиши приведите к следующему виду:


procedure TfrmDD. FormKeyDown (Sender: TObject; var Key: Word

Shift: TShiftState) ; var

BitMap : TBitmap; // Для записи картинок в файл begin

case Key of

VK NEXT : BlurFactor := BlurFactor + 1;

VK_PRIOR : begin

BlurFactor := BlurFactor - 1;

if BlurFactor < 1 then BlurFactor := 1;

end;


VK_HOME : begin

Inc (ParticleCount, 1000);

if ParticleCount > MaxParticles then ParticleCount := MaxParticles;

end;


VK_END : begin

Dec {ParticleCount, 1000);

if ParticleCount < 2000 then ParticleCount := 2000;

end;


// По нажатию пробела содержимое экрана сохраняется в файле

VK_SPACE : begin

BitMap := TBitmap.Create;

BitMap.PixelFormat := pf24bit; // Разрядность задаем 24

BitMap.Height := ClientHeight;

BitMap.Width := ClientWidth;

// Копируем в BitMap содержимое экрана

BitBlt(BitMap.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,

Canvas.Handle, 0, 0, SRCCOPY);

BitMap.SaveToFile ('l.bmp'); // Записываем в файл

end;


VK_ESCAPE,

VK_F12 : Close;

end;


end;


Записываются 24-битные файлы, и информация о цвете не теряется в любом случае.


Доступ к пикселам в 16-битном режиме

В таком режиме информация о цвете пиксела разделяется на три цветовые составляющие, но шестнадцать на три нацело не делится, поэтому разработчики вынуждены прибегать к неравномерному распределению. Наиболее распространенной является схема 5-6-5. В этом формате первые пять битов хранят значение красного оттенка, следующие шесть битов отводятся под зеленую составляющую, ну и последние пять битов заняты оттенком синего. Всего получается 65 536 (216) различных цветов. Из них по 32 градации красного и синего, 64 градации зеленого.

Схема 5-6-5 является самой распространенной. Поэтому для начала будем опираться именно на нее. Как быть в случае другого формата, рассмотрим позднее.

Для примера возьмем цвет, образованный следующими значениями составляющих:


* красный, 5 бит: 00011; зеленый, 6 бит: 001011; синий, 5 бит: 00101.

Значение пиксела с таким цветом будет следующим (пробелы вставлены для удобочитаемости):


0001 1001 ОНО 0101

Все выглядит просто, имея значение трех составляющих, мы должны в пиксел заносить значение по следующей формуле:


blue + green * 2"5 + red * 2Л11 или blue + green * 64 + red * 4096

Операции умножения и деления с участием степени двойки лучше оптимизировать с помощью операции сдвига. Теперь окончательная формула выглядит так:


blue OR (green SHL 5) OR (red SHL 11)

Иллюстрация в виде примера последует позже, а сейчас задержимся на том, как вырезать из пиксела значения составляющих. Для этого применяются битовые маски. Так, для получения значения пяти битов красной составляющей надо использовать бинарное число

1111 1000 0000 0000

и логическую операцию AND для вырезания значения первых пяти битов. Вот так:


0001 1001 ОНО 0101 &

1111 1000 0000 0000

-------------------------------

0001 1000 0000 0000

Результат найден, как видим, верно, но ему предшествуют одиннадцать нулей. Чтобы получить значение составляющей, надо применить к этому выражению операцию битового сдвига вправо. Вот пример для красной составляющей:


Red : Byte;

Red := (pixel & $F800) SHR 11;

Или, если поменять порядок действий, вырезать ее можно так:


Red := (pixel SHR 11) AND $lf;

Маска в этом случае та же - пять единиц, но без завершающих одиннадцати нулей.

Перейдем к иллюстрации - проекту каталога Ех17. Работа его выглядит очень просто, на экране появляются вспышки синих и красных частиц. Работа с системой частиц во многом похожа на код предыдущего примера, но теперь воспользуемся концепцией ООП:


const

MAX ENERGY =60; // Максимальная энергия частицы

DEFAULT_SIZE =200; // Количество частиц во вспышке

DEFAULT_POWER =30; // Для зарядки энергии частицы

type

TParticle = record // Данные на отдельную частицу

X, Y : Single; // Позиция

SpeedX, SpeedY : Single; // Скорости по осям

Energy : Integer; // Энергия

Angle : Integer; // Направление движения

R, G, В : Byte; // Цвет

end;


TParticleSystem = class // Класс системы частиц

public

procedure Init (NewSize, Power : Integer); // Инициализация

procedure Calculate; // Пересчет положений частиц

function Render : HRESULT; // Отображение вспышки

private

Particle : Array [0..1000] of TParticle; // Массив частиц

Size : integer; // Размер

end;


Инициализация системы выглядит так:


procedure TParticleSystem.Init (NewSize, Power : Integer);

var

i : Integer;

X, Y : Integer; // Стартовая точка вспышки Speed : Single;

begin

Size := NewSize; // Устанавливаем размер системы

// Центр вспышки располагаем вдали от границ экрана

X := random (ScreenWidth - 80) + 40;

Y := random (ScreenHeight - 80) + 40;

for i := 0 to Size do begin // Частицы системы

Particle[i].X := X;

Particle[i].Y := Y;

Particle[i].Energy := random (MAX_ENERGY); // Энергия

Particle[i].Angle := random (360); // Угол движения

Speed := random (Power) - Power / 2;

Particle[i].SpeedX := sinAfParticle[i].Angle] * Speed;

Particle [i] . SpeedY := cosA[Particle [i] .Angle] * Speed;

Particle [i] . r := random (256); // Сине-красный цвет

Particle [i] . g := 0;

Particle[i] .b := random (256);

end;


end;


Первый раз система инициализируется в начале работы приложения. Здесь же заполняются вспомогательные массивы, хранящие синусы и косинусы углов:


sinA : Array [0..360] of Single;

cosA : Array [0..360] of Single;

PS : TParticleSystem;

for j := 0 to 360 do begin // Для оптимизации, чтобы вычислять

sinA[j] := sin(j * Pi / 180); // только один раз

cosA[j] := cos(j * Pi / 180); end;

PS := TParticleSystem. Create; // Создание системы

PS.Init (DEFAULT_SIZE, DEFAULT_POWER) ; // Инициализация системы

В методе calculate класса вспышки пересчитываются текущие координаты частиц:


procedure TParticleSystem. Calculate;

var

i : Integer;

begin

for i := 0 to Size do begin

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*