Na FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!

Vytvořit web zdarma

Na FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!

Vytvořit web zdarma

int21h

Grafick reim v TP7 a FP

V dal sti naeho serilu o grafice se zamme na pokroilej reimy, jako jsou 800x600 a vy, ale tak vce barev ne 256. Naume se njak ty efekty jako je prhlednost, naten obrzk, kreslen ar, krunic, natn obrzk ve formtu BMP, ale tvorbu formt vlastnch. A tak trochu nakousneme zobrazovn v 3D. Ped tm ne zaneme, jet pr zmnek o Free Pascalu a minulm dlu.


Pokud jste se pokusili pepsat minul pklady do Free Pascalu, co bylo zvlt ohledn XMS trochu zbyten, asi jste zjistili, e Vm nco nefunguje. Podmnek je nkolik. Potebujete zapisovat do DOSov pamti. Tu u FP alokoval pod svj deskriptor, take Mem smruje do DOSov pamti jako doposud. Pokud ale pouvte ASM, muste pout selektor FS a offset te obshne SEG*16+OFS:


Move(VVRAM,Mem[$a000:0],64000);


asm
	mov	edi,$a0000		{tady nen $a000}
	lds	esi,vvram
	mov	ax,fs
	mov	es,ax			{vhodn je ES obnovit}
	mov	ecx,16000		{ES = DS}
	rep	movsd
(*
	push	ds
	pop	es
*)
end;

Data pro XMS mus leet v konvenn pamti (ovem ve FP je zbyten XMS pouvat). Funkce GetMem a FreeMem vak alokuj pam na hald v chrnnm reimu. Mete pout funkce typu DosMemGet, atp. nebo vyut funkce od DMPI (jedn se o ty sam funkce, jen jinak pojmenovan; viz. npovda k FP). Na alokovn a pesuny pamti mete pout tyto funkce:


var	handle : longint;


begin
 handle := global_dos_alloc(32768);
 dosmemget(hi(handle),0,Buffer,32768);	{pete DOSovou pam}
 dosmemput(hi(handle),0,Buffer,32768);	{zape do konv.pamti}
 dosmemfillchar(hi(handle),0,32768,#0);
 dosmemmove(hi(handle),0,Cil_Seg,Cil_Ofs,32768);
 global_dos_free(word(handle));
end;  

Horn WORD v handle obsahuje segment tto pamti, doln WORD pak obsahuje selektor. Funkce Move a FillChar maj stejn vznam, jako FillChar a Move v TP7. Pro Fill existuj jet varianty s Byte, Word a Dword, co je vhoda oproti TP7. Pokud ale bte ve FP, vele doporuuji mepouvat pomalou XMS, protoe pomoc GetMem mete alokovat teba 5 MB velk prostor (vce ne 2 MB pro XMS Vm vtinou poskytnou emultory, nap. DOSbox pod Windows XP), ve kterm si mete ukldat vechny obrzky a obsluha bude stejn jako u dynamickch promnnch (nebo si mete vytvoit tento buffer pes VAR, pak ale jeho velikost nebude dynamick, a v nich rozliench tedy zbyten budete vyadovat vce RAM ne je nutn).

A dky Mircosoftovi jsem tak odhalil men kosmetickou vadu v mm "zpoovai". Pokud budete vyuvat asova na adrese 0:$46c, mete ho samozejm vyuvat zpsobem, jakm jsem popsal ped tm. To Vm vytvo zaruen zpodn jak potebujete. Pokud ale potebujete, aby toto zpodn bylo maximln, kter program vytvo, a to vetn kdu, kter b ped nm, muste to napsat takto:


var	Cas : longint;
const	Delka = 1;
begin
 repeat
  Cas := MemL[0:$46c];
   {njak kd}
  While MemL[0:$46c]-Cas <= Delka do;
 until False;
end;  

Dky tomuto se vlastn nejprve zjist as, pak se provede kd, a teprve tehdy, pokud proveden kdu bylo moc rychl, nastoup na adu zpodn. U pomalch pota se kd provede pomaleji a tedy nebude nutn pidvat dal zpodn, jako v ppad m procedury, kter vytvela zpodn stle stejn nehled na rychlost potae. A pokud budete mnit rychlost pomoc port a zjistte, e to jede stle 18.2 Hz (co se nkdy pod Windows stv), tak Vm sta obsadit peruen $1c, kam si dte prost Inc(Tiky), kde Tiky : longint, protoe toto peruen Vai rychlost reflektovat bude. Pak sta jen tuto promnnou nastavit na zatku programu na 0 a vce se o ni nezajmat (jen ji samozejm muste st namsto toho MEML).

Grafiku ve Free Pascalu budeme brt extra v njakm ptm dlu. Nic neslibuji, ale mon se jet potom podvme i na Windows API, kde si vytvome pr oken a tlatek (a se to naum). A jet potom budu brt zvukov karty a pak bude zvltn seril o komunikaci mezi potai (s IPX, sockety pes TCP a sriov kabel). Pokud se nkomu z Vs nechce ekat, nech se podv na www.volny.cz/martinlux, kde najde nejen spoustu program, kter vyuvaj klvesnici, my, zvukovou kartu, sriov port, i grafiku, ale tak v sekci Programovn/Perifrie tutorialy na zvukov karty, s IPX a komunikaci pes COM porty.


Tak, nauili jsme se 256barevn md a dle u nemusme. Jen podotknu, e pokud budete chtt pouvat nap. 800x600, tak muste provst jen pr vc:


- zvtit velikost VVRAM na 800x600 byt
- pokud vyuvte zmnu logick dlky dku, tak ji muste pizpsobit:
8 bit    1024
16 bit   2048
24 bit   4096
32 bit   4096
(daje jsou v bytech).

Nyn ale 256barevn reimy opustme a vrhneme se na nco lepho. Vrtme se k nim a budeme pevdt barvy, dve ne. Tabulku reim jsem uvdl v minulm dlu. Pokud nechcete pouvat vyhledvn, muste se spolehnout na ni. Take si te mete nastavit prakticky jakkoliv md. J bych jen tabulku doplnil o dal mdy, kter obas plat (nap. u karet ATi):

$11b	1280  1024	16.8M		24
$11c	 640   400	65536		16
$11d	 640   480	16.8M		32
$11e	 800   600	16.8M		32
$11f	1024   768	16.8M		32
$120	1600  1200	  256		 8
$121	1600  1200	32767		15
$122	1600  1200	65536		16

Pedchoz tabulka vak tak plat (pod Windows 9X a DOSem ano, pod Linuxem asi ne, v DOSboxu by mla platit vdy i pod Linuxem). Nyn Vm chyb u jen jedno: vybrat si md, kter chcete pouvat a pepnout se do nj. Jak ale vytvet pixely? Pedem musme ci, e zde u neexistuje dn paleta. Do VVRAM tedy nebudete zapisovat indexy, ale pmo u RGB popis pixel.

Jak pixely vypadaj jsme si uvdli na zatku naeho serilu, take to opakovat nebudu. Uvedu jen funkce, kter zap pixel do pamti VVRAM (resp. pro zjednoduen jen do pole; opt plat, e pod DOSem muste 24 bitov pixely (3 byty) nejprve nast po 4 bytech (1 byte z druhho pixelu), pak zmnit ten prvn pixel a pak odeslat zase 4 byty). Tentokrt zde neuvdm zmnu banku, protoe ta je zvisl na aktulnm rozlien a dlce dku (stejn tak funkce OffsetXMS mus reflekovat aktuln rozmry a BPP u VVRAM):


procedure Pixel16(X,Y,Hodnota : word);
begin
 word(VasProstor^) := Hodnota;
 UlozXMS(OffsetXMS(X,Y),2);
end;


procedure Pixel24(X,Y : word; Hodnota : longint);
begin
 CtiXMS(OffsetXMS(X,Y),4);
 longint(VasProstor^) := (Hodnota and $ffffff) or
  (longint(Mem[Seg(VasProstor^):Ofs(VasProstor^)+3]) shl 24);
 UlozXMS(OffsetXMS(X,Y),4);
end;


procedure Pixel32(X,Y : word; Hodnota : longint);
begin
 longint(VasProstor^) := Hodnota;
 UlozXMS(OffsetXMS(X,Y),4);
end;  

Pokud byste mli dobrho sprvce XMS nebo by V program bel jen pod Windows (ani na jedno nejde spolhat), tak by procedura pro 24 bitov pixely vypadala takto:


procedure Pixel24(X,Y : word; Hodnota : longint);
begin
 longint(VasProstor^) := Hodnota;
 UlozXMS(OffsetXMS(X,Y),3);
end;  

ten pixel je podobn (funkce pro 16 bitov pixely pracuje stejn dobe i s 15 bitovmi, ale 15 bitov reimy nejsou rychlej, nezabraj mn VRAM, a navc maj 2x mn barev: jedin ospravedlniteln vyuit je u tch karet, kter 16 bitov reimy nepodporuj a Vy nechcete pouvat 32 bitov - nutno podotknout, e prce s 16 bity je pomalej ne s 24/32 bity zvlt, pokud mnte sloky RGB pixel, viz. dle):


function CtiPixel16(X,Y : word) : word;
begin
 CtiXMS(OffsetXMS(X,Y),2);	{pro (nkdy) zrychlen meme st i 4}
 Pixel16 := word(VasProstor^);	{jinak se nic mnit nebude}
end;


function CtiPixel24(X,Y : word) : longint;
begin
 CtiXMS(OffsetXMS(X,Y),4);
 Pixel24 := longint(VasProstor^) and $ffffff;
end;


function CtiPixel32(X,Y : word) : longint;
begin
 CtiXMS(OffsetXMS(X,Y),4);
 Pixel32 := longint(VasProstor^);
end;  

Tyto funkce jsou uiten, pokud potebujete pixely jen pesouvat nap. z obrzku do VVRAM pes test prhlednosti (transluciency je prsvitnost). Pokud samozejm chcete zobrazit jen dek, mete zase provst:


Move(Zdroj,Cil,DelkaRadku*BPP);  

Ale pokud budete chtt s pixely pracovat (nap. je barvit, mnit jas, potat prsvitnost), budete potebovat znt jejich sloky. Take si napeme funkce, kter nm zjist sloky pixel (u 15/16 bitovch budou mt RGB sloky max. 32, resp. u G a 64 odstn; u 24/32 bit a 256), a tak funkce, kter takov rozloen pixel zase slo (nic Vm samozejm nebrn, abyste sloky RGB pmo zapsali do VVRAM bez skldn prostou pravou ve uvedench zapisovacch funkc). Tady se u funkce pro 15 a 16 bitov pixely bude liit (naopak funkce pro 24 bitov pixely me obslouit i 32 bitov, pokud nebudete potebovat nejvy byte):


procedure GetPix15(Pixel : word; var R,G,B : byte); assembler;
asm  
	mov	bx,pixel
	mov	ax,bx
	and	ax,$1f
	les	di,b
	mov	es:[di],al
	mov	ax,bx
	shr	ax,5
	and	ax,$1f
	les	di,g
	mov	es:[di],al
	shr	bx,10
	and	bx,$1f	{vymaeme nejvy bit T}
	les	di,r
	mov	es:[di],bl  
end;


procedure GetPix16(Pixel : word; var R,G,B : byte); assembler;
asm  
	mov	bx,pixel
	mov	ax,bx
	and	ax,$1f
	les	di,b
	mov	es:[di],al
	mov	ax,bx
	shr	ax,5
	and	ax,$3f
	les	di,g
	mov	es:[di],al
	shr	bx,11
	les	di,r
	mov	es:[di],bl  
end;


procedure GetPix24(Pixel : longint; var R,G,B : byte); assembler;
asm  
	mov	al,pixel+2.byte
	les	di,r
	mov	es:[di],al
	mov	al,pixel+1.byte
	les	di,g
	mov	es:[di],al
	mov	al,pixel.byte
	les	di,b
	mov	es:[di],al  
end;


procedure GetPix32(Pixel : longint; var R,G,B,T : byte); assembler;
asm  
	mov	al,pixel+2.byte
	les	di,r
	mov	es:[di],al
	mov	al,pixel+1.byte
	les	di,g
	mov	es:[di],al
	mov	al,pixel.byte
	les	di,b
	mov	es:[di],al
	mov	al,pixel+3.byte
	les	di,t
	mov	es:[di],al  
end;  

Nezapomete, e intel ukld ve opan (aby se to dalo snadno petypovat), take v typu Longint mte sloky jako RGB od nejvyho bitu k nejnimu, ale v pamti jsou uloeny jako BGR (je to vhoda, pokud chcete udlat z LONGINTu BYTE, sta prost napsat Byte(L) a pete se jen 1. byte). Te jet funkce pro sloen pixelu (nezapomete, e do 15/16 bitovch pixel muste dvat sloky zmenen!):


procedure SetPix15(var Pixel : word; R,G,B : byte); assembler;
asm  
	xor	bx,bx
	mov	bl,r
	shl	bx,5
	or	bl,g
	shl	bx,5
	or	bl,b
	les	di,pixel
	mov	es:[di],bx  
end;


procedure SetPix16(var Pixel : word; R,G,B : byte); assembler;
asm  
	xor	bx,bx
	mov	bl,r
	shl	bx,6
	or	bl,g
	shl	bx,5
	or	bl,b
	les	di,pixel
	mov	es:[di],bx  
end;


procedure SetPix24(var Pixel : longint; R,G,B : byte); assembler;
asm  
	les	di,pixel
	mov	al,b
	mov	es:[di],al
	inc	di
	mov	al,g
	mov	es:[di],al
	inc	di
	mov	al,r
	mov	es:[di],al  
end;


procedure SetPix32(var Pixel : longint; R,G,B,T : byte); assembler;
asm  
	les	di,pixel
	mov	al,b
	mov	es:[di],al
	inc	di
	mov	al,g
	mov	es:[di],al
	inc	di
	mov	al,r
	mov	es:[di],al
	inc	di
	mov	al,t
	mov	es:[di],al
end;  

Procedura pro pevod sloek RGB mezi 15/16/24 pixely me vypadat nsledovn:


procedure _15_to_16(var R,G,B : byte);
begin
 G := G shl 1;
end;


procedure _16_to_15(var R,G,B : byte);
begin
 G := G shr 1;
end;


procedure _15_to_24(var R,G,B : byte);
begin
 R := R shl 3;
 G := G shl 3;
 B := B shl 3;
end;


procedure _16_to_24(var R,G,B : byte);
begin
 R := R shl 3;
 G := G shl 2;
 B := B shl 3;
end;


procedure _24_to_15(var R,G,B : byte);
begin
 R := R shr 3;
 G := G shr 3;
 B := B shr 3;
end;


procedure _24_to_15(var R,G,B : byte);
begin
 R := R shr 3;
 G := G shr 2;
 B := B shr 3;
end;  

A pokud byste nkdy potebovali pevst pmo 16bitov pixel (pro 15bitov je poteba mal prava kvli prostednmu daji) na 24/32 bitov a obrcen (nap. mte obrzek v 16 bitech, ale VVRAM je 32 bitov - test na prhlednost mete dlat jednodue: slote si z RGB sloek 16 bitov daj a pak u jen testujete zda WORD v obrzku je stejn jako WORD prhledn barvy a pokud ano, nebudete ho muset pesouvat - je to rychlej ne testovat zvlt sloky RGB; podobn to mete dlat pro 24/32 bitov pixely v Longintu), mete vyut tyto dv funkce:


function Pixel16to24(Barva : word) : longint; assembler;
asm  
	mov	dx,barva
	mov	ax,dx
	mov	bx,dx
	and	ax,$1f
	and	bx,$7e0
	and	dx,$f800
	shl	ax,3
	shl	bx,5
	shr	dx,8
	or	ax,bx  
end;


function Pixel24to16(Barva : longint) : word; assembler;
asm  
	mov	bl,barva.byte
	shr	bl,3
	mov	al,barva+1.byte
	xor	ax,ax
	shr	al,2
	shl	ax,5
	xor	cx,cx
	mov	cl,barva+2.byte
	shr	cl,3
	shl	cx,11
	or	ax,bx
	or	ax,cx
end;  

Kdybyste to snad sami nezvldli, tak zde mte i 15bitov funkce:


function Pixel15to24(Barva : word) : longint; assembler;
asm  
	mov	ax,barva
	mov	bx,ax
	mov	dx,ax
	and	ax,$1f
	shl	ax,3
	and	bx,$3e0
	shl	bx,3
	or	ax,bx
	and	dx,$7c00
	shr	dx,7  
end;	{vsledek je v DX:AX}


function Pixel24to15(Barva : longint) : word; assembler;
asm  
	mov	bl,barva.byte
	shr	bl,3
	mov	al,barva+1.byte
	xor	ax,ax
	shr	al,3
	shl	ax,5
	xor	cx,cx
	mov	cl,barva+2.byte
	shr	cl,3
	shl	cx,10
	or	ax,bx
	or	ax,cx  
end;  

Samozejm mete pixely zapisovat pes Mem pmo do VasProstor po slokch R,G,B, ppadn je i rovnou pomoc SHR zmenovat, pokud mte 8 bitov sloky a potebujete pro HC jen 5/6 bitov.

Te, kdy vme, jak pracovat s pixely, tak si ukeme dva (resp. ti efekty), kter jsme trochu nakousli u 256barevnch reim. Jak zjistit, zda je pixel pln prhledn u vme (test <> nebo =). Jak ale spojit dva pixely, kdy jeden z nich je barva pozad a druh je trochu prhledn (barevn sklo)? Nebo jak obarvit pixel i zmnit jeho jas? Ve je velice jednoduch. Ukeme si to pro 24 bitov a 16 bitov pixely. Ostatn si snad jist odvodte sami. Prhlednost se standardn pot tmto vzorekem:


	(HORNI*PRUHL+DOLNI*(100-PRUHL)) DIV 100  

To v ppad, e bychom potali prhlednost pes 0-100%. To je ale velice pomal, take zkusme namsto destkov soustavy dvojkovou, kter potam sed o mnoho vce. Tuto metodu lze pout i pokud potebujete zprhlednit nkolik vrstev pixel nad sebou (sta, kdy pjdete od spodu a budete potat vdy dvojice Pozadi-DalsiVrstva, kdy do Pozadi postupn dosadte ji vypoten prniky). My budeme pro zjednoduen brt, e pro 8 bitov sloky plat, e 255 je neprhledn pixel, 0 je pln prhledn. Pro 6/5 bitov to bude 63, resp. 31. Pokud budeme potat jas (resp. pro R<>G<>B je to barven), tak budeme brt pro 8 bitov sloky, e 100% je 64, pro 6 bitov to bude 16, a pro 5 bitov 4. Tm zskme monost pixel a 4x zesvtlit, nebo 64x ztmavit pro 8 bitov, 4x/16x pro 6 bitov a 4x/4x pro 5 bitov):


procedure ZmenJas16(var R,G,B : byte; dR,dG,dB : byte);
begin	{dR a dB = <0,31>, dG=<0,63>; tot pro R,G,B}
 R := word(R)*word(dR) shr 3;
 R := word(R)*word(dR) shr 4;
 R := word(R)*word(dR) shr 3;
end;


procedure ZmenJas24(var R,G,B : byte; dR,dG,dB : byte);
begin
 R := word(R)*word(dR) shr 6;
 R := word(R)*word(dR) shr 6;
 R := word(R)*word(dR) shr 6;
end;


procedure Pruh16(R1,G1,B1,R2,G2,B2,R3,G3,B3,Pruhl : byte);
var PrPruhl : word;		{Pruhl=<0,31>}
begin
 PrPruhl := 31-Pruhl;
 R3 := (word(R1)*word(Pruhl)+word(R2)*PrPruhl) shr 5;
 G3 := (word(G1)*word(Pruhl shl 1)+word(G2)*(PrPruhl shl 1)) shr 6;
 B3 := (word(B1)*word(Pruhl)+word(B2)*PrPruhl) shr 5;
end;


procedure Pruh24(R1,G1,B1,R2,G2,B2,R3,G3,B3,Pruhl : byte);
var PrPruhl : word;
begin
 PrPruhl := 255-Pruhl;
 R3 := (word(R1)*word(Pruhl)+word(R2)*PrPruhl) shr 8;
 G3 := (word(G1)*word(Pruhl)+word(G2)*PrPruhl) shr 8;
 B3 := (word(B1)*word(Pruhl)+word(B2)*PrPruhl) shr 8;
end;  

Tak, a te meme vesele vytvet zajmav efekty, kdy se nm pixely prolnaj, atd. Pokud jedete ve 256 barvch a chcete vypotat prsvitnost dvou pixel, muste si pomoc jejich indexu zjistit z tabulky (paleta) jejich R,G,B sloky (pokud mono v rozsahu 0-255; nebo 0-63, kdy si ale muste napsat novou funkci), pak je pomoc ve uveden funkce zmixovat a pak muste podle vsledn R,G,B barvy najt njakou, kter pesn (nebo alespo piblin) odpovd njak barv v palet a podle n vrtte index. To budeme brt pozdji, dn strach.

Nejprve ale trochu optimalizace. Pokud budete chtt vypotat prhlednost celho obrzku, je zbyten neustle potat kad pixel. Stejn to plat i pro jas. Vemte si toto: mte obrzek 320x200 a chcete ho prhledn zobrazit na 75% viditelnosti pes pozad (nap. mlha), Pro 64.000x potat u kadho pixelu znovu vsledek? Kdy si to meme pedpotat pedem (asi se nevyplat u obrzk, kter maj mn ne 256-512 pixel, tj. nap. 16x16, 32x16, atd., leda e by jich lo po sob nkolik a vechny se stejnou % prhlednost). Bylo by samozejm ideln si na zatku programu pedpotat vechno, ale to by zabralo pli mnoho prostoru (tabulka pro osvtlen 256*256 = 64kB, a pro prhlednost dokonce 256*256*256 = 16 MB).

Vyeme to tak na pl. Tabulku pro osvtlen si meme vypost pi startu programu a to pro 64 odstn jasu. Tm si zachovme docela slun rozsah a pitom zmenme velikost (16 kB):


var	Jas : array[0..63,0..255] of byte;
	j,p : byte;


begin
 for j := 0 to 63 do
  for i := 0 to 255 do
   Jas[j,p] := word(p)*word(j shl 2) shr 6;
end;  

Tabulka je platn pro 24/32 bitov pixely (v dnen dob se u moc nevyplat podporovat 16 bitov reimy, protoe VRAM m na kart kad dost a navc vpoty s nimi jsou dost pomal - muste pevdt. Trochu Vs jen zachrn, e vlastn pente 50% a 66% pvodnch dat). Nyn, kdy budete chtt zjistit hodnotu njakho pixelu po zmn jasu, provedete to nsledovn (ZMENA je 0-255):


NOVYJAS := Jas[ZMENA shr 2,STARYJAS];  

Pro kadou sloku pixelu. Tm se vpoet zkrtil na jedno SHR, jedno MUL (to TP7 dl, pokud si to pepete do ASM, mete MUL 256 nahradit SHL 8 a bude to jet rychlej) a MOV.

OK, jas (a barevnost) u umme mnit, te jak prhlednost. Nememe vyuvat vechny kombinace, protoe to by prost byla tabulka jak dlo (pod FP samozejm dn problm, ale uvdomte si, e 16 MB jen na tabulku nen ani v dnen dob zrovna astn npad: do tto velikosti mete nahrt destky sprit, kter Vm tedy budou chybt a budete muset zvyovat nroky na HW). Vyuijeme men nepesnosti lidskho oka. Sloky budete opt zadvat 0-255, stejn tak prhlednost, ale my budeme pouvat jen 32 stup prhlednosti (tedy nemusme ped kadm obrzkem potat tabulku znovu, pokud by pouval jinou ne ten pedchoz) a pouze 128 rovn jasu jednotlivch sloek u hornho pixelu a 64 u dolnho (budeme pedpokldat, e horn pixel bude vtinou vce vidt ne ten doln, resp. PRUHL bude 128 a ve, tch pr obasnch <128 zase a tak moc neukod). Tm se velikost tabulky zmen na 256 kB. Pokud se Vm to zd moc, mete zmenit poet stup na 16, a dostanete se na 128 kB (takovou tabulku ale mete mt i XMS (v ppad TP7, u FP je 256 kB nic, tam mete klidn dt spodnmu pixelu tak 128 odstn a tabulka bude mt jen 512 kB, co je snesiteln velikost) a penet vdy jen tu st, kterou potebujete (tj. vdy jen 128*64 byt pro dan stupe prsvitnosti, take v konvenn pamti bude vdy jen 8 kB - podobn to mete dlat i s jasem, kdy Vm bude stait mt v doln pamti vdy jen 256 byt).


var	Pruhl7 : array[0..127,0..63] of byte;		{pro TP7 a XMS}
	Pruhl : array[0..31,0..127,0..63] of byte;	{pro FP}
	p,a,b : byte;


begin
	{vpoet pro TP7 s XMS}
 for p := 0 to 31 do
 begin
  for a := 0 to 127 do
   for b := 0 to 63 do
    Pruhl7[a,b] := (word(a shl 1)*word(p)+word(b shl 2)*(31-p)) shr 5; 
  ; ulo tabulku do XMS (1 handle, offs=8192*p)
 end;
	{vpoet pro FP}
 for p := 0 to 31 do
  for a := 0 to 127 do
   for b := 0 to 63 do
    Pruhl[p,a,b] := (word(a shl 1)*word(p)+word(b shl 2)*(31-p)) shr 5; 
end;  

Vpoty jsou opt platn pro 8 bitov sloky pixel. Pro jin mte dv monosti: bu pepete algoritmus a nebo budete vechno potat jako v TC (tj. HC pixely pevedete na TC, spotate a zase pevedete zpt na HC). Pixely pak zmixujete tmto stylem:


{metoda pro TP7}
; pokud nen natena, nati XMS tabulku z ofs=(p shr 3)*8192
Slozka := Pruhl7[horni shr 1, dolni shr 2];


{metoda pro FP}
Slozka := Pruhl[p shr 3, horni shr 1, dolni shr 2];  

Ve uveden je zameno hlavn na eten pamt, take pokud si mete dovolit pltvat, mete uetit jednu SHR instrukci a dt hornmu pixelu 256 jas (bez SHR) a dolnmu 128 (SHR 1). Tabulka bude mt pak 2 MB (nebo 1 MB, pokud zachovte 64 odstn pro doln). A pamatujte, e i VAR promnn jsou v konvenn pamti, take nemuste tabulku penet z XMS nejprve do njakho pointeru pes GETMEM a pak teprve do VAR (mete to udlat pmo; VAR je tak defakto pointer).

Dobr, nauili jsme se dlat efekty ve vych barvch. Co kdy ale nkdo neme vyuvat HC nebo TC a jede jen v 256 barvch? Musme je pevdt. Nam eenm je, e meme mt stejn vechny obrzky v pamti v TC a HC, stejn tak nai VVRAM (pokud ovem nepracujeme s 8 bitovmi, a jen potebujeme provst prhlednost, jak jsem psal ve). Vechny vpoty pak tak provdme v TC, a teprve, kdy potebujeme vykreslit VVRAM, pevedeme ji na 256 barev a tyto poleme do VRAM (vyuijeme jet njak dal pracovn buffer). Mme ti monosti, jak provdt tento pevod.

Prvn metoda je, e si nechme vytvoit paletu v odstnech edi. To jsme u brali. Pot, kdy dostaneme pixel, meme ho pevst jednodue tmto stylem:


INDEX := (R+G+B) div 3;  

Nebo rychleji, kdy vyuijeme pracovn sloku (tu si mete nastavit na 0-255 a tm vlastn mnit gamma obrazu: pokud dte 0, nedostanete nikdy pln blou; pokud dte 255, nedostanete nikdy pln ernou):


INDEX := (R+G+B+X) shr 2;  

Ano, i toto je metoda. Ale pokud chceme zobrazovat barevn, musme provdt pevod. Dejme tomu, e paletu u mme (jak ji dostaneme, o tom se zmnme pozdji). Budeme ji tedy muset prochzet a zjiovat, zda se sloky RGB pixelu shoduj se slokami njakho indexu. Protoe je ale nepravdpodobn, e by po zmn pixelu njak z nich byl pesn shodn s tm nam, budeme muset potat s jistou mrou tolerance. Jak s n nalome je na ns. Meme ji postupn zvyovat od 0 to 255, dokud nenajdeme pixel. Meme zvyovat vechny sloky narz (rychlej) nebo stdav (9x pomalej, ale pesnj). Meme tak mnit vdy jen + a pak jen -, ale to ns opt 4x zdr. Toleranci meme zvyovat po 1, nebo exponenciln (tj. 1,2,4,8,16,32, atd.), co je rychlej, ale me dt hor vsledky. J jsem si pro ukzku vybral metodu, kter zvyuje exponenciln a u vech sloek souasn na ob strany.


function NajdiPixel(R,G,B : byte) : byte;
var	ind : byte;
	rl,rh,gl,gh,bl,bh : integer;
	tol : byte;
	tolerance : word;
begin
 tol := 0;
 repeat
  tolerance := 1 shl tol;
  inc(tol);
  rl := R-tolerance;
  rh := R+tolerance;
  gl := G-tolerance;
  gh := G+tolerance;
  bl := B-tolerance;
  bh := B+tolerance;
  if rl < 0 then rl := 0;
  if gl < 0 then gl := 0;
  if bl < 0 then bl := 0;
(*
  if rh > 255 then rh := 255;
  if gh > 255 then gh := 255;
  if bh > 255 then bh := 255;
*)
  for ind := 0 to 255 do
   if (Paleta[ind].R >= rl) and
      (Paleta[ind].R <= rh) and
      (Paleta[ind].G >= gl) and
      (Paleta[ind].G <= gh) and
      (Paleta[ind].B >= bl) and
      (Paleta[ind].B <= bh) then
    begin
     NajdiPixel := ind;
     Exit;
    end;
 until tolerance = 256;
 NajdiPixel := 0;
end;  

Pokud bychom chtli zvyovat toleranci po 1, sta nm obyejn INC a nemusme dlat dn SHL (a navc uetme 1 promnnou). Pro real-time pevod rozlien nad 640x480 ale budete potebovat alespo 1 GHz procesor, abyste tam mohli provozovat adventuru nebo dungeon hru (zatmco u Gray palety nm sta obyejn 486 a u exponenciln metody poblin Pentium II). Dobe, najt barvu umme, ale kde seeneme paletu?

Mme nkolik een. Bu si paletu nateme z njakho externho souboru (768 byt), nebo z PEL registr video karty (to u umme), nebo si vytvome adaptivn paletu. Jak se to dl? Budeme potebovat celkem velk prostor na okldn dat. Pod FP opt nen problm, pod TP7 budeme asi potebovat vyuvat opt XMS. Budeme toti postupovat nsledovn: peteme pixel z VVRAM a zjistme, zda danou barvu u v tabulce mme. Pokud ne, pidme ji, pokud ano, zvme jej vskyt. Pro sporu msta je vhodn vyuvat barvy typu HC, protoe uetme 1 byte na barvu. Pak budeme moci dt vskyt WORD a tm budeme mt kadou poloku zarovnanou na DWORD. Pklad uvedu pro FP, pro TP7 si muste tabulku udlat v XMS a penet ideln vdy jen ty 2-4 byty tam a sem (nebo si pokat na lep metodu). Pod TP7 bychom tabulku dynamicky zvtovali, tj. bychom uloili hodnotu barvy a pot jej slo, zvili poet barev. Pi ptm vkldn bychom prohledvali u jen tolik barev, kolik bychom jich tam mli. Jednodu ale je, pokud si to meme dovolit (ono nm ani nic jinho nezbyde, pokud budeme potat s tm, e nm tabulka me stejn nabobtnat a na 65536 barev) udlat tabulku velkou 128 kB a mt v n jen vskyty barev, piem hodnota barvy se vezme jako index. To nm uet spoustu asu s prohledvnm:


var	Tabulka : array[0..65535] of word;
	x,y : word;
begin
 FillChar(Tabulka,SizeOf(Tabulka),0);
 for y := 0 to MaxY-1 do
  for x := 0 to MaxX-1 do
   if Tabulka[tc2hc(VVRAM[y,x])] < 65535 then
    Inc(Tabulka[tc2hc(VVRAM[y,x])]);
end;  

Dobe, te mme barvy. Co s nimi? Provedeme jejich tdn. Jakou metodu si zvolte nechm na Vs, j pouiji teba Bubble Sort. Protoe jsme si ale na zatku uetili as s ukldnm hodnoty barvy do tabulky, nememe nyn jen tak setdit tabulku, protoe bychom pak nevdli, kter vskyt patil kter barv. Musme proto udlat dal tabulku, kam nejprve ulome sla barev (resp. i jejich hodnoty), a tuto tabulku budeme tdit souasn.


var	Zmena : boolean;
	index : word;
	Prac : word;
	Barvy : array[0..65535] of word;	{toto v TP7 neudlte :-(}
begin
 for index := 0 to 65535 do
  Barvy[index] := index;
 repeat
  Zmena := False;
  for index := 0 to 65534 do
   if Tabulka[index] < Tabulka[index+1] then
   begin
    Prac := Tabulka[index];
    Tabulka[index] := Tabulka[index+1];
    Tabulka[index+1] := Tabulka[index];
    Prac := Barvy[index];
    Barvy[Index] := Barvy[Index+1];
    Barvy[Index+1] := Prac;
    Zmena := True;
   end;
 until not Zmena;
end;  

Tabulku te mme setdnou (nejvce se vyskytujc barvy jdou prvn), a nyn zjistme, kolik v n je barev (dokud nenarazme na vskyt = 0 nebo na konec tabulky), jak je prmrn vskyt, a kter barva (index) tomuto prmru piblin odpovd.


var	Barev : word;
	Prumer : word;
	Mez : word;
	Soucet : longint;
begin
 Soucet := 0;
 Barev := 65535;
 for index := 0 to 65535 do
  if Tabulka[index] <> 0 then
   Inc(Soucet,Tabulka[index])
    else
     begin		{pedpokldm, e tabulka nen przdn}
      Barev := Index;
      Break;
     end;
 if Barev > 256 do
 begin
  Prumer := Soucet div longint(Barev);
  for index := 0 to Barev-2 do
   if (Tabulka[index] >= prumer) and
      (Tabulka[index+1] < prumer) then
      begin
       Mez := index;
       Break;
      end
       else if index = Barev-2 then
        Mez := Barev-1;
 end; 
end;  

Te vme, kter barva je prmr vech vskyt. Nyn peteme 256 barev v pomru 192:64 z obou oblast. Pro takto divn? 192 odstn vezmeme z tch barev, kter se vyskytuj nejastji, take budou mt vce monost. Ale nesmme zanedbat ani ty odstny, kter nejsou tak ast, ale jsou (jinak bychom o n pili). Tch ale vezmeme mn. Pokud je barev v tabulce 256 a mn, nemusme nic potat, prost pevezmeme celou tabulku (pozor! musme pevzt barvy z tabulky BARVY, ne poty vskyt, a musme je uloit normln od zatku, tj. prvn barvu z tabulky dme do PALETA[0], neobsazen barvy na konci vymaeme na $ff; toto pak pome naemu ve uvedenmu algoritmu ve hledn). Nyn tedy zjistme, po kolika barvch musme barvy z tabulky natat:


var	ViceSkok,MeneSkok : word;
begin
 ViceSkok := Mez div 192;
 if ViceSkok = 0 then Inc(ViceSkok);
 MeneSkok := (Barev-Mez) shr 6;
 if MeneSkok = 0 then Inc(MeneSkok);
end;  

A te u jen budeme st barvy z tabulky a hzet je do palety. Musme samozejm pevst HC na TC a otoit poad sloek (u VGA palety je poad RGB, zatmco my mme pixely BGR, u VESy je to natst stejn, jen tam je o 1 byte na barvu navc). Pro tento el si zavedeme jet pr funkc, kter nm vyseparuj RGB sloky:


function R(pixel : longint) : byte;
asm  
	mov	al,pixel+2.byte  
end;


function G(pixel : longint) : byte;
asm  
	mov	al,pixel+1.byte  
end;


function B(pixel : longint) : byte;
asm  
	mov	al,pixel.byte  
end;  

A te vytvome svou adapativn paletu:


var	Pixel : longint;
	pozice : byte;
begin
 pozice := 255;
 for index := 0 to 191 do
 begin
  Pixel := hc2tc(Barvy[index*viceskok]);
  paleta[pozice].r := r(pixel);
  paleta[pozice].g := g(pixel);
  paleta[pozice].b := b(pixel);
  Dec(pozice);
 end;
 for index := 0 to 63 do
 begin
  Pixel := hc2tc(Barvy[index*meneskok+mez]);
  paleta[pozice].r := r(pixel);
  paleta[pozice].g := g(pixel);
  paleta[pozice].b := b(pixel);
  Dec(pozice);
 end;
end;  

Pro rychlej vpoet meme samozejm zavst dal promnnou, kter bude na zatku naplnna 0 nebo MEZem a budeme ji jen zvyovat pomoc INC o MeneSkok nebo ViceSkok (abychom nemuseli nsobit). Mme ale hlavn problm. Tuto legraci budeme toti muset provdt ped kadm kreslenm VVRAM do VRAM, protoe se nm tam mn pi nov zmn VVRAM (vtinou) podl barev. Jednodu een pro ns je provst toto pouze jednou a to po nahrn vech sprit, kter ve scn budou, do pamti. Pak vytvome tabulku nikoliv ze vech pixel VVRAM, ale ze vech pixel vech obrzk. V tomto ppad vytvome paletu (a nahrajeme ji do VGA karty) jen jednou.

Ale pro to dlat sloit, kdy to jde jednodue. Ukeme si jeden geniln trik, kter sice nevyuv adpativn paletu, ale doke i na prmrnm Pentiu provdt real-time (v relnm ase) pevod z TC na 256 barev a pitom obraz vypad skoro stejn jako pvodn TC obrzek. Zde jednodue vyuvme toho, e mme speciln vypotanou paletu a pak u jen bereme RGB sloky pmo jako index do tto palety (kter vypad takto: bbgg grrr). Geniln mylenky bvaj prost:


procedure VytvorPaletu;
var	r,g,b,i : byte;
begin
 R := 0;
 G := 0;
 B := 0;
 for i := 0 to 255 do
 begin
  paleta.r := r;
  paleta.g := g;
  paleta.b := b;
  Inc(b);
  if b = 4 then
  begin
   b := 0;
   Inc(g);
   if g = 8 then
   begin
    g := 0;
    inc(r);    
   end;
  end;
 end;
end;  

Samotn index pixelu pak vypotme nsledovn:


Index := word(R) and $e0+(word(G) shr 5) shl 2+word(B) shr 6;  

A v tom je cel ten zzrak. Nyn si u tedy mete vychutnat krsu TC na potach, kter pro to teba ani nemaj VRAM. Vhodou tto druh metody je, e paletu sta tak vytvoit jen jednou a cel pevod VVRAM pak stejn probh nezvisle na tom, co tam mte za data. Jednu nevhodu to samozejm m: pokud bude cel Vae VVRAM zbarvena jen do 1 odstnu (nap. modr), pijdete o vtinu detail a bude to vypadat stran (zde by tedy mla nastoupit adaptivn pomalej metoda). Ale pro normln fotky je to vce ne dobr.

Fajn, umme zobrazovat, ale nemme co. Meme kreslit obrazce pomoc pixel, ale jednodu asi bude, kdy si na to vytvome njak ty procedury. Naume se kreslit ry (vodorovn, svisl, ikm), tverce a obdelnky, krunice a elipsy, a tak se je naume vyplovat barvou i vzorem. Pouijeme pr trik, abychom urychlili nae vykreslovn. Zaneme nejprve s tm nejjednodum - ry. Tady asi nen moc, co bych ml vysvtlovat:


procedure Vodorovne(X1,X2,Y : word; Barva : BPP);
var i : word;
begin
 for i := X1 to X2 do
  PixelBPP(i,Y,Barva);
end;


procedure Svisle(X,Y1,Y2 : word; Barva : BPP);
var i : word;
begin
 for i := Y1 to Y2 do
  PixelBPP(X,i,Barva);
end;  

ru pro kreslen horizontln jde vrazn urychlit (v TP7 jen pro BPP=1, v FP pro vechna, ale muste vdy pout sprvn Fill):


procedure Vodorovne(X1,X2,Y : word; Barva : byte);
begin
 FillChar(VVRAM[X1,Y],X2-X1,Barva);
end;  

Kdy umme kreslit ry, tak umme kreslit i tverce a obdelnky:


procedure _4uhelnik(X1,Y1,X2,Y2 : word; Barva : BPP);
begin
 Vodorovne(X1,Y1,X2,Y1,Barva);
 Vodorovne(X1,Y2,X2,Y2,Barva);
 Svisle(X1,Y1,X1,Y2,Barva);
 Svisle(X2,Y1,X2,Y2,Barva);
end;  

Bylo by ale vhodn se nauit kreslit i ikm ry. Existuje spousta algoritm. My pouijeme ten (algoritmus nen mj), kter pot prstky rozdlu. Nebudeme ale pouvat reln sla (kvli rychlosti):


procedure Cara(X1,Y1,X2,Y2 : word; Barva : BPP);
var	DeltaX, DeltaY, NumPixels, Counter,
	D, Dinc1, Dinc2,
	X, Xinc1, Xinc2,
	Y, Yinc1, Yinc2 : Integer;
begin
	{rozdly v osch X a Y}
 DeltaX := abs(x2-x1);
 DeltaY := abs(y2-y1);
	{zkontrolujeme pesnost. Tm urme nezvislou promnnou}
 if (DeltaX >= DeltaY) then
 begin
    {X bude nezvisl promnn}
  NumPixels := Deltax+1;
  D := (DeltaY shl 1)-DeltaX;
  Dinc1 := DeltaY shl 1;
  Dinc2 := (DeltaY-DeltaX) shl 1;
  Xinc1 := 1;
  Xinc2 := 1;
  Yinc1 := 0;
  Yinc2 := 1;
 end else
  begin
	{Y bude nezvisl promnn}
   NumPixels := DeltaY+1;
   D := (DeltaX shl 1)-DeltaY;
   Dinc1 := DeltaX shl 1;
   Dinc2 := (DeltaX-DeltaY) shl 1;
   Xinc1 := 0;
   Xinc2 := 1;
   Yinc1 := 1;
   Yinc2 := 1;
  end;
	{Ujistme se, e X a Y se mn sprvnm smrem}
 if x1 > x2 then
 begin
  Xinc1 := -Xinc1;
  Xinc2 := -Xinc2;
 end;
 if y1 > y2 then
 begin
  Yinc1 := -Yinc1;
  Yinc2 := -Yinc2;
 end;
	{Zaneme kreslit na bodu X1 a Y1}
 X := x1;
 Y := y1;
 for Counter := 1 to NumPixels do
 begin
  Pixel(X,Y,Barva);
  if (D < 0) then
  begin
   inc(D, Dinc1);
   inc(X, Xinc1);
   inc(Y, Yinc1);
  end else
   begin
    inc(D, Dinc2);
    inc(X, Xinc2);
    inc(Y, Yinc2);
   end;
 end;
end;  

Trochu "sloitj" (i kdy krat) je vpoet krunice (algoritmus nen z m hlavy; u tch, co jsou, se ppadn omlouvm za peklepy a chyby, ale mli byste bt u na takov rovni, abyste si to byli schopni upravit, opravit a pizpsobit podle teorie). Ale zde je mon pomoci si tm, e je vlastn dokonale symetrick, take nm sta spotat si pouze 1/8 bod a ty ostatn promtneme pes osy X a Y, a zrove XY. Elipsa (algoritmus bohuel neznm) se kresl podobn (zkuste teba vyut Vae znalosti ze S z goniometrie a rovnice bodu na elipse), jen musme potat 2x tolik bod ne u krunice, protoe nen soumrn podle os XY, ale jen X a Y. Pstupy do VVRAM mete nahradit opt njakm tm pesunem do XMS, stejn tak si opt meme pedpotat opakujc se vpoty u X a Y. Jen upozoruji, e u nesymetrickch md, jako je nap. 320x200 (1.6:1) nebo 640x480 (1.33:1) nemus bt krunice kulat (pokud obrazovku "tpnete" do BMP a zobrazte nap. ve Windows, tak kulat na 99% bude), protoe pixely nejsou tvercov (proto budete muset kreslit spe elipsy). Je vhodn tak testovat, zda vykreslovan pixel nele mimo obrazovku (nebo nastaven vez).


procedure Kruznice(X,Y,R : word; Barva : BPP);
var	d,cx,cy : integer;
begin
 d := 3-(r+r);
 cx := 0;
 cy := r;
 while cx <= cy do
 begin
  VVRAM[y+cy,x+cx] := barva;
  VVRAM[y-cy,x+cx] := barva;
  VVRAM[y+cy,x-cx] := barva;
  VVRAM[y-cy,x-cx] := barva;
  VVRAM[y+cx,x+cy] := barva;
  VVRAM[y-cx,x+cy] := barva;
  VVRAM[y+cx,x-cy] := barva;
  VVRAM[y-cx,x-cy] := barva;
  inc(cx);
  if d < 0 then
   d := d+cx shl 2+6
    else
     begin
      d := d+(cx-cy) shl 2+10;
      dec(cy);
     end;
 end;
end;  

A kdy u mme hotov tvary (jedna perlika, pokud zmnte d<0 za d>0, dostanete kosotverec; prvnm SHL 2 mnte prohnut dovnit (m vt slo, tm se to bude podobat kosotverci se zaoblenmi rohy), druhm SHL 2 mnte vchylku ven (m vt slo, tm se to bude podobat tverci ze zaoblenmi rohy); pokud nahradte dvojice CX za CY nebo obrcen, dostanete z nkterch oblouk rovn ry a tedy i zajmav tvary). Meme se je nauit vyplovat. Tyto funkce funguj na principu zsobnku, kdy si ulo pixely, kter jsou vedle nich jet nevyplnn a dokud v zsobnku jet nco je a my stle mme jet dek pro plnn, opakujeme vykreslovn. Existuj dv varianty. Prvn hled pixely jen vedle sebe v osch XY. Je rychlej a tak se hod pro vyplovn krunic, elips a trojhelnk. Pomalej metoda, kter prohledv vech 8 os (tj. i ikmo) se nehod pro objekty, kter maj ikm ry (protoe "vytee" ven), ale pome u nkterch sloitjch objekt (to lze ale obejt prost tm, e vlote nkolik bod, odkud se zane vyplovat a pouijete "4"). Zsobnk muste udlat dostaten hlubok, aby u velkch i sloitch objekt stail (me bt poteba a destky kB!).


type	ZasSirka = word;
const	Vyska : ZasSirka = 1024;
var	Zasobnik : array[0..Vyska-1] of record
					 X,Y : word;
					end;
	Vrchol : ZasSirka;


procedure Vloz(X,Y : word);
begin
 if Vrchol = Vyska then Exit;
 Zasobnik[Vrchol].X := X;
 Zasobnik[Vrchol].Y := Y;
 Inc(Vrchol);
end;


procedure Vyjmi(var X,Y : word);
begin
 if Vrchol = 0 then Exit;
 X := Zasobnik[Vrchol].X;
 Y := Zasobnik[Vrchol].Y;
 Dec(Vrchol); 
end;


procedure Vypln4(X,Y : word; Okraje,Barva : BPP);
var l,r : word;
begin
 Vrchol := 0;
 repeat
  if Vrchol > 0 then Vyjmi(X,Y);
  l := x;
  while (l > 0) and (VVRAM[l,y] <> Okraje) do
  begin			{cyklus doleva}
   if y-1 > 0 then
    if VVRAM[l,y-1] <> Okraje then Vloz(l,y-1);
   if y+1 < MaxY then
    if VVRAM[l,y+1] <> Okraje then Vloz(l,y+1);
   Pixel(l,y,Barva);
   Dec(l);
  end;
  p := x+1;
  while (p < MaxX) and (VVRAM[p,y] <> Okraje) do
  begin			{cyklus doprava}
   if y-1 > 0 then
    if VVRAM[p,y-1] <> Okraje then Vloz(p,y-1);
   if y+1 < MaxY then
    if VVRAM[p,y+1] <> Okraje then Vloz(p,y+1);
   Pixel(p,y,Barva);
   Inc(p);
  end;
 until Vrchol = 0;
end;


procedure Vypln8(X,Y : word; Okraje,Barva : BPP);
var l,r : word;
begin
 Vrchol := 0;
 repeat
  if Vrchol > 0 then Vyjmi(X,Y);
  l := x;
  while (l > 0) and (VVRAM[l,y] <> Okraje) do
  begin			{cyklus doleva}
   if y-1 > 0 then
   begin
    if VVRAM[l,y-1] <> Okraje then Vloz(l,y-1);
    if l-1 > 0 then
     if VVRAM[l-1,y-1] <> Okraje then Vloz(l-1,y-1);
   end;
   if y+1 < MaxY then
   begin
    if VVRAM[l,y+1] <> Okraje then Vloz(l,y+1);
    if l-1 < MaxX then
     if VVRAM[l-1,y+1] <> Okraje then Vloz(l-1,y+1);
   end;
   Pixel(l,y,Barva);
   Dec(l);
  end;
  p := x+1;
  while (p < MaxX) and (VVRAM[p,y] <> Okraje) do
  begin			{cyklus doprava}
   if y-1 > 0 then
   begin
    if VVRAM[p,y-1] <> Okraje then Vloz(p,y-1);
    if p+1 > 0 then
     if VVRAM[p+1,y-1] <> Okraje then Vloz(p+1,y-1);
   end;
   if y+1 < MaxY then
   begin
    if VVRAM[p,y+1] <> Okraje then Vloz(p,y+1);
    if p+1 < MaxX then
     if VVRAM[p+1,y+1] <> Okraje then Vloz(p+1,y+1);
   end;
   Pixel(p,y,Barva);
   Inc(p);
  end;
 until Vrchol = 0;
end;  

Tyto procedury vypluj jednou barvou a to tm stylem, e vypln vechny pixely, kter nejsou urit barvy (OKRAJE) na barvu Barva. Mete si je samozejm upravit tak, aby fungovaly jen na barv, kterou urte (tedy budou vlastn nahrazovat barvu Okraje na Barvu ve svm nejblim okol) a to prostou zmnou pkazu <> na pkaz =. Meme si ale napsat i procedury, kter vykresluj plochu ne jednou barvou, ale njakm vzorkem. Aby ten drel na mst (tj. se rzn nerozjdl podle toho, jak moc je ikm lev i prav strana objektu, a odkud se zane vyplovat), budeme brt souadnice XY na obrazovce s uritou pravou jako souadnice v bufferu vzorku (ten je vlastn obyejn obrzek X*Y s BPP, jakou potebujete).


type	TVzor = array[0..15,0..15] of RGB;
var	Vzorek : TVzor;		{rozmry jako mocniny 2}


procedure Vypln4Vzor(X,Y : word; Okraj : BPP; Vzor : TVzor);
var l,r : word;
begin
 Vrchol := 0;
 repeat
  if Vrchol > 0 then Vyjmi(X,Y);
  l := x;
  while (l > 0) and (VVRAM[l,y] <> Okraje) do
  begin			{cyklus doleva}
   if y-1 > 0 then
    if VVRAM[l,y-1] <> Okraje then Vloz(l,y-1);
   if y+1 < MaxY then
    if VVRAM[l,y+1] <> Okraje then Vloz(l,y+1);
   Pixel(l,y,vzor[y and 15,l and 15]);
   Dec(l);			{mocnina dvou -> sta nm AND msto MOD}
  end;
  p := x+1;
  while (p < MaxX) and (VVRAM[p,y] <> Okraje) do
  begin			{cyklus doprava}
   if y-1 > 0 then
    if VVRAM[p,y-1] <> Okraje then Vloz(p,y-1);
   if y+1 < MaxY then
    if VVRAM[p,y+1] <> Okraje then Vloz(p,y+1);
   Pixel(p,y,vzor[y and 15,p and 15]);
   Inc(p);
  end;
 until Vrchol = 0;
end;  

Dobr, take umme kreslit 2D objekty. Ale chtli jste u nkdy kreslit i 3D? Nen nic snaho. Pro pepoet na 2D obrazovku se pouv vzoreek:


Xobr := (X * 512) DIV (Z + 512) + XobrMax div 2;
Yobr := (Y * 512) DIV (Z + 512) + YobrMax div 2;  

Jednodue do rovnic dosadte X,Y,Z (Z se vtinou pouv do 255) a vyjde Vm, na kter XY souadnice mte bod na obrazovku nakreslit. Mlo by tak platit, e m je bod dl, tm by ml bt tmav (pokud tam ovem nemte njak svj zdroj svtla, kter pipotete zlumen dle Z k jeho jasu podle toho, zda jeho Z le v oblasti, kde je svtlo). Rovnice lze samozejm upravovat dle efektu, kter chcete doshnout, take me vypadat teba i takto:


Xobr := (X shl 9) div Z + XobrMax shr 1
Yobr := (Y shl 8) div Z + YobrMax shr 1;  

Toto plat pro statickou kameru (resp. kamera, kter je ve stedu obrazovky). Pokud se m kamera hbat, muste upravit rovnice teba takto:


Xobr := ((X+Xcam)*Vzdal) DIV (Z+Vzdal)+XobrMax div 2
Yobr := -((Y+Ycam)*Vzdal) DIV (Z+Vzdal)+YobrMax div 2  

Vimnte si, e u Y souadnice je mnus. To je proto, jeliko souadnice na monitoru stoupaj odshora dol; ale pozici kamery na map vtinou vyjadujete tak, e Y=0 je dole, a smrem nahoru souadnice stoupaj. Vzdlenost me bt dle "oky" kamery, nap. 512 nebo nic (mete takto snadno doshnout i prothlho pohledu, kdy k Z nic pitat nebudete a budete zvyovat slo za nsobenm, resp. posunem vlevo). Kad bod XYZ mete brt jako st objektu. Pokud chcete nap. kreslit zdi jako u WOLFa 3D, sta Vm vdt, kde le dan bod na map a podle toho, kam se dvte (pokud se otote o 180 stup, tak muste tak zmnit souadnice objekt, co byly za Vmi (nebo si propotat souadnice svho pohledu), aby te leely jakoby ped Vmi, pokud jim nechte stejn XYZ, vyjdou nesmysly) to pepotat a pak pevst na XY. Pokud Vs to zajm, podvejte se do asopisu Vhe. Jen nutno podotknout, e pro kreslen her typu Prince of Persia (pseudo-3D) nebo Transport Tycoon (izometrie) nepotebujete tyto pevody, ale spe znalosti kreslen 3D technickch vkres, kdy zmente poet pixel na stranch, kter jsou jakoby 3D na 1/2 bu na Y (vrchn) nebo na X (krajn) jejich vynechnm, a kreslte je pod hlem 45 st., tedy vdy X+1 a Y+1.

Pokud ale chcete kreslit na obrazovku vce objekt, kter se budou zakrvat, nebo dokonce prolnat, asi narazte na problm, jak kreslit jednotliv body (jist, kreslit objekty od nejzadnjch po nejbli, ale co kdy jsou dva objekty propleten?). Mete si samozejm objekty uspodat, vyhodit ty strany, kter nejsou vidt (jsou od ns odvrcen), i ty, kter jsou zakryty cel jinm objektem, ale stle muste vykreslit body. Na toto se pouv tzv. Z-Buffer. Jedn se o pole, kter m stejn rozmry X*Y jako VVRAM (ale u nemus mt stejn BPP, protoe to bv vtinou 1 a 2 dle toho, jak me bt Z). Do Z bufferu se ukldaj, jak u nzev napovd, souadnice Z. Na zatku kadho vykreslovn ho naplnte max. hodnotami (255 pro byte) a vdy, kdy budete chtt kreslit bod, tak zjistte, zda jeho Z je men ne v to bufferu. Pokud ano, vlote jeho Z na toto msto a vykreslte jej. V opanm ppad jej "zahodte".


var	ZBuffer : array[0..MaxY-1,0..MaxX-1] of byte;  

A te pro kad obrzek provedete to, co je v nsledujcm pkladu (pokud je poteba dan pixel kreslit, tj. pokud jej ji nevyadila funkce testujc prhlednost). Kreslme obrzek Xobr*Yobr na souadnice Xv,Yv, a obrzek je dejme tomu natoen od 45 stup "dovnit" monitoru (ze). Pro kad bod musme Z njak vypotat (ideln si vypotejte Z prvnho a poslednho bodu, zjistte si rozdl, vynsobte 65536 (bez relnch sel potebujeme mt vt citlivost) a vydlte potem bod v obrzku (Yobr). Nastavte Z na Z bliho bodu. Nyn po peten kadho X zvyte njakou pracovn promnnou typu LONGINT o Zr*65536/X. Pokud bude pracovn promnn >=65536, zvyte Z o 1 a od t pracovn promnn odette 65536 (takto se nahrazuj sla 0.4, 0.8, 1.2-1 = 0.2 (Z+1), 0.6, 1.0-1 = 0 (Z+1), 0,4...).


for y := 0 to ObrY-1 do
 for x := 0  to ObrX-1 do
 begin
  ; vypoteme Z, pokud je teba obrzek natoen
  ; vypoteme souadnice Xo a Yo na obrazovce
  if z < zbuffer[yo,xo] then
  begin
   Pixel(yo,xo,barva);
   zbuffer[yo,xo] := z;
  end;
 end;  

Voala, a mme 3D prostor jak vyit. Te se u mete pustit do tvorby vlastnho Dooma (podlahy jsou to sam co zdi, jen se naklpj podle jin osy). Fajn, ale stle jet nemme co zobrazovat. Jasn, mme krunice a ry, ale kdo by dneska hrl hru na bzi drtnch model. Chtlo by to njak ty textury. Tak se naume st obrzky (a vytvome si i vlastn formt). Ale to a jindy :-)


*** POKRAOVN P͊T ***

2006-11-30 | Martin Lux