raj▒c w Jazz Jack Rabbit nie mo┐na nie ulec ciekawym efektom graficznym. Bo nie w ka┐dej grze z okresu < 1998 widaµ takie rzeczy. Szczeg≤lnie zainspirowa│a mnie transparencja...
Transparencja to po prostu przezroczysto╢µ. Chodzi mi o wy╢wietlanie jakiego╢ obrazka na tle innego, z tym, ┐e wy╢wietlany obrazek bΩdzie nieco prze╢witywa│.
Okazuje siΩ, ┐e tak┐e i my mo┐emy zrobiµ taki efekt, co wiΩcej to wcale nie jest trudne.
Paleta
Tutaj bΩd▒ nam potrzebne wszystkie odcienie jakiego╢ koloru, np. czerwonego. Naj│atwiej by│oby zrobiµ tak:
For i := 0 To 255 Do SetCol(i, i Div 4, 0, 0);
Ale taka paleta to marnotrawstwo! PamiΩtajmy bowiem, ┐e karta VGA pozwala na wy╢wietlenie do 64 odcieni danego koloru, a nie 256. Dlatego te┐ do stworzenia tego i wielu innych efekt≤w (np. bump mapping) wystarczy taka paleta:
For i := 0 To 63 Do SetCol(i, i, 0, 0);
Tym sposobem mamy jeszcze 192 kolory do dyspozycji. Mo┐emy je po╢wiΩciµ na np. napisy, czy te┐ na t│o, kt≤re nie musi byµ wype│nione odcieniami tylko jednego koloru.
Zmienne, sta│e, typy
Najwa┐niejszy jest w tym efekcie bufor ramki. Co to oznacza? Wiele gier wykorzystuje tΩ technikΩ. A wszystko po to, aby do minimum ograniczyµ bezpo╢rednie zapisywanie do pamiΩci karty. DziΩki temu unikniemy migotania obrazu, a sam program bΩdzie znacznie szybszy.
Type ScreenArray = ^ScreenType; ScreenType = Array[0..63999] Of Byte; Var Screen : ScreenArray;
Potrzebne s▒ jeszcze dwie dodatkowe tablice. W pierwszej bΩdzie t│o, w drugiej za╢ przezroczysty obrazek. Pierwsza tablica bΩdzie tego samego typu, co bufor ramki, tak wiΩc piszemy:
Var Image1 : ScreenArray;
Druga tablica bΩdzie wymaga│a odrΩbnego typu. BΩdzie on podobny do bufora ramki, z tym, ┐e bΩdzie mia│ rozmiar szeroko╢ci nak│adanego obrazka pomno┐onej przez jego wysoko╢µ i ca│a ta liczba musi byµ zmniejszona o jeden. Przyk│adowo obrazek bΩdzie mia│ rozmiar 128x128:
Type Image2Array = ^Image2Type; Image2Type = Array[0..16383] Of Byte; { 16383 = 128 x 128 - 1 } Var Image2 : Image2Array;
Potrzebne bΩd▒ jeszcze liczniki:
Var i, x, y : Word;
oraz parΩ tymczasowych zmiennych:
Var TempCol : Byte; LX, LY : Word;
Inicjalizacja
Najpierw tworzymy w pamiΩci konwencjonalnej wszystkie tablice, pisz▒c:
New(Screen); New(Image1); New(Image2);
S▒ one po│o┐one poza standardowym segmentem danych programu, tak wiΩc bez tych trzech instrukcji komputer siΩ zawiesi.
Teraz trzeba wyzerowaµ wszystkie tablice, aby unikn▒µ "zaszumionego" ekranu:
FillChar(Screen^[0], 64000, 0); FillChar(Image1^[0], 64000, 0); FillChar(Image2^[0], SizeOf(Image2^), 0);
NastΩpnie musimy zapisaµ w Image1 t│o, za╢ w Image2 nak│adany obrazek. Nie bΩdΩ bawi│ siΩ w prezentowanie sposob≤w na za│adowanie pliku BMP, PCX, czy GIF, ale wygenerujΩ obrazki w programie.
For x := 0 To 319 Do For y := 0 To 199 Do Image1^[320 * y + x] := x Xor y; For i := 0 To 16383 Do Image2^[i] := Random(10) + 40;
Tak by│oby najpro╢ciej i chyba najlepiej.
Oczywi╢cie przed u┐yciem instrukcji Random trzeba j▒ zainicjalizowaµ poprzez instrukcjΩ Randomize i sprawdziµ, czy program u┐ywa modu│u CRT.
G│≤wna pΩtla
Teraz najlepsze! Musimy stworzyµ pΩtlΩ Repeat..Until, a w niej umie╢ciµ, co nastΩpuje:
Efekt latarki
W tytule wspomnia│em o latarce. No wiΩc, spos≤b tworzenia takiego efektu jest bardzo podobny do przedstawionego wy┐ej algorytmu. Wystarczy tylko:
Nak│adany obrazek mo┐na odczytaµ z pliku, mo┐na go tak┐e wygenerowaµ w programie metod▒ cieniowania Phonga (Phong shading). Procedura jest zawarta w kodzie ╝r≤d│owym.
Kontakt
To ju┐ wszystko. Pozosta│y siΩ jeszcze dwa programy przyk│adowe. Je╢li macie
jakie╢ pytania, czekam!
Piotr Horzycki/MediaWorks Entertainment
http://mediaworks.w.interia.pl/
e-mail: mediaworks@interia.pl
================================= TRANSPAR.PAS ================================ {$G+} Program Transparency; Uses Crt, DOS; Type { Bufor ramki } ScreenArray = ^ScreenType; ScreenType = Array[0..64000] Of Byte; { Nak│adany obrazek } ImageArray = ^ImageType; ImageType = Array[0..16383] Of Byte; Const kUp = 72; { Kody klawiszy } kDown = 80; kLeft = 75; kRight = 77; kEsc = 1; Var Screen : ScreenArray; { Bufor ramki } Image1 : ScreenArray; { T│o } Image2 : ImageArray; { Nak│adany obrazek } i, j, x, y : Word; { Liczniki } KeyDown : Array[0..255] Of Boolean; { Stany wszystkich klawiszy } OldKbdInt : Pointer; { Stary sterownik klawiatury } LX, LY : Word; { Pozycja obiektu } TempCol : Byte; { Tymczasowa zmienna } Procedure NewKbdInt; Interrupt; { Umie╢ci│em tutaj t▒ procedurΩ, gdy┐ chcia│em, aby mo┐na by│o sterowaµ przezroczystym obiektem, a korzystanie z ReadKey'a to koszmar } Var Key : Byte; Begin Key := Port[$60]; If Key And $80 = $80 Then Begin Key := Key And $7F; KeyDown[Key] := False End Else KeyDown[Key] := True; Port[$20] := $20 End; { NewKbdInt } Procedure SetCol(Color, R, G, B : Byte); Begin Port[$3C8] := Color; Port[$3C9] := R; Port[$3C9] := G; Port[$3C9] := B; End; { SetCol } Begin Randomize; For i := 0 To 255 Do KeyDown[i] := False; GetIntVec($09, OldKbdInt); SetIntVec($09, @NewKbdInt); New(Screen); New(Image1); New(Image2); FillChar(Screen^[0], 64000, 0); FillChar(Image1^[0], 64000, 0); FillChar(Image2^[0], 6400, 0); For i := 0 To 16383 Do Image2^[i] := 40 + Random(20); Asm mov ax,13h int 10h End; { Paleta... } For i := 0 To 63 Do SetCol(i, i, 0, 0); For i := 64 To 127 Do SetCol(i, 0, i, 0); For i := 128 To 191 Do SetCol(i, 0, 0, i); For i := 192 To 255 Do SetCol(i, i, i, i); { Narysuj t│o } For x := 0 To 319 Do For y := 0 To 199 Do Image1^[320 * y + x] := x Xor y; x := 159; { Kwadrat ma byµ w ╢rodku } y := 99; Repeat FillChar(Screen^[0], 64000, 0); LX := 0; LY := 0; Move(Image1^[0], Screen^[0], 64000); { Na│≤┐ kwadrat } For i := (x - 64) To (x + 63) Do For j := (y - 64) To (y + 63) Do Begin TempCol := Image1^[320 * j + i]; TempCol := (TempCol * Image2^[128 * LY + LX] Div 63) Div 4; Screen^[320 * j + i] := TempCol; Inc(LX); If LX = 128 Then Begin LX := 0; Inc(LY); End; End; { Czy strza│ka zosta│a naci╢niΩta? } If KeyDown[kUp] Then If (y - 64) > 0 Then Dec(y); If KeyDown[kDown] Then If (y + 63) < 199 Then Inc(y); If KeyDown[kLeft] Then If (x - 64) > 0 Then Dec(x); If KeyDown[kRight] Then If (x + 63) < 319 Then Inc(x); { Z bufora ramki - na ekran } Move(Screen^[0], Mem[$A000:0], 64000); Until KeyDown[kEsc]; Dispose(Screen); Dispose(Image1); Dispose(Image2); TextMode(C80); SetIntVec($09, OldKbdInt); Writeln('Transparency Effect'); Writeln('Designed by Piotrala'); Writeln; Writeln('Contact us!'); Writeln('http://mediaworks.w.interia.pl/'); Writeln('e-mail: mediaworks@interia.pl'); End. { TRANSPAR.PAS } ================================= REDLIGHT.PAS ================================ {$G+} Program RedLight; Uses CRT, DOS; Type { Bufor ramki } ScreenArray = ^ScreenType; ScreenType = Array[0..64000] Of Byte; { Kszta│t ╢wiat│a } LightArray = ^LightType; LightType = Array[0..16383] Of Byte; Const kUp = 72; { Kody klawiszy } kDown = 80; kLeft = 75; kRight = 77; kEsc = 1; Var Screen : ScreenArray; Image : ScreenArray; { T│o } Light : LightArray; { Kszta│t ╢wiat│a } i, j, x, y : Word; { Liczniki } KeyDown : Array[0..255] Of Boolean; { Stan wszystkich klawiszy } OldKbdInt : Pointer; { Stary sterownik klawiatury } LX, LY : Word; { Takie dwa liczniki } PhongMap : Pointer; { Tam odbywa siΩ Phong shading } TempCol : Byte; { Tymczasowa zmienna } Procedure NewKbdInt; Interrupt; { Umie╢ci│em tutaj t▒ procedurΩ, bo chcia│em, aby mo┐na by│o sterowaµ ╢wiat│em, a wykorzystanie zwyk│ego ReadKey'a to koszmar } Var Key : Byte; Begin Key := Port[$60]; If Key And $80 = $80 Then Begin Key := Key And $7F; KeyDown[Key] := False End Else KeyDown[Key] := True; Port[$20] := $20 End; { NewKbdInt } Procedure SetCol(Color, R, G, B : Byte); Begin Port[$3C8] := Color; Port[$3C9] := R; Port[$3C9] := G; Port[$3C9] := B; End; { SetCol } Procedure CalcLight; { To ta procedura oblicza kszta│t ╢wiat│a - Phong shading Kszta│t jest obliczany w rozdzielczo╢ci 256x256, a potem jest przeskalowywany do 128x128 } Function Length(x1, y1, x2, y2 : LongInt) : Word; Begin Length := Round(Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))); End; { Length } Begin GetMem(PhongMap, 65535); For y := 0 To 127 Do For x := 0 To 127 Do Begin TempCol := Length(x, y, 128, 128); If TempCol > 127 Then TempCol := 127; TempCol := 129 - TempCol; Mem[Seg(PhongMap^):y Shl 8 + x] := TempCol; Mem[Seg(PhongMap^):(255 - y) Shl 8 + x] := TempCol; Mem[Seg(PhongMap^):y Shl 8 + (255 - x)] := TempCol; Mem[Seg(PhongMap^):(255 - y) Shl 8 + (255 - x)] := TempCol; End; For y := 0 To 127 Do For x := 0 To 127 Do Begin TempCol := Mem[Seg(PhongMap^):256 * (y * 2) + (x * 2)]; If TempCol > 63 Then TempCol := 63; Light^[128 * y + x] := TempCol; End; FreeMem(PhongMap, 65535); End; { CalcLight } Begin Randomize; For i := 0 To 255 Do KeyDown[i] := False; GetIntVec($09, OldKbdInt); SetIntVec($09, @NewKbdInt); New(Screen); New(Image); New(Light); FillChar(Screen^[0], 64000, 0); FillChar(Image^[0], 64000, 0); FillChar(Light^[0], 6400, 0); CalcLight; Asm mov ax,13h int 10h End; { Paleta... } For i := 0 To 63 Do SetCol(i, i, 0, 0); For i := 64 To 127 Do SetCol(i, 0, i, 0); For i := 128 To 191 Do SetCol(i, 0, 0, i); For i := 192 To 255 Do SetCol(i, i, i, i); { Narysuj "XORowane" t│o } For x := 0 To 319 Do For y := 0 To 199 Do Image^[320 * y + x] := x Xor y; x := 159; { Umie╢µ ╢wiat│o w ╢rodku ekranu } y := 99; Repeat FillChar(Screen^[0], 64000, 0); LX := 0; LY := 0; { O╢wietl dane miejsce } For i := (x - 64) To (x + 63) Do For j := (y - 64) To (y + 63) Do Begin TempCol := Image^[320 * j + i]; TempCol := (TempCol * Light^[128 * LY + LX] Div 63) Div 4; Screen^[320 * j + i] := TempCol; Inc(LX); If LX = 128 Then Begin LX := 0; Inc(LY); End; End; { Poka┐ paletΩ na dole - dla niedowiark≤w, kt≤rzy nie wierz▒ mojej tezie, } { i┐ mo┐na mieµ jeszcze dodatkowe 192 kolory do dyspozycji } For i := 0 To 255 Do Screen^[320 * 197 + i] := i; For i := 0 To 255 Do Screen^[320 * 198 + i] := i; For i := 0 To 255 Do Screen^[320 * 199 + i] := i; { Czy naci╢niΩto klawisz strza│ki? } If KeyDown[kUp] Then If (y - 64) > 0 Then Dec(y); If KeyDown[kDown] Then If (y + 63) < 199 Then Inc(y); If KeyDown[kLeft] Then If (x - 64) > 0 Then Dec(x); If KeyDown[kRight] Then If (x + 63) < 319 Then Inc(x); { Z bufora ramki - na ekran } Move(Screen^[0], Mem[$A000:0], 64000); Until KeyDown[kEsc]; Dispose(Screen); Dispose(Image); Dispose(Light); TextMode(C80); SetIntVec($09, OldKbdInt); Writeln('Red Light Effect'); Writeln('Designed by Piotrala'); Writeln; Writeln('Contact us!'); Writeln('http://mediaworks.w.interia.pl/'); Writeln('e-mail: mediaworks@interia.pl'); End. { LIGHT.PAS }
Mo┐esz pobraµ gotowy program pokazuj▒cy powy┐szy efekt wraz ze ╝r≤d│ami w pascalu.