• Nem Talált Eredményt

Háromszögsávok és hálók

In document Fejlett grafikai algoritmusok (Pldal 132-139)

7. Ütközés-detektálás 112

8.5. Poligon technikák

8.5.2. Háromszögsávok és hálók

A grafikus teljesítmény növelésének egy nagyon gyakori módja az, hogy kevesebb vertexet küldünk át háromszögenként a grafikus csővezetéken. Nyilvánvaló előnyei, hogy kevesebb pontot és normálvektort kell transzformálni, kevesebb vonalvágást kell végrehajtani, keve-sebb megvilágításhoz tartozó számítást kell elvégezni és sorolhatnánk még a többi elvégzendő műveletet. Habár egy alkalmazás szűk keresztmetszete lehet a kitöltési sebesség (azaz a másodpercenként kitöltendő pixelek száma).

A háromszögsávok és háromszöglegyezők esetén a közös vertexeket csak egyszer kell elküldeni a grafikus csővezetékbe, hiszen az első három vertex megadása után minden új vertexszel egy új háromszöget hozunk létre (lásd8.2. ábrát).

v

1

v

2

v

3

v

0

T

0

T

1

T

2

T

3

T

4

T

5

T

6

v

4

v

6

v

8

v

5

v

7

8.2. ábra. Háromszögek sorozata egy háromszögsávként ábrázolható. Megjegyezzük, hogy a háromszögek irányítottsága háromszögenként váltakozik, ennek következtében az első háromszög irányítottsága határozza meg az összes háromszög körbejárását.

Egy szekvenciális háromszögsávot, rendezett vertexek listájával definiálhatjukv0,v1, . . . , vn, ahol a Ti az i-ik háromszöget 4vi,vi+1,vi+2 jelöli, 0 i < n− 2 esetén. Azért hívjuk szekvenciálisnak, mert a vertexeket a megadott sorrendben küldjük a GPU felé. A definícióból következik, hogy a szekvenciális háromszögsáv n vertexen 2 háromszöget határoz meg. Azn−2-t a háromszögsáv hosszának nevezzük.

Ha egy szekvenciális háromszögsávmháromszöget tartalmaz, akkor az első három vertex alkotja az első háromszöget. Újabb vertexek hozzá vételével kapjuk a maradék m 1 háromszöget. Ez azt jelenti, hogy a vertexek va átlagos száma egym hosszú szekvenciális háromszögsáv esetén a következőképpen fejezhető ki:

va = 3+ (m1)

m =1+ 2

m. (8.1)

Könnyen látható, hogy m → ∞ esetén va 1 teljesül. Valós esetben ennek nincs nagy jelentősége. Amennyiben m = 10, akkor va = 1.2. ami azt jelenti, hogy átlagosan 1.2 vertexet küldünk át háromszögenként. Ebből adódik a háromszögsávok jelentősége.

8.5. POLIGON TECHNIKÁK 133

Attól függően, hogy hol van a renderelési csővezetéknek a szűk keresztmetszete, potenci-álisan lehetőség van arra, hogy a renderelési idő kétharmadát megspóroljuk szekvenciális háromszögsávok segítségével.2 A sebességnövekedés a redundáns műveletek, mint például az adatok grafikus hardverre való küldése, megvilágítási számítások elvégzése, vágás, mátrix transzformációk stb. elkerülése miatt következik be.

Ha nem követeljük meg a szigorú szekvenciáját a háromszögeknek, ahogy ezt a szek-venciális háromszögsávok esetén tesszük, akkor hosszabb és ezáltal hatékonyabb sávokat hozhatunk létre. Ezeket a háromszögsávokat általánosított háromszögsávoknak nevezzük.

Ahhoz, hogy ilyen sávokat tudjunk előállítani szükség van egy fajta vertex gyorsítótárra a grafikus kártyán, amely a transzformált és a megvilágított vertexeket tárolja, ahol a vertexeket el lehet érni és ki lehet cserélni rövid bit kódok küldésével. Így a háromszögsáv előállító a puffer tartalmát teljes mértékben kézben tarthatja, bár néhány esetben ezek a tárak FIFO típusúak, amikor a felhasználónak nem kell kezelni azt. Amikor a vertexek a gyorsítótárba kerülnek, akkor más háromszögek is felhasználhatják azokat elenyésző költséggel.

A szekvenciális háromszögek

”általánosításához” a csere műveletet kell bevezetnünk, amely megcseréli a két utolsó vertexnek a sorrendjét. Az Iris GL-ben3, erre külön uta-sítás létezik. OpenGL-ben és Direct3D-ben viszont a csere parancsot egy vertex újra küldésével valósíthatjuk meg, ami cserénként egy vertexnyi plusz költséget jelent. A csere ilyen módú megvalósítása egy olyan háromszöget hoz létre, melynek nincs területe.

Mivel egy új háromszögsáv létrehozásának a költsége két vertex, szemben a csere egy plusz vertexével, még így is jobban járunk, mintha újra kezdenénk a háromszögsávot.

Továbbá az aktuális API hívások, melyek a sáv küldésért felelősek, további költségeket jelentenének, így kevesebb API hívás szintén növelheti a teljesítményt. Egy háromszögsáv, amely a (v0,v1,v2,v3, csere,v4,v5,v6) vertexek átküldésére várakozik, megvalósítható a következőképpen (v0,v1,v2,v3,v2,v4,v5,v6), ahol a csere a v2 vertex újraküldésével lett megvalósítva (lásd8.3. ábrát).

Ahogy korábban említettük a háromszög-legyező (lásd 8.4. ábrát) hasonló jó tulajdon-sággal rendelkezik, mint a háromszögsáv. A legtöbb alacsony szintű grafikus API támogatja a háromszög-legyezők létrehozását. Megjegyezzük, hogy egy általános konvex poligont könnyen lehet háromszög-legyezővé alakítani és természetesen háromszögsávvá is könnyű alakítani.

Aközépső vertexenaz összes háromszög osztozik. Egy új háromszöget a középső vertex, az előzőleg elküldött vertex és az új vertex segítségével hozzuk létre. Aznvertexből felépülő háromszög-legyezőt a v0,v1, . . . ,vn1 rendezett vertex listái alkotják, ahol v0 a középső vertex. Az i-ik háromszög 4v0,vi+1,vi+2 jelöli, 0 i < n 2 esetén. A 8.1. képletet alkalmazva, ebben az esetben ism→ ∞esetén,vaa háromszög-legyezőknél is egy vertexhez tart háromszögenként. Bármelyik háromszög-legyező átalakítható háromszögsávvá (amely sok cserét fog tartalmazni), de ez fordítva nem hajtható végre.

Sávok előállítása

Egy adott általános háromszöghálót érdemes hatékonyan szétbontani háromszögsávokra.

2Ez háromszög-legyezők esetén is igaz. Néhány gyártó cég azt javasolja, hogy inkább háromszögsávokat használjunk a háromszög-legyezők helyett is a meghajtó optimalizálása miatt.

3Integrated Raster Imaging System Graphics Library, melyet a Silicon Graphics (SGI) fejlesztett ki 2- és 3D-s számítógépes grafika létrehozására az IRIX-alapú IRIS grafikus munkaállomásaikon

v

1

v

2

v

3

v

0

v

4

v

6

v

5

T

3

T

4

T

4

T

1

T

2

T

0

8.3. ábra. A grafikus csővezetékbe a(v0,v1,v2,v3,v2,v4,v5,v6)vertexeket küldjük, ame-lyek egy háromszögsávot fognak létrehozni. A csereművelet av2vertex kétszeri alkalmazá-sával van megvalósítva.

v

1

v

2

v

3

v

0

v

4

v

5

T

0

T

3

T

1

T

2

8.4. ábra. Háromszög-legyező. AT0háromszöghöz av0(középső vertex),v2ésv2vertexeket küldi el. A rákövetkezőTi(i >0)háromszög esetén csak avi+2vertexet küldi el.

8.5. POLIGON TECHNIKÁK 135

Optimális háromszögsávok előállítására bebizonyították, hogy NP-teljes probléma és ezért meg kell elégednünk heurisztikus módszerekkel, amelyek a háromszögsávok számának az alsó határához közelítenek. A következőkben egy mohó stratégiát fogunk bemutatni a szekvenciális háromszögsávok létrehozására.

Mindegyik háromszögsáv létrehozó algoritmus a poligon halmaz szomszédsági adat struktúrájának a létrehozásával kezdődik, ahol mindegyik poligonhoz tartozó él esetén szomszédos poligonra való hivatkozást is el kell tárolni. A poligonok szomszédjainak a számátfoknaknevezzük, és egész értéke 0 és a poligon vertexeinek a száma között van.

Euler síkbeli kapcsolódó gráfokra vonatkozó tétele (8.2. egyenlet) alapján meghatároz-hatjuk a vertexek átlagos számát, amit a csővezetékbe küldünk.

v−e+f−2g =2, (8.2)

ahol v a vertexek számát, eaz élek számát,f a lapok számát és g az objektumban lévő lyukak számát jelöli. Mivel kapcsolódó gráfok esetén minden él mentén két lap helyezkedik el és minden lapnak legalább három éle van, ezért a2e3fmindig teljesül. Behelyettesítve ezt Euler tételbe, egyszerűsítés után azt kapjuk, hogyf 2v4. Ha mindegyik lap háromszög, akkor 2e = 3f f = 2v 4. Mindent összevetve ez azt jelenti, hogy a háromszögek száma kisebb vagy egyenlő a vertexek számának a kétszeresénél a háromszögekre való felbontáskor. Mivel a háromszögenkénti vertexek száma a háromszögsávban az egyhez tart, minden vertexet (átlagosan) legalább kétszer kell elküldeni a szekvenciális háromszögsáv használatakor.

Továbbfejlesztett SGI háromszögsáv-képző algoritmus

Ez az algoritmus csak olyan modellek esetén működik, amelyek teljesen háromszögekből épülnek fel. A mohó algoritmusok olyan optimalizálási algoritmusok, amelyek a lokális optimumok alapján döntenek (azt választják, amely a legjobb az adott pillanatban). Az SGI algoritmus is egy ilyen mohó algoritmus, ahol mindig azt a kezdő háromszöget választja, amelyiknek a legkisebb a foka (legkevesebb szomszédos oldala van). Néhány módosítással az SGI algoritmus a következőképpen néz ki:

1. Válasszuk ki a kezdő háromszöget.

2. Építsünk 3 különböző háromszögsávot a háromszög minden éle mentén.

3. Terjesszük ki ezeket a háromszögsávokat az háromszögsáv első elemétől az ellenkező irányba.

4. Válasszuk ki a három közül a leghosszabb háromszögsávot és töröljük a többit.

5. Ismételjük meg a folyamatot az 1-es lépéstől addig, amíg az összes háromszög be nem került a sávba.

Az első lépésben az algoritmus a legkisebb fokszámú háromszöget választja ki. Amennyi-ben több ilyen megegyező fokszámú háromszög létezik, akkor az algoritmus a szomszédos háromszögek szomszédjainak a fokszáma alapján dönt. Ha ezek után még mindig nem egyértelmű a háromszög kiválasztása, akkor tetszőlegesen kiválaszt egyet.

Végezetül a sávot a háromszögsáv kezdő és végső háromszögének az irányba terjesztjük ki. Az alapötlet az, hogy az elkülönített háromszögek nem szerepelnek egyetlen egy három-szögsávban sem. Lényegében ezeknek a háromszögeknek a számát minimalizálja az SGI algoritmus. Lineáris idejű algoritmus megvalósítható hash táblák segítségével, amelyek a szomszédsági adatokat tárolják, valamint prioritási sorok segítségével, amelyeket mindegyik új sáv kezdő háromszögének keresésére használunk fel. Többen is bebizonyították, hogy tetszőleges háromszöget választva az algoritmus során ugyan olyan jó eredményt kaphatunk.

Így nem biztos, hogy megéri a jó kezdő háromszög kiválasztásával bajlódni. A 2-es lépéstől a 4-es lépésig biztosítva van az, hogy a kezdő háromszöget az aktuális háló leghosszabb sávjában megtaláljuk.

Egy praktikus szempont lehet az, hogy a háromszögek irányítottságát meg kellene őrizni azért, hogy helyes árnyalást és hátsólap eltávolítást kapjunk. Emlékezzünk vissza, hogy a háromszögsáv első háromszöge határozza meg a háromszögek körbejárását (lásd8.2. ábrát).

Egy példát láthatunk arra, hogy mi történik abban az esetben, ha a körbejárás megváltozik a kiterjesztés során, ahogy ez a8.5. ábrán is látható.

v1

8.5. ábra. Sáv kiterjesztés. (a)T3háromszöget (órajárással ellentétes körbejárású) választjuk kezdő háromszögként és a sáv jobbra terjeszkedikT4 ésT3 háromszögeket magába foglalva.

(b) A sávot balra terjesztettük ki, így növelve annak hosszát. Az eredmény sávban aT0v0v1v2 háromszöget választjuk kezdő háromszögként. Emiatt a teljes háromszögsáv körbejárása megváltozik (órajárással megegyező lesz), mivelT0is ilyen irányítottságú. Ezt a problémát kikerülhetjük úgy, hogy az első vertexet megduplázzuk a sávban, amivel egy üres-területű háromszöget hozunk létre.

8.5. POLIGON TECHNIKÁK 137

Duális gráf háromszögsáv-képző algoritmus

Egy másik stratégia az, amikor a háromszög-háló duális gráfját használjuk. Ekkor a gráf élei a szomszédos lapok középpontjait összekötő élei lesznek (lásd 8.6. ábrát). Ezen élek gráfjátfeszítőfánaknevezzük, amelyet azután jó háromszögsávok keresésére használhatunk fel.

8.6. ábra. Háromszög-háló és annak duális gráfja

Pufferbarát háromszögsáv-képző algoritmus

Tételezzük fel, hogy a háromszöghálókat háromszögsávokkal hoztuk létre. Mivel a gra-fikus kártyák többségének van vertex puffere (gyorsítótára), ezért a csővezetéken átküldendő sávok sorrendjei különböző vertex puffer teljesítményt fognak mutatni számunkra, mivel a gyorsítók különböző tármérettel rendelkeznek. Egy egyszerű előfeldolgozási művelettel javíthatjuk a sorrendet, amely a következőképpen néz ki:

• Vegyünk egy háromszögsávot, melynek a vertexeit a vertex gyorsítótár (FIFO) egy szoftveres szimulációjában helyezzük el.

• A fennmaradó háromszögsávok közül kiválasztjuk azt, amelyik a legjobban használja ki a tár tartalmát.

• Az eljárást addig ismételjük, amíg az összes háromszögsávot fel nem dolgoztuk.

Az NVIDIA NVTriStrip4függvénykönyvtár is pontosan ezeket a lépéseket követi.

Háromszöghálók

A háromszöghálók vertexek listájából és körvonalak halmazából állnak. Minden vertex pozíció és további adatokat tartalmaz, mint például diffúz és spekuláris színeket, árnyalási normálvektort, textúra-koordinátákat stb. Mindegyik háromszög körvonal egész értékű indexek listájával rendelkezik. Ezek az indexek a vertexekre mutatnak a listában.

Általános renderelési szekvenciák létrehozása

Adott egy indexelt háromszög háló, amely hatékonyan renderelhető egy primitív vertex gyorsítótárral rendelkező grafikus hardver segítségével. A kérdés továbbra is az indexek

4http://developer.nvidia.com/object/nvtristrip_library.html

megfelelő sorrendjének meghatározása. Ráadásul, ahogy azt az imént már említettük, a különböző hardverek különböző méretű vertex gyorsítótárral rendelkeznek. Az egyik megoldás az, hogy mindegyik méretre más és más módon állítjuk elő az indexek sorrendjét.

Természetesen ez nem a legszebb megoldás. Az igazi megoldás egyuniverzálisindex sorozat előállítása, amely az összes lehetséges vertex gyorsítótár méret esetén jól viselkedik.

Mielőtt rátérnénk az univerzális algoritmus ismertetésére, szükség van a terület-kitöltő görbe fogalmának bevezetésére. A terület-kitöltő görbe egy egyszerű, folytonos görbe, amelyik nem metszi önmagát, kitölt egy olyan négyzetet vagy téglalapot, amely uniform négyzetrácsait csak egyszer érint annak bejárása során. A jó terület-kitöltő görbe jó térbeli összefüggőséggel rendelkezik, amely azt jelenti, hogy amikor bejárjuk azt mindig az előzőleg meglátogatott pontok közelében maradunk. A Hilbert görbe (lásd 8.7. ábrát) egy példa a terület-kitöltő görbére, amely rendelkezi a térbeli összefüggőség tulajdonsággal is.

8.7. ábra. Első-, másod- és harmadrendű Hilbert görbe

Ha ilyen terület-kitöltő görbét használunk a háromszögek bejárására a háromszög háló esetén, akkor a vertex gyorsítótárban nagy találati arányra számíthatunk. Egy rekurzív algoritmussal jó index sorozatot lehet létrehozni a háromszöghálókra. Az alap ötlet az, hogy szétvágjuk a hálót megközelítőleg két egyforma méretű hálóra, majd az egyik illetve a másik hálót rendereljük le. Ezt ismételjük rekurzívan minkét hálóra. A háló szétvágását előfeldolgozási lépésként hajtjuk végre kiegyensúlyozott él-vágás algoritmussal5, amely minimalizálja az él vágások számát. Az algoritmus komplexitása lineáris a hálóban lévő háromszögek számára nézve. Az algoritmus a következőképpen néz ki:

1. Hozzuk létre a háromszögháló duális gráfját.

2. Ezután a kiegyensúlyozott él-vágás algoritmussal eltávolítjuk a minimális élek halma-zát a duális gráfban azért, hogy a hálót két különálló, nagyjából megegyező méretű hálóra vágjuk szét.

3. A jó gyorsítótár teljesítmény eléréséhez ajánlott, hogy az utolsó háromszög az első hálóban közel legyen a második háló első háromszögéhez. Ezt elérhetjük azzal, hogy az első háló utolsó háromszöge és a második háló első háromszöge esetén megengedjük, hogy a duális gráfban egy élen osztozzanak, amelyet átvágtunk.

Ezt ismételve rekurzívan, az indexek sorozata előállítható. A renderelési sorrendet a rekurziós folyamat határozza meg.

5A MeTiS szoftver csomaggal ez elvégezhetőhttp://www-users.cs.umn.edu/k̃arypis/metis

8.5. POLIGON TECHNIKÁK 139

In document Fejlett grafikai algoritmusok (Pldal 132-139)