Ha valaki esetleg nem olvasta az előző nekifutásomat a témából, pótolja be mielőtt itt próbál tovább olvasni.

Addig jutottunk, hogy voltak ronda színű falaink, amik között lehetett mászkálni. Most a padló, a plafon, és a textúrázás lesz a téma.

Falak textúrázása

Természetesen azonos méretű textúrákat fogunk használni. Ez érvényes a későbbi padlóra is. Az a legegyszerűbb, ha 64*64 -es a méret, mivel ennyi volt a falak mérete egységekben.
Ahogy az egész kép felépítése, a texture mapping is oszlopról oszlopra halad. A fal egy képernyőoszlopának a tetején kezdjük, és az alján végzünk. Közben persze lineárisan interpolálunk. Ahogy a mellékelt ábra is mutatja:


Új problémát csak a textúrakoordináták (U,V) kiszámolása jelent. Ezt pedig minden egyes képernyőoszlopra meg kell tennünk, ahova esik fal. A V kiszámolása nem nagy ügy, mivel a fal teteje mindig V=0, a fal alja pedig V=64. Szóval a fal teteje a textúra tetejére esik, az alja pedig az aljára. Mivel a falak keresésénél eltároltuk a kibocsátott sugár és a fal metszéspontját, tudjuk, hogy a fal melyik részét érintette. Ebből egyszerűen kiszámolható az oszlop U koordinátája is. X irányú falak esetén a metszéspont X koordinátája határozza meg, Y irányú falak esetén pedig az Y. Egyszerűen a 64-el való osztás maradékaként megkapott érték (egyszerűbben az alsó 5 bit, 63-al and-elünk ) lesz az U.
Ha valaki nem érti:


Tehát Y falak esetén U=Y and 63, X irányúak esetén U=X and 63.

Persze ha kétszer ekkora a textúra (128X128), (ahogy a példaprogramban is) akkor egyszerűen felszorozzuk kettővel a kiszámolt értékeket.

Ha megvannak a textúrakkordináták, csak ki kellene rajzolni azt a képernyőoszlopot. Mivel a falak függőlegesek, végig ugyanaz az U, csak a V-t kell léptetnünk 0 tól 64-ig. Tehát a V lépésköz 64/H, ahol H az éppen rajzolt képernyőoszlop magassága. Ezt persze fixpontosan kell számolni, mondjuk 8.8-ban, ezért szorozzuk fel 256-al a lépésközt.
DeltaV=16384/H

Ha esetleg a textúra kétszer ekkora (128X128), akkor szorozzuk meg kettővel a lépésközt.

Így működik a texture mapping egy képernyőoszlopra 128X128-as textúrával pascalban:

{fuggoleges texturazott vonal kirajzolasa}
procedure WallTexLine(x,start,stop,U : integer;n : byte);
var Y : integer;
    DeltaV,V : integer;
    tex : ^Ttexture;
begin
      if (stop=<start) then exit;
      if stop<0 then exit;

      tex:=@textures[n];

      DeltaV:=32768 div (stop-start); {8.8 fix pontos}
      V:=0;

      {clipping}
      while start<0 do
      begin
            V:=V+DeltaV;
            start:=start+1;
      end;
      if stop>YMAX+1 then stop:=YMAX+1;

      {egy oszlop kirajzolasa}
      for Y:=start to stop-1 do
      begin
            screen[Y,X]:=tex^[V shr 8,U];
            V:=V+DeltaV;
      end;
end;

A rutin megnézi azt is, hogy a megadott y koordináták belül vannak-e a képernyőn, és clipping-el.

Ha a bemutatott egyszerű módon számoljuk ki a texúrakoordinátákat, akkor jobb és bal oldalon, pont ellentétes irányban lesznek felfeszítve a falra. Ennek a dolognak feliratokat tartalmazó textúráknál igen zavaró a hatása. Hasonlóan fordítva lesznek mögöttünk, mint előttünk. Ezért tükrözni kell a textúrakoordinátát:

X falak esetén:
{ha lefele nezunk tukrozzuk a texturat}
 if (a>ANG_180) and (a<ANG_360) then U:=63-U;

Y falak esetén:
{ha balra nezunk tukrozzuk a texturat}
if (a>ANG_90) and (a<ANG_270) then U:=63-U;

Padló és mennyezet

A padlót egyszerűen a fal aljától a képernyő aljáig kell rajzolnunk. Ide kell leképezni. Némileg bonyolultabb a hogyan kérdése. Természetesen ez a művelet is oszlopról oszlopra történik, így a fal egy oszlopának kirajzolása után rögtön hozzáláthtunk a padló kirajzolásához. Pont ott kell elkezdeni, ahol a falat abbahagytuk.

  • Veszünk a képernyőpontot
  • Kiszámoljuk a padló ide leképezendő pontjának a távolságát.
  • A távolság segítségével megkapjuk a leképezendő pont világkoordinátáit
  • A világkoordinátákból megtudjuk a textúrakoordinátákat
  • Kirajzoljuk a pontot

A padló kirajzolása a falakhoz hasonlóan sugarak segítségével történik. Itt nem a falakat keressük, hanem a padlót:


Hasonló háromszögek alapján:


A metszéspont távolsága(Distance)        Néző magassága(1/2 része a falmagasságnak)
-------------------------------------- = ----------------------
Leképezési sík távolsága (HeightScale)   PY- Képernyőközép

Ebből, kicsit magyarítva:

Distance=(HeightScale/2)/(Y-100)

Kis magyarázat. A HeightScale nevű állandót már kiszámoltuk a falak leképezéséhez. Ez az állandó tartalmazza a leképezési sík távolságát. Ezt osztani kell kettővel, mivel a szemlélő magassága pont fele a fal magasságának. Ezt az értéket pedig osztjuk az Y és a Horizont (ami a képernyő felénél van) különbségével. Ezeket az értékeket táblázatba rendezve kapásból tudni fogjuk a távolságot minden képernyősorra.
A plafon számítása ugyanígy történik, egy táblázatot oda is tudunk használni, vagy ugyanezt tükrözzük.
Pl..:

     {a felet kell venni annak, amint a falak magassaganak kiszamolasakor hasznalunk}
      HeightScale:=HeightScale shr 1;

      {milyen messze vannak az egyes kepernyosorokra eso 3Dponotok a szemlelotol}
      for i:=0 to YHALF-1 do FloorDistT[i]:=trunc(HeightScale/(YHALF-i)/TABMUL);
      FloorDistT[YHALF]:=MaxLongint; {elmeletileg vegtelen tavolsag lenne}
      for i:=YHALF+1 to YMAX do FloorDistT[i]:=trunc(HeightScale/(i-YHALF)/TABMUL);

A programrészlet minden egyes képernyősorra kiszámolja a távolságot. A 0-99-ig a plafon 100-199-ig a padlóra vonatkozóan.

Ha megvan a távolság, abból ki kell számolni, hogy a padló melyik pontját látjuk. Ehhez szükségünk van arra is, hogy merre nézünk, illetve, hogy mennyi a képernyőoszlopra tartozó sugár szöge.

Az 1/cos értékeket persze kirakjuk táblázatba. Így Dist=Distance*InvCos.
A textúrapont két világkoordinátáját akkor fogjuk megkapni a kiszámolt távolságból (Dist), ha vesszük a nézet szöget.

WX = Dist*cos(a)
WY = - Dist*sin(a)

Ezekből a textúrakoordinátákat már olyan egyszerűen megkapjuk, mint a falnál. Csak az alsó 5 bitet kell venni. (AND elni 63-al).
U = WX and 63
V = WY and 63

Az egyszerűség és gyorsaság kedvéért ugyanezeket a textúrakoordinátákat fogadjuk el a plafonra is, persze ott másik textúrára értelmezve. A plafont és a padlót egyszerre rajzoljuk, így az Y képernyőkoordinátát csak tükrözzük a horizontra. A Rutin 128X128-as textúrára:

{Padlo es plafon egy vonalanak kirajzolasa}
{erossen de-optimalizalt!!!}
Procedure FloorCeilingTexLine(x,h,a,b : integer; fn,cn : byte);
var wx,wy,distance : longint;
    U,V : integer;
    fy, cy : integer;
    ftex,ctex : ^Ttexture;
begin
      cy:=YHALF-(H shr 1);
      fy:=YHALF+(H shr 1);
      if (fy>YMAX) or (cy<0) then exit;

      ftex:=@textures[fn];
      ctex:=@textures[cn];

      while fy<=YMAX do
      begin
            {A padlo pontjanak tavolsaga}
            distance:=(FloorDistT[fy]*InvCosT[b]) shr (TABSHIFT);
            {A vilagkoordinatak}
            wx:=(distance*CosT[a] shr TABSHIFT)+Px;
            wy:=-(distance*SinT[a] shr TABSHIFT)+Py;
            {A texturakoordinatak}
            U:=wx and 63;U:=U+U;
            V:=wy and 63;V:=V+V;
            screen[fy,x]:=ftex^[V,U];    {padlo}
            screen[cy,x]:=ctex^[V,U];    {plafon}
            fy:=fy+1;
            cy:=cy-1;
      end;
end;

Még számtalan gyorsabb módja lehetne, de ez a legegyszerűbb, és annyira nem lassú.

Lásd a példaprogramot. forrkod.zip