-•- Texture mapping -•- Hráli jste někdy Wolfenstein 3D nebo jeho méně známé pokračování Spear of Destiny ? To je ale blbá otázka, vždyť Wolfensteina pařil každý až do skonání fyzických sil. Jeho tvůrci ID software sklidili tenkrát veliký úspěch a obdiv. Starej dobrej Wolf se totiž docela obstojně hejbal i na obyčejný 286ce a to se fakt jen tak nevidí. Asi si kladete otázku, jak je to možné. Celý 3D engine Wolfensteina stojí na tzv. DDA wolf-mappingu. Pro začátek se vám pokusím vysvětlit jeho princip. Aby lidský mozek dostal pocit, že se pohybuje v nějakém virtuálním prostoru, je třeba jeho oči omámit velikým počtem různobarevných bodů. Na počítači dosáhneme realistického pohybu texturovaných ploch při rychlosti alespoň 25 obrázků za sekundu. Na pomalých počítačích se takovéto rychlosti i u čistých vektorových objektů dosahuje velmi špatně, a to ani nemluvím o texturách. Ale Wolf se skutečně přibližoval k 20 obrázkům za sekundu i na 286ce. Celé tajemství této ďábelské rychlosti se dá odůvodnit minimalizováním zobrazovaných textur za jeden frámeček. Ve Wolfovi se vykreslují pouze zdi, podlaha a strop jsou vyplněny konstantní barvou. Wolf-mapping není náročný, jak na výpočet, tak na pochopení, a proto si ho můžete zkusit naprogramovat sami. Základní rutinou je DDA algoritmus, který se mimo jiné používá například na kreslení čar. Princip je jednoduchý: /[x2,y2] / / / / / [x1,y1] tato úsečka se dá vyjádřit matematicky tímto způsobem: y = kx + b y2-y1 x2y1-x1y2 kde k je k = ------- a b je b = ------------ x2-x1 x2-x1 V praxi si předpočítáme k a b. Následuje cyklus podobný tomuto: for x:=x1 to x2 do begin y:=round(k*x+b); putpixel(x,y); end; na obrazovce by se měla objevit čára z bodu [x1;y1] do bodu [x2;y2] Nyní si představte texturu: [x1;y1] |-----------| | . . . . . | | . . . . . | | . . . . . | | . . . . . | | . . . . . | |-----------| [x2;y2] a potřebujete ji rotovat například podle osy z, použijete tedy stejný způsob, jaký jsem načrtl v mém minulém příspěvku ve Výhni #1. Výpočtem získáte souřadnice čtyř bodů a co teď ? Jednoduše si představte horní a dolní okraj textury jako čáry a podle výše uvedené rutiny vytvořte tento magický cyklus: [x1;y1] --- |. --- |.. . --- |.. . . --- [x2;y2] |.. . . . ..| |.. . . . ..| |.. . . . ..| |.. . . --- [x2;y4] |.. . --- |. --- --- [x1;y3] k1:=(y2-y1)/(x2-x1) b1:=(x2y2-x1y2)/(x2-x1) k2:=(y4-y3)/(x2-x1) b2:=(x2y3-x1y4)/(x2-x1) repeat y1:=round(k1*x1+b1); y3:=round(k2*x1+b2); inc(x1); textureline(x1,y1,y3); until x1=x2 Kdyby jste měli procedůrku textureline, tak by to všechno fachalo, tak jak má, ale vy ji asi nemáte, a tak mi nezbývá nic jiného, než vám její princip také osvětlit. Pro názornost další ansi obrázek: 1. =-=-=-=-=-=-=-=-=-=-=-=-= 2. ============ takhle bude asi vypadat první a poslední sloupec pixelů textury otočený o 90 stupňů. My ho potřebujeme smrštit z původní délky tak, aby se nám vešel mezi souřadnice y1 a y3. Jak už jistě tušíte, zase přichází na řadu algoritmus pro kreslení čáry: y3-y1 k = --------- kde h je výška textury h pro tentokrát to bude trochu složitější, ale magie to není: k:=h/(y3-y1); for y:=y1 to y3 do begin testY:=k*y; if frac(testY)=0 then putpixel(x1,testY,texture[xc,y-y1]) end; huu ...:-) hned podám vysvětlení ... na začátku spočtu k a začínám cyklus od pozice y1, do proměnné testY uložím současný stav k*y, a pokud se jedná o celé číslo, vykreslím na souřadnice [x1;testY] bod z textury (xc - sloupec textury - závisí na předchozím cyklu). Pokud by nastala situace, kdy (x2-x1) je větší nebo menší než délka textury, stačí pouze přepsat cyklus textureline pro x-ové souřadnice a místo výstupu bodu na obrazovku, přiřadit do proměnné xc aktuální sloupec textury. Takhle to celé vypadá přibližně v praxi: Procedure TextureLine(X,Y1,Y2:Integer; Texture:Pointer; THeight:Word); { --- viz výše --- } Procedure DrawTexture(X1,Y1,Y2,X2,Y3,Y4,TX,TY:Word); var X,deltaX,deltaHiY,deltaLoY : Integer; koefLoY,koefHiY,koefX : Real; begin deltaX:=X2-X1-1; if deltaX=0 then deltaX:=1; deltaHiY:=Y1-Y3; deltaLoY:=Y4-Y2; koefLoY:=deltaLoY/deltaX; koefHiY:=deltaHiY/deltaX; koefX:=TY/deltaX; X:=0; repeat TextureLine(X+X1,Y1-Round(koefHiY*X),Y2+Round(koefLoY*X), Texture+(TX*Round(X*koefX)),TX); if deltaX>0 then Inc(X) else Dec(X); until X=deltaX; end; Výsledek můžete shlédnout stejně jako v minulém čísle Výhně, pokud spustíte intro3.exe. Zcela analogicky se dá vytvořit i textura podlahy. Tak teď je to snad každému jasné. Ohledně Wolfenstenia 3D se dá ještě podotknout to, že kromě principu nemá jeho algoritmus s uvedenými příklady nic společného. Je kompletně přepsán do assembleru a využívá mnoha předpočítaných tabulek pro dosažení maximální rychlosti. Doufám, že se pokusíte něco udělat. Pokud by jste měli ňáké problémy nebo vás zajímá obecný texture-mapping polygonů (v podstatě jde o propojení horizontálního a vertikálního algoritmu), můžete se obrátit na mě, já se pokusím případné dotazy zodpovědět. ReDox P.S.: Po dohodě mohu poskytnout assembleroidní zdrojáky k procedůrce textureline. výheň