<#KEYWORDS:[WORDS:<Grafika 3D oglnie. PART II >]#><#SCROLLS:[BYLINE:1][BYPAGE:1][BYTWOPAGES:0]#><#JLIMIT:[C1LMARGIN:10][C1RMARGIN:630][C2LMARGIN:10][C2RMARGIN:300]#>
<#JUSTIFY:[TYPE:2]#><#FONT:[BOLD:1][FACE:<arial.ttf>][AALIASED:1][SIZEX:12][SIZEY:16][COLOR:0xFFFFFF40]#>Grafika 3D oglnie. PART II
<#FONT:[FACE:<arial.ttf>][BOLD:0][SIZEX:10][SIZEY:12 ][COLOR:0xFFFFFFFF]#><#TITLE:[CAPTION:<Grafika 3D oglnie. PART II>][AUTOSCROLL:1]#>
    
    Witam po raz kolejny. Jeli kojarzycie poprzedni odcinek <#FONT:[COLOR:0xFF6B8DFD]#>"porad mieciowego kodera"<#FONT:[COLOR:0xFFFFFFFF]#>, to pewnie pamitacie, e zajmowalimy si w nim podstawami grafiki 3d, czyli perspektyw, wypenianiem trjkta i liczeniem dla niego owietlenia. Obiecaem, e nastpnym razem napisz co nieco o texturowaniu, korekcji perspektywy i clippingu. Mam nadzieje, e uda mi si w tym artku <#FONT:[COLOR:0xFF6B8DFD]#>"wykoczy"<#FONT:[COLOR:0xFFFFFFFF]#> problematyk podstaw grafiki trjwymiarowej i ju od nastpnego razu bdziemy mogli zaj si usprawnianiem podstawowych wzrw, ktre za chwil przedstawi.

  <#FONT:[COLOR:0xFFEDEF61]#>TEXTUROWANIE.<#FONT:[COLOR:0xFFFFFFFF]#>
    
    Ostatnio skoczylimy na goraudzie i phongu. Pierwsza z tych metod polegaa na interpolacji na caym trjkcie danej wartoci <#FONT:[COLOR:0xFFB8AA98]#>(tu: koloru)<#FONT:[COLOR:0xFFFFFFFF]#>, przy uyciu staej delty. Podobnie dzieje si w przypadku teksturowania. Co prawda sposb przeze mnie przedstawiony jest jak najbardziej podstawowym i jak najbardziej wolnym, nie mniej jednak nie moemy zabra si za optymalizacj czego, czego nie znamy. Tak wic, chcc oteksturowa obiekt musimy kademu wierzchokowi przypisa pewien koordynat tekstury <#FONT:[COLOR:0xFFB8AA98]#>(u, v)<#FONT:[COLOR:0xFFFFFFFF]#>. Nastpnie wartoci te interpolujemy po caym trjkcie uywajc metody identycznej do tej z gorauda. Potem pobieramy kolor punktu z tekstury o wsprzdnej u1, v1 <#FONT:[COLOR:0xFFB8AA98]#>(wyliczone wartoci)<#FONT:[COLOR:0xFFFFFFFF]#> i wrzucamy na ekran. Jak wida, metoda ta jest straaasznie wolna i nawet przy zastosowaniu staej delty zera strasznie duo mocy obliczeniowej. Wanie dlatego wymylono mnstwo sposobw na zoptymalizowanie tego procesu. Ale o tym w nastpnym artku. 

     Teoretycznie tak wypeniony trjkt moglibymy zostawi ju w spokoju, ale jak si okazuje to co widzimy na ekranie niekoniecznie musi by mie dla oka, a ju na pewno nie zamierzone. Dlaczego? Ot dlatego, e trjkty rysujemy z zastosowaniem perspektywy i o ile interpolowane wartoci koloru jako to znosz, o tyle koordynaty tekstur nie zachowuj si tak jak powinny. Rysowane trjkty w zdecydowanej wikszoci nie s prostopade do ekranu, wanie dlatego przy wyliczaniu wartoci u i v musimy uy metody zwanej korekcj perspektywy. Polega ona na tym, e poza interpolowaniem wartoci u, v na trjkcie, uzaleniamy je jeszcze od wartoci <#FONT:[COLOR:0xFF00FF00]#>z<#FONT:[COLOR:0xFFFFFFFF]#>, czyli odlegoci punktu od obserwatora. Uff... wydaje si zagmatwane, ale wcale tak nie jest. W praktyce wyglda to tak, e nie interpolujemy po trjkcie wartoci u i v, tylko odpowiednio:

    u1 = u / z
    v1 = v / z

gdzie z to po prostu z danego wierzchoka, a u i v to koordynaty tekstur na tyme. Do tych dwu wartoci dochodzi jeszcze trzecia:

    z1 = 1 / z

ktra te jest interpolowana po caym trjkcie. I teraz dla kadego punktu na trjkcie liczymy odpowiednio u i v tekstury w nastpujcy sposb.

    u2 = u1 / z1
    v2 = v1 / z1

Mamy teraz u2, v2 - czyli wsprzdne tekstury dla danego punktu trjkta. Jak wida nie jest to tak strasznie zagmatwane. Jest za to bardzo <#FONT:[COLOR:0xFF6B8DFD]#>"mocoerne"<#FONT:[COLOR:0xFFFFFFFF]#>, bo nawet po zamianie dzielenia na mnoenie daje to nam dwa mnoenia + dzielenie, dlatego mona np. liczy wartoci u i v w inner loopie <#FONT:[COLOR:0xFFB8AA98]#>(czyli linii poziomej trjkta)<#FONT:[COLOR:0xFFFFFFFF]#> co 8 bd 16 punktw, a potem interpolowa je liniowo. Moemy te oczywicie teksturowa trjkty bez korekcji perspektywy, ale wtedy musimy liczy si z tym, e scenka nie bdzie wygldaa zbyt ciekawie. 

    Dochodzi jeszcze jedna sprawa. Jak poczy cieniowanie <#FONT:[COLOR:0xFFB8AA98]#>(owietlenie)<#FONT:[COLOR:0xFFFFFFFF]#> z teksturowaniem. Dawniej, w trybach paletowych, byo to bardzo trudne do zrobienia bez ustawienia odpowiedniej palety. Dzi cay problem polega na wymnoeniu jednej wartoci przez drug, czyli:

    col = tex * light

    gdzie:

    col - kolor punktu na ekranie (16, 24, bd 32 bit)
    tex - kolor pobrany z tekstury
    light - warto natenia wiata z przedziau (0,1)

Przy czym pamita musimy, aby przy trzech skadowych RGB, wymnaa przez ostatni warto kady kolor osobno.

    <#FONT:[COLOR:0xFFEDEF61]#>Z - BUFFER<#FONT:[COLOR:0xFFFFFFFF]#>

    No tak. Wspomniaem w poprzednich dwch dziaach o interpolacji wartoci koloru i koordynatw tekstur po caym trjkcie, a tu dochodzi nam trzecia warto, ktr musimy rwnomiernie <#FONT:[COLOR:0xFF6B8DFD]#>"rozpracowa"<#FONT:[COLOR:0xFFFFFFFF]#> na caej dugoci i szerokoci trjkta. Mowa o wartoci Z. Po co nam ona? Ano po to, e podczas rysowania trjktw musimy sprawdza, ktre z nich s widoczne, ktre czciowo zasonite, a ktre przysaniaj nam inne. Mwic krtko, musimy posortowa trjkty tak, eby to co z tyu nie wyazio nam na przd :). eby nam si to udao, naley uporzdkowa trjkty od najbliej lecego do najdalej oddalonego od obserwatora. Nie pisz tu, eby uporzdkowa wartoci Z rosnco, bd malejco, poniewa zaley to od ukadu wsprzdnych, jaki sobie przyjmiecie w swoim silniku. Generalnie zakada si, e wraz z oddaleniem od obserwatora wartoci z ulegaj zwikszeniu. Ale ju np. w <#FONT:[COLOR:0xFF00FF00]#>OpenGL'u<#FONT:[COLOR:0xFFFFFFFF]#> o Z skierowana jest w stron obserwatora, co oznacza, e dodatnie wartoci znajduj si poza obszarem widzenia <#FONT:[COLOR:0xFFB8AA98]#>(za kamer)<#FONT:[COLOR:0xFFFFFFFF]#>, a im mniejsze z tym dalej obiekt od obserwatora. Aby posortowa trjkty moemy po prostu zsumowa wartoci wsprzdnych z kadego z wierzchokw, a nastpnie na tej podstawie zrobi sortowanie. Po tym wszystkim moemy ju rysowa trjkty od najdalszego do najbliszego, majc pewno, e te <#FONT:[COLOR:0xFF6B8DFD]#>"z tyu"<#FONT:[COLOR:0xFFFFFFFF]#> i tak bd przykryte przez te <#FONT:[COLOR:0xFF6B8DFD]#>"z przodu"<#FONT:[COLOR:0xFFFFFFFF]#>. Jest to pewne rozwizanie, ale skrajnie nieefektywne, poniewa rysujemy nawet te cianki, ktre w finalnej wersji nie s widoczne, co bardzo spowalnia dziaanie engine'u. Lepszym sposobem sortowania trjktw co do odlegoci, jest rysowanie <#FONT:[COLOR:0xFF6B8DFD]#>"od przodu do tyu"<#FONT:[COLOR:0xFFFFFFFF]#>, czyli najpierw rysowanie trjktw znajdujcych si najbliej, a potem coraz dalej, przy czym na kadym punkcie trjkta sprawdzamy czy jest on dalej czy bliej od tego, ktry jest ju na ekranie w tym miejscu. Wanie do tego potrzebna jest nam interpolowana warto z. Majc j moemy podczas rysowania sprawdzi w odpowiednim buforze, odpowiadajcym wielkoci naszemu buforowi ekranu, czy powinnimy zastpi dany pixel, znajdujcy si <#FONT:[COLOR:0xFF6B8DFD]#>"na ekranie"<#FONT:[COLOR:0xFFFFFFFF]#> czy te nie. Jeli to co mamy narysowane jest bliej od tego, ktry wanie sprawdzamy, to nie rysujemy nic, jeli natomiast okae si, e rysowany pixel jest bliej od tego, ktry jest na ekranie, to zastpujemy pixel nowym, a w buforze z wartociami Z wpisujemy now warto. Wanie ten bufor, zwany Z - <#FONT:[COLOR:0xFF00FF00]#>BUFFEREM<#FONT:[COLOR:0xFFFFFFFF]#> bd te <#FONT:[COLOR:0xFF00FF00]#>DEPTH BUFFEREM<#FONT:[COLOR:0xFFFFFFFF]#> przechowuje wartoci o odlegoci kadego punktu znajdujcego si na ekranie od obserwatora <#FONT:[COLOR:0xFFB8AA98]#>(kamery)<#FONT:[COLOR:0xFFFFFFFF]#>. Wanie po to interpolujemy warto Z po trjkcie, eby DLA KADEGO PIXELA sprawdzi czy powinnimy go rysowa czy te nie.

    <#FONT:[COLOR:0xFFEDEF61]#>CLIPPING<#FONT:[COLOR:0xFFFFFFFF]#>

    Waciwie jest to problem, od ktrego powinno si zacz piszc wasny engine. Dlaczego? Ano dlatego, e jeli okae si, i rysowany trjkt wychodzi poza ekran, to wszystko nam si skaszani. Aby temu zapobiec musimy sprawdzi, czy dany trjkt jest przez nas widoczny, a jeli cho troch wychodzi poza ekran, naley go <#FONT:[COLOR:0xFF6B8DFD]#>"obci"<#FONT:[COLOR:0xFFB8AA98]#> (clip)<#FONT:[COLOR:0xFFFFFFFF]#>. Na samym pocztku musimy zdefiniowa sobie tzw. <#FONT:[COLOR:0xFF6B8DFD]#>"piramid widzenia" <#FONT:[COLOR:0xFFB8AA98]#>(z angielskiego to bdzie "fustrum")<#FONT:[COLOR:0xFFFFFFFF]#>. Nastpnie sprawdzamy, czy dany trjkt naley do naszej piramidy i jeli tak doczy go do listy rysowanych trjktw. Jeli tylko czciowo naley on do niej, to doczamy go do listy trjktw, na ktrych zastosujemy jeszcze clipping 2D. Aby sprawdzi czy ciana mieci si w naszym <#FONT:[COLOR:0xFF6B8DFD]#>"fustrumie"<#FONT:[COLOR:0xFFFFFFFF]#> moemy zastosowa algorytm Sutherlanda - Hodgmana, ktrego nie bd tu opisywa, bo zrobi to ju Biki w trzecim Dragonie <#FONT:[COLOR:0xFFB8AA98]#>("Obcinanie 3D")<#FONT:[COLOR:0xFFFFFFFF]#>. Nie ukrywam, e samemu przerabiajc ten temat korzystaem z pomocy wspomnianego tekstu, wic mgbym po prostu przepisa go stamtd, tylko po co? Zainteresowanych odsyam do Dragona.

    Innym sposobem na sprawdzenie, czy dany trjkt jest do narysowania czy do wyrzucenia, jest po prostu zrzutowanie go przez perspektyw i sprawdzenie, czy jego wartoci mieszcz si na naszym ekranie. Jeli nie to wywalamy, jeli tylko czciowo to stosujemy clipping 2D. To jest ju stosunkowo proste, gdy polega na odpowiednim zmodyfikowaniu procedury rysujcej trjkt. Jeli trjkt ucinamy od gry, to po prostu pomijamy rysowanie kilku linii poziomych, jeli na dole - rysujemy bez kilku ostatnich linii. Jeli ucinamy z prawej to rysujemy punkty w linii poziomej tylko do okrelonej wartoci x, jeli z lewej - pomijamy wszystkie punkty a do okrelonej wartoci x. Prawda, e proste?

    Wracajc jeszcze do drugiego sposobu... przy jego pomocy moemy wywali wszystkie trjkty lece <#FONT:[COLOR:0xFF6B8DFD]#>"z boku", "na grze"<#FONT:[COLOR:0xFFFFFFFF]#> lub <#FONT:[COLOR:0xFF6B8DFD]#>"pod spodem"<#FONT:[COLOR:0xFFFFFFFF]#>. A co z trjktami lecymi <#FONT:[COLOR:0xFF6B8DFD]#>"w gbi"<#FONT:[COLOR:0xFFFFFFFF]#>? Po prostu sprawdzamy, czy ich wartoci mieszcz si w okrelonym przedziale <#FONT:[COLOR:0xFFB8AA98]#>(0,Z)<#FONT:[COLOR:0xFFFFFFFF]#>, przy czym wiksze od zera s zawsze <#FONT:[COLOR:0xFFB8AA98]#>(zakadajc, e wyrzucilimy wczeniej trjkty lece za kamer)<#FONT:[COLOR:0xFFFFFFFF]#>, wic sprawdzamy tylko czy ich wartoci s mniejsze od ustalonego Z, ktre moemy dobra sobie sami odpowiednio do potrzeb. A eby nie byo, e atwo to powiem Wam, e to jeszcze nie koniec paprania si z obcinaniem, bo po sprawdzeniu i wyrzuceniu trjktw <#FONT:[COLOR:0xFF6B8DFD]#>"z gbi"<#FONT:[COLOR:0xFFFFFFFF]#> moe si okaza, e scenka nagle nam si <#FONT:[COLOR:0xFF6B8DFD]#>"urywa"<#FONT:[COLOR:0xFFFFFFFF]#>. Aby temu zapobiec moemy zastosowa stopniowe <#FONT:[COLOR:0xFF6B8DFD]#>"przyciemnianie"<#FONT:[COLOR:0xFFFFFFFF]#> trjktw lecych dalej, czyli to, na co potocznie mwi si...

    <#FONT:[COLOR:0xFFEDEF61]#>MGA<#FONT:[COLOR:0xFFFFFFFF]#>

    Wydawa by si mogo, e jest to problem dosy zoony, ale jak si okazuje jest to jedna z najprostszych rzeczy do zakodowania. Musimy tylko do wartoci natenia wiata doda jeszcze jeden wspczynnik, ktry zaleny bdzie od odlegoci - znaczy si od z. I tak, definiujemy sobie dwie paszczyzny z, ktre bd oznaczay pocztek <#FONT:[COLOR:0xFF6B8DFD]#>"mgy"<#FONT:[COLOR:0xFFFFFFFF]#> i jej koniec <#FONT:[COLOR:0xFFB8AA98]#>(najczciej ta sama warto ktra okrela Zclip sceny)<#FONT:[COLOR:0xFFFFFFFF]#>. I teraz dla kadego wierzchoka lecego midzy tymi dwoma paszczyznami liczymy odpowiedni warto natenia <#FONT:[COLOR:0xFF6B8DFD]#>"mgy"<#FONT:[COLOR:0xFFFFFFFF]#> i sprowadzamy j do przedziau <#FONT:[COLOR:0xFFB8AA98]#>(0,1):<#FONT:[COLOR:0xFFFFFFFF]#>

    radius = (z - pocztek_mgy) / (koniec_mgy - pocztek_mgy)

Oczywicie <#FONT:[COLOR:0xFF00FF00]#>koniec_mgy - pocztek_mgy<#FONT:[COLOR:0xFFFFFFFF]#> wyliczamy zaraz na pocztku. Otrzymany radius dodajemy po prostu do wartoci natenia wiata dla kadego wierzchoka. Akurat w tym przypadku wraz ze zwikszaniem odlegoci mga bdzie coraz janiejsza, ale moemy przecie zrobi <#FONT:[COLOR:0xFFB8AA98]#>(1 - radius)<#FONT:[COLOR:0xFFFFFFFF]#> i mamy co zgoa innego. Moemy take zamiast interpolowa wartoci liniowo doda do kadej z nich okrelon warto, bd wyliczy sinus. Wszystko zaley od wyobrani.

    <#FONT:[COLOR:0xFFEDEF61]#>ENVIROMENT<#FONT:[COLOR:0xFFFFFFFF]#>
    
    O jejku. Ale mi si rozpisao. Okej, ostatnia rzecz na dzisiaj to prostu enviroment mapping - czyli <#FONT:[COLOR:0xFF6B8DFD]#>"mapowanie rodowiskowe"<#FONT:[COLOR:0xFFFFFFFF]#> czy jako tak. W naszym przypadku bdzie to troszk fake'owane. A zrobimy to tak... Wyobracie sobie kul. Zamy, e chcemy, aby odbio si w niej to, co stoi za nami i obok nas i potraktujmy to jako tekstur. Punkt kuli znajdujcy si najbliej nas bdzie odbija to co jest w centrum tekstury, a punkty znajdujce si <#FONT:[COLOR:0xFF6B8DFD]#>"po bokach"<#FONT:[COLOR:0xFFFFFFFF]#> odbija bd <#FONT:[COLOR:0xFF6B8DFD]#>"kracowe"<#FONT:[COLOR:0xFFFFFFFF]#> rejony tekstury. Jasne? Mam nadzieje, e tak :). A teraz zastanwmy si jak to si ma do naszego obiektu i od czego moemy uzasadni warto u i v tekstury. Zakadam, e o Z skierowana jest w stron obserwatora <#FONT:[COLOR:0xFFB8AA98]#>(jak w OpenGL'u)<#FONT:[COLOR:0xFFFFFFFF]#>. Mamy wic punkt najbliej nas. Warto wektora normalnego w tym punkcie wynosi <#FONT:[COLOR:0xFFB8AA98]#>(0,0,1)<#FONT:[COLOR:0xFFFFFFFF]#>. Punkty kracowe <#FONT:[COLOR:0xFFB8AA98]#>(gra, d i boki)<#FONT:[COLOR:0xFFFFFFFF]#> maj normale o wartociach <#FONT:[COLOR:0xFFB8AA98]#>(1,0,0), (0,1,0), (-1,0,0), i (0,-1,0)<#FONT:[COLOR:0xFFFFFFFF]#>. Mona z tego wywnioskowa, e koordynaty tekstur daj si uzaleni od wartoci wektorw normalnych. I tak:

    u = (normal.x + 1) / 2
    v = (normal.y + 1) / 2

W ten oto sposb moemy, <#FONT:[COLOR:0xFF6B8DFD]#>"odbi"<#FONT:[COLOR:0xFFFFFFFF]#> rodowisko na obiekcie. Jest to sposb <#FONT:[COLOR:0xFF6B8DFD]#>"oszukaczy"<#FONT:[COLOR:0xFFFFFFFF]#>, gdy zakadamy, e to co chcemy odbi jest tylko na jednej teksturce <#FONT:[COLOR:0xFFB8AA98]#>(a przecie chcemy widzie cae otoczenie)<#FONT:[COLOR:0xFFFFFFFF]#>, jednake stosunkowo szybkie, adnie wyglda i czasami wystarcza. Prawdziwy enviroment polega na tym, e robimy niejako <#FONT:[COLOR:0xFF6B8DFD]#>"zrzut"<#FONT:[COLOR:0xFFFFFFFF]#> wszystkich szeciu stron otoczenia, nakadamy na szecian, liczymy wektor od liczonego wierzchoka do punktu kamery, obliczamy wektor odbicia, sprawdzamy przez ktra cian wygenerowanego cube'a on przechodzi, a nastpnie pobieramy z danej tekstury warto u i v. To tylko oglny zapis, bo w praktyce jest to znacznie bardziej skomplikowane. Na dzisiejszych kartach jest to obsugiwane sprztowo i nazywa si Cube Mapping. Jak kto ma GeForce'a albo Rodeona to mona poeksperymentowa, bo efekty naprawd fajne.

   A jak ju jestemy przy oszukaczym enviromencie, to dodam, e za jego pomoc moemy zrobi oszukaczego phonga. Zamiast zwyczajnej tekstury dajemy tekstur <#FONT:[COLOR:0xFF6B8DFD]#>"latarki" <#FONT:[COLOR:0xFFB8AA98]#>(tak jak do bumpa)<#FONT:[COLOR:0xFFFFFFFF]#> i jazda. Co prawda te jest to troszk ograniczone, gdy w tym przypadku zakadamy, e wiato umiejscowione jest w pozycji <#FONT:[COLOR:0xFF6B8DFD]#>"za nami"<#FONT:[COLOR:0xFFFFFFFF]#>. Nie mniej jednak, moemy pobawi si w odpowiednie obracanie normali. Ciekawe co wyjdzie?  :). Pozdrawiam.

<#FONT:[BOLD:1]#><#FONT:[COLOR:0xFFFFFF40]#>pienia/msm<#FONT:[BOLD:0]#><#FONT:[COLOR:0xFFFFFFFF]#>
