3D-NÄKYVYYSOPTIMOINTI MOBIILILAITTEILLA Miikka Kulmala Opinnäytetyö Toukokuu 2005 Informaatioteknologian instituutti
1 SISÄLTÖ LYHENTEET JA TERMIT... 4 1 NÄKYVYYSOPTIMOINTI, MITÄ JA MIKSI?... 6 2 3D-GRAFIIKAN PERUSTEET... 7 2.1 Yleistä... 7 2.2 Esitysliukuhihna... 8 2.2.1 Kolme päävaihetta... 8 2.2.2 Sovellusvaihe... 8 2.2.3 Geometriavaihe... 8 2.2.4 Rasterointivaihe... 12 2.2.5 Yhteenveto... 13 3 ERILAISIA OPTIMOINTIMENETELMIÄ... 14 3.1 Näkyvyysoptimointi... 14 3.2 Tilanjakotietorakenteet... 14 3.2.1 Yleistä... 14 3.2.2 Rajoitustilavuushierarkiat... 14 3.2.3 Binääriset tilanjakopuut (BSP-puut)... 16 3.2.4 Kasipuut ja nelipuut... 20 3.2.5 Näkymäverkot... 22 3.3 Erottelumenetelmät... 23 3.3.1 Yleistä... 23 3.3.2 Taustapintojen erottelu... 23 3.3.3 Ryhmitelty taustapintojen erottelu... 25 3.3.4 Hierarkkinen näköpyramidierottelu... 26 3.3.5 Portaalit... 27 3.3.6 Tarkkuustasot... 29
2 3.3.7 Katve-erottelu... 29 3.4 Muita menetelmiä... 30 4.1 Mobiililaitteiden rajoitukset... 32 4.2 DieselEnginen rakenne... 33 4.3 Suunnitelmasta toteutukseen... 34 4.3.1 Lähtökohdat... 34 4.3.2 Suunnitelma... 35 5 TULOKSET... 39 5.1 Miten testattiin?... 39 5.2 Tulosten tarkastelu... 40 6 AJATUKSIA... 41 6.1 Yleistä... 41 6.2 Ongelmat... 41 6.3 Mitä jatkossa?... 42
3 KUVIOT KUVIO 1. 3D-grafiikka koostuu toisiinsa liitetyistä kolmioista... 7 KUVIO 2. Esitysliukuhihna jaettuna kolmeen päävaiheeseen... 8 KUVIO 3. Geometriavaiheen alivaiheet... 9 KUVIO 4. Näköpyramidin havainnollistaminen (Akenine-Möller & Haines 2002, 10, 15.).... 10 KUVIO 5. Ortografinen projektio (vas.) ja perspektiiviprojektio (oik.) (Akenine- Möller & Haines 2002, 17.)... 11 KUVIO 6. Primitiivien leikkaus (Akenine-Möller & Haines 2002, 19.)... 12 KUVIO 7. Syvyyspuskurin havainnollistaminen... 13 KUVIO 8. Rajoitustilavuushierarkian havainnollistaminen... 15 KUVIO 9. Perusakselien suuntainen BSP-puu kaksiulotteisessa avaruudessa... 17 KUVIO 10. Polygonisuunnattu BSP-puu kaksiulotteisessa avaruudessa... 18 KUVIO 11. Kuvasarja nelipuun rakentamisesta... 21 KUVIO 12. Näkymäverkon havainnollistaminen... 23 KUVIO 13. Taustapintojen erottelu... 24 KUVIO 14. Taustapintojen erottelu kolmion kärkipisteiden määrittelysuunnan mukaan... 25 KUVIO 15. Hierarkkinen näköpyramidierottelu... 27 KUVIO 16. Portaalierottelu... 28 KUVIO 17. Teekannu ja sen eri tarkkuustasoja... 29 KUVIO 18. UML-kaavio DieselEnginen 3D-moottorin keskeisistä luokista... 34 KUVIO 19. Päivitetty UML-kaavio... 36 KUVIO 20. Testitulosten vertailu... 40 TAULUKOT TAULUKKO 1. CDiesel3DSceneF -luokan operaatiokutsut ja kuvaukset... 37 TAULUKKO 2. CDiesel3DLODSwitch -luokan operaatiokutsut ja kuvaukset... 38 TAULUKKO 3. Testatut laitteet... 39
4 LYHENTEET JA TERMIT 3D Kolmiulotteinen FPS Frames Per Second, kertoo montako kertaa sekunnissa näyttöä päivitetään. Konveksi Konveksi monikulmio tarkoittaa sellaista monikulmiota, jossa kaikki kulmat ovat kuperia, ts. mikään monikulmion nurkka ei ole kääntynyt monikulmion sisäänpäin. Esim. säännölliset monikulmiot ovat konvekseja, mutta L-kirjaimen muotoinen monikulmio ei ole konveksi. Muunnospino Muunnospinoon (engl. matrix stack) tallennetaan kolmiulotteisia muunnoksia pinomaiseen tietorakenteeseen (viimeksi asetettu otetaan ensimmäisenä pois). Muunnospinon tärkeä ominaisuus on se, että pinon päälle asetettu muunnos yhdistetään aina edelliseen päällä olleeseen. Näin pinon päällimmäinen muunnos sisältää kaikki sen alla olevat muunnokset. Näkymä Näkymällä (engl. scene) tarkoitetaan kaikkia sillä hetkellä käsiteltäviä kolmiulotteisia objekteja, myös niitä jotka mahdollisesti jäävät kuvan ulkopuolelle. Esimerkkinä tietokonepelin maailma. Pelimoottori Ohjelmointikirjasto, joka sisältää rutiineja esimerkiksi grafiikan piirtämiseen, äänien soittamiseen, näppäinten käsittelyyn. Pelimoottoria käyttämällä pelin tekijä voi keskittyä olennaiseen, eli pelin sisällön tuottamiseen. Primitiivi Primitiivi on kolmiulotteisen grafiikan pienin mahdollinen osa josta objektit koostuvat, yleensä kolmio, mutta voi tarkoittaa myös viivoja ja pisteitä.
5 Rasterointi Kuvan muodostaminen kaksiulotteiselle pinnalle. Tekstuuri Kuva, joka määrittelee millainen pintakuviointi kolmiulotteisella objektilla on. UML UML (Unified Modeling Language) on laajalle levinnyt tekniikka oliopohjaisten järjestelmien kuvaamiseen. UML mahdollistaa sekä staattisten että dynaamisten rakenteiden havainnollisen mallintamisen. Verteksi Kärkipiste, kulmapiste (engl. vertex).
6 1 NÄKYVYYSOPTIMOINTI, MITÄ JA MIKSI? Reaaliaikainen kolmiulotteinen grafiikka mobiililaitteilla, kuten kännyköillä, kämmenmikroilla ja älypuhelimilla, on päivä päivältä yleisempää. Tyypillisiä sovelluksia ovat esimerkiksi käyttöliittymät, näytönsäästäjät, karttasovellukset ja pelit. 3Dnäkyvyysoptimointi on yksi tärkeimmistä reaaliaikaisen kolmiulotteisen grafiikan osaalueista jopa nykyisillä tehokkailla pöytäkoneilla, joissa on kolmiulotteisen grafiikan piirtämiseen tarkoitettu laitteistokiihdytys. Mitä näkyvyysoptimointi sitten on? Asiaa on ehkä helpointa lähestyä esimerkin kautta: kuvitellaan kolmiulotteiseksi pelimaailmaksi kaupunki, jonka keskellä pelaaja seisoo. Pelaaja näkee edessään kadun, muutaman auton ja rakennusten seiniä, vaikka koko kaupungissa on kuitenkin satoja rakennuksia ja autoja. Näkyvyysoptimoinnin perusajatuksena on erotella näkymästä vain ne osat, jotka näkyvät lopullisessa kuvassa mikä piirretään ruudulle. Säästää huomattavan määrän aikaa kun piirrettäväksi lähetetään ainoastaan se, mitä tarvitaan kuvan muodostamiseen. DieselEngine on Inmar Softwaren kehittämä pelimoottori, joka mahdollistaa graafisesti näyttävien pelien ja esitysten tekemisen multimediapuhelimille, kämmenmikroille ja pöytäkoneille. DieselEnginessä ei tällä hetkellä ole käytännössä minkäänlaista korkeamman tason näkyvyysoptimointia, joten tarkoituksena oli tutkia erilaisia optimointimenetelmiä ja toteuttaa DieselEngineen sopiva sellainen. Työtä ei tehdä työsuhteessa, vaan vapaana tutkimuksena, joten DieselEnginen lähdekoodit eivät ole käytettävissä. Työ toteutettiin Inmar Software Oy:lle, joka on jyväskyläläinen vuonna 2000 perustettu mobiililaitteiden peli- ja grafiikkateknologioihin keskittynyt ohjelmistoalan yritys.
7 2 3D-GRAFIIKAN PERUSTEET 2.1 Yleistä 3D-grafiikka koostuu pääsääntöisesti kolmioista. Kaikki mikä ruudulla näkyy on pilkottu primitiiveihin (ks. kuvio 1). Näillä primitiiveillä ja niiden kärkipisteillä on erilaisia ominaisuuksia, jotka määrittelevät mm. värin, pintakuvioinnin, läpinäkyvyyden ja valon heijastumisen. KUVIO 1. 3D-grafiikka koostuu toisiinsa liitetyistä kolmioista
8 2.2 Esitysliukuhihna 2.2.1 Kolme päävaihetta Kun kolmiulotteisesta näkymästä muodostetaan kuva kaksiulotteiselle pinnalle, täytyy se ajaa esitysliukuhihnan (engl. rendering pipeline) läpi. Kuvan muodostuksesta voidaan eritellä muutamia yleisiä vaiheita. Akenine-Möller ja Haines (2002) jakavat liukuhihnan kolmeen päävaiheeseen: sovellus-, geometria- ja rasterointivaiheeseen (ks. kuvio 2). Luvuissa 2.2.1 2.2.4 esitellään em. teoksessa esitelty liukuhihna. KUVIO 2. Esitysliukuhihna jaettuna kolmeen päävaiheeseen 2.2.2 Sovellusvaihe Sovellusvaiheessa määritellään piirrettävä geometria ja piirtotyylit (värit, pintakuvioinnit ym.). Pääosa näkyvyysoptimoinnista tapahtuu juuri sovellusvaiheessa, ajatuksena on, että mahdollisimman vähän turhaa geometriaa päästetään seuraavaan vaiheeseen. Lisäksi sovellusvaiheessa voidaan tehdä mm. törmäystarkistuksia. Yleisesti ottaen kaikki mikä ei liity grafiikan piirtämiseen tehdään tässä vaiheessa. 2.2.3 Geometriavaihe Geometriavaihe voidaan jakaa viiteen alivaiheeseen kuvion 3 osoittamalla tavalla. Geometriavaihe muuntaa liukuhihnan läpi kulkevat kolmiulotteiset primitiivit kaksiulotteiseen avaruuteen rasterointia varten.
9 KUVIO 3. Geometriavaiheen alivaiheet Malli- ja näkymämuunnos 3D-mallin kärkipisteet on määritelty malliavaruudessa (engl. model space). Tämä on avaruus, jossa mallit ovat ennen mitään muunnosta. Malli muunnetaan maailmaavaruuteen (engl. world space) mallimuunnoksella (engl. model transformation), joka siirtää mallin oikealle paikalleen virtuaalimaailmaan. Jos ajatellaan, että näkymää katsellaan kameran läpi, kameraan nähden suhteellista avaruutta sanotaan näkymäavaruudeksi (engl. view space). Kaikki 3D-mallit siirretään maailma-avaruudesta näkymäavaruuteen, jolloin saadaan aikaan kameran linssin läpi kuvattu alue. Kamera sijaitsee näkymäavaruuden origossa osoittaen z-akselia pitkin joko positiiviseen tai negatiiviseen suuntaan (suunnalla on lähinnä vain semanttinen merkitys). Malli- ja näkymämuunnos (engl. model & view transform) sisältää edellä mainitussa prosessissa tarvittavat muunnokset. Muunnosten jälkeen kaikki kärkipisteet ja niiden mukana kolmiot ovat samassa avaruudessa valmiina valaistusta varten. Tilaa, jonka sisällä olevat kappaleet näkyvät kameran kautta, sanotaan näköpyramidiksi (engl. view frustum). Näköpyramidi on geometrisesti äärettömyyteen ulottuva pyramidi, jonka kärki on kamerassa ja pohja kameran osoittamassa suunnassa (ks. kuvio 4).
10 KUVIO 4. Näköpyramidin havainnollistaminen (Akenine-Möller & Haines 2002, 10, 15.). Valaistus Valaistuksen käyttäminen ei ole pakollista, mutta se lisää realismia ja tuo objektien pinnanmuodot paremmin esiin. Valaistusvaihe ei ole näkyvyysoptimoinnin kannalta olennainen, joten sitä ei tässä käsitellä tarkemmin. Projektio Projektiovaiheessa näkötilavuus muunnetaan yksikkökuutioksi, jonka ääripisteet ovat (-1,-1,-1) ja (1,1,1). Tätä kuutiota kutsutaan kanoniseksi näkötilavuudeksi (engl. canonical view volume). On olemassa periaatteessa kaksi projektiomenetelmää, ortograafinen projektio ja perspektiiviprojektio. Perspektiiviprojektiossa kappaleet näyttävät sitä pienemmiltä, mitä kauempana katsojasta ne ovat (ks. kuvio 5). Ortografinen projektio taas säilyttää yhdensuuntaiset viivat yhdensuuntaisina (ks. kuvio 5). Vaikka projektiovaihe vain muuntaa tilan toiseksi, sitä kutsutaan projektioksi, koska rasteroinnin jälkeen pisteiden z-arvoja ei tallenneta enää luotuun kuvaan, vaan niitä käytetään syvyyspuskurin luonnissa (ks. luku 2.2.4). Kappaleet siis projisoidaan kolmesta ulottuvuudesta kahteen.
11 KUVIO 5. Ortografinen projektio (vas.) ja perspektiiviprojektio (oik.) (Akenine- Möller & Haines 2002, 17.) Leikkaus Ainoastaan ne primitiivit, jotka ovat kokonaan tai osittain näkötilavuuden sisällä, jatkavat liukuhihnan viimeiseen eli rasterointivaiheeseen, jossa ne piirretään ruudulle. Jos primitiivi on kokonaan näkötilavuuden sisällä, se jatkaa sellaisenaan rasterointivaiheeseen. Kokonaan näkötilavuuden ulkopuolella olevia primitiivejä ei välitetä eteenpäin, koska ne eivät ole näkyvissä kuvassa. Osittain näkötilavuuden sisällä olevat primitiivit täytyy leikata (ks. kuvio 6). Projektiovaiheen ansiosta leikkaus tehdään aina yksikkökuutiota vasten.
12 KUVIO 6. Primitiivien leikkaus (Akenine-Möller & Haines 2002, 19.) Ruutukartoitus Vain näkötilavuuden sisällä olevat primitiivit välitetään ruutukartoitusvaiheeseen (engl. screen mapping) asti, koordinaatit ovat tässä vaiheessa vielä kolmiulotteisia. Kaikkien primitiivien x- ja y-koordinaatit muunnetaan ruutukoordinaateiksi (engl. screen coordinates), jolloin uudet koordinaatit kuvaavat pisteiden paikkaa näyttöruudulla. Tämä muunnos ei vaikuta z-koordinaattiin, joka välitetään sellaisenaan muunnettujen x- ja y-koordinaattien kanssa rasterointivaiheeseen. 2.2.4 Rasterointivaihe Geometriavaiheelta saatujen muunnettujen ja projisoitujen kärkipisteiden, värien ja esimerkiksi pintakuvioinnin koordinaattien avulla rasterointivaiheen on tarkoitus piirtää lopullinen kuva näytölle. Rasterointivaihe muuntaa primitiivit kuvapisteiksi, joiden väriin voi vaikuttaa mm. sävytysarvo, pintakuviointi ja entisen kuvapisteen väri. Näkyvyysoptimoinnin kannalta mielenkiintoinen osa rasterointivaihetta on syvyyspuskuri. Syvyyspuskuri on käytännössä kuvaruudun kokoinen taulukko, joka tallentaa jokaisen kuvapisteen kohdalle tiedon siitä, miten lähellä katsojaa piste on käyttäen hyväksi geometriavaiheelta saatuja z-koordinaatteja. Aina uutta kuvapistettä piirrettäessä tarkistetaan syvyyspuskurista, onko sillä kohdalla jo olemassa piirrettävää lähempänä katsojaa oleva kuvapiste. Jos näin on, uutta pistettä ei piirretä. Kuviossa 7 vasemmalla on normaali kolmiulotteinen näkymä ja oikealla syvyyspuskuri; mitä tummempi väri, sitä lähempänä kameraa piste on.
13 KUVIO 7. Syvyyspuskurin havainnollistaminen Syvyyspuskuri ratkaisee näkyvyysongelman tarkasti, mutta ei missään nimessä nopeasti. Kuvitellaan esimerkiksi tilanne, jossa katsoja katsoo kohtisuoraan kymmenen saman kokoisen pallon jonoa niin, että vain ensimmäinen pallo on näkyvissä ja muut sen takana. Pahimmassa tapauksessa jokainen näistä palloista muunnetaan ja piirretään sekä näytölle että syvyyspuskuriin. (Akenine-Möller & Haines 2002, 368 369.). 2.2.5 Yhteenveto Vaikka liukuhihnassa on vaiheita, jotka poistavat näyttöalueen ulkopuolella olevaa geometriaa, on näkyvyysoptimointi erittäin tärkeä reaaliaikaisen grafiikan osa-alue. Mitä varhaisemmassa vaiheessa turha geometria pystytään sulkemaan pois, sitä vähemmän aikaa liukuhihnan läpikäyntiin kuluu. Edellä esitelty liukuhihna ei tietenkään ole ainut mahdollinen, mutta pääpiirteissään kuvan muodostaminen noudattaa aina samoja vaiheita. Muun muassa Jim Blinn (1996) kuvaa kirjassaan tarkemmin prosessin eri vaiheita ja niissä käytettyjä algoritmeja.
14 3 ERILAISIA OPTIMOINTIMENETELMIÄ 3.1 Näkyvyysoptimointi Näkyvyysoptimoinnilla (engl. visibility optimization) pyritään optimoimaan ja järjestämään geometriadata siten, että pystytään mahdollisimman tehokkaasti sulkemaan näkymästä pois ne primitiivit, jotka jäävät näkökentän ulkopuolelle tai etualalla olevien peittävien primitiivien taakse. Useimpien optimointimenetelmien pohjalla on jonkinlainen tilanjakotietorakenne (engl. spatial data structure), jolla optimoidaan kyselyjä geometriadatasta. Tämän lisäksi on erilaisia erottelumenetelmiä (engl. culling techniques), joilla selvitetään mitkä osat geometriasta piirretään. Luvuissa 3.2.1 3.3.7 tarkastellaan erilaisia tilanjakotietorakenteita ja erottelumenetelmiä. 3.2 Tilanjakotietorakenteet 3.2.1 Yleistä Tilanjakotietorakenteilla organisoidaan geometriaa kolmiulotteisessa avaruudessa. Tarkoituksena on nopeuttaa kyselyitä geometriadatasta mahdollisimman optimaaliseksi. Tietorakenteet ovat yleensä hierarkkisia, eli ylin taso käsittää sen sisällä olevat, joka käsittää sen sisällä olevat jne.. Tämä nopeuttaa hakuja ja kyselyjä huomattavasti, koska parhaassa tapauksessa jo alussa voidaan hylätä suuri osa tarpeetonta dataa. (Akenine-Möller & Haines 2002, 346 347.) 3.2.2 Rajoitustilavuushierarkiat Rajoitustilavuus (engl. bounding volume) on alue, joka sulkee sisäänsä objekteja. Ideana on, että rajoitustilavuus on geometrisesti huomattavasti yksinkertaisempi muoto, kuin sen sisältämät objektit. Näin kaikki näkyvyystestit rajoitustilavuutta vasten ovat paljon nopeampia kuin itse objekteja vasten tehdyt. Yleisimpiä rajoitustilavuuk-
15 sia ovat pallot (engl. bounding sphere) ja laatikot (engl. bounding box). Rajoitustilavuuksia käytetään nopeuttamaan erilaisia laskutoimituksia, piirtämistä ja kyselyitä geometriadatasta. (Akenine-Möller & Haines 2002, 347.) Reaaliaikaisen kolmiulotteisen grafiikan piirtämisessä rajoitustilavuushierarkia on ehkä käytetyin tilanjakotietorakenne. Rajoitustilavuushierarkiaa käytetään usein esimerkiksi hierarkkisessa näköpyramidierottelussa (ks. luku 3.3.4). Näkymä organisoidaan juuresta, sisäisistä solmuista ja lehdistä koostuvaan hierarkkiseen puurakenteeseen. Ylin solmu on juuri, jolla ei ole vanhempia. Lehti on solmu, jossa on piirrettävä geometria, eikä sillä ole lapsia. Sisäinen solmu taas on solmu, jolla on osoittimet lapsiinsa. Näin ollen juuri on sisäinen solmu, ellei se ole puun ainut solmu. Jokaisella puun solmulla, mukaan lukien lehdet, on rajoitustilavuus joka sulkee sisäänsä koko sen alipuun geometrian tästä nimi rajoitustilavuushierarkia. Tämä tarkoittaa sitä, että juurella on rajoitustilavuus, joka sisältää koko näkymän geometrian. (Emt., 347.) Kuviossa 8 vasemmalla on yksinkertainen näkymä, jossa on kuusi objektia, joista jokainen on suljettu rajoituspallon sisään. Rajoituspallot on kerätty yhteen suuremmilla rajoituspalloilla, kunnes koko näkymä on yhden suuren rajoituspallon sisällä. Oikealla on kyseisen rajoitustilavuushierarkian puurakenne. KUVIO 8. Rajoitustilavuushierarkian havainnollistaminen
16 3.2.3 Binääriset tilanjakopuut (BSP-puut) Binäärinen tilanjako (engl. binary space partitioning) on erittäin suosittu menetelmä 3D-näkymien geometriadatan järjestämiseen, jossa N-ulotteinen avaruus jaetaan rekursiivisesti N-1 -ulotteisilla hypertasoilla pienempiin konvekseihin osiin. BSP-puu on tätä jakamista kuvaava tietorakenne. Kolmiulotteisessa avaruudessa puun juurisolmu kuvaa koko avaruutta ja sisältää tason, joka jakaa avaruuden kahteen osaan. Ensimmäinen (etummainen) lapsi kuvaa osajoukkoa, joka on tason positiivisella puolella. Toinen (takimmainen) lapsi kuvaa osajoukkoa, joka on tason negatiivisella puolella. Osajoukot voidaan jakaa jälleen uusilla tasoilla pienempiin osiin, jolloin solmut sisältävät osajoukon jakavan tason ja niiden lapset. (Eberly, D. 2001, 417-418.) Akselisuunnatut BSP-puut Akselisuunnatussa BSP-puussa (engl. axis-aligned BSP tree) koko näkymä suljetaan ensin yhden akselisuunnatun rajoituslaatikon (engl. axis-aligned bounding box) sisään. Tämän jälkeen laatikko jaetaan rekursiivisesti pienemmiksi laatikoiksi. Otetaan laatikko mielivaltaiselta rekursiotasolta, valitaan yksi laatikon akseleista ja luodaan kohtisuora taso, joka jakaa laatikon kahdeksi pienemmäksi osaksi. Joissain tapauksissa taso luodaan niin, että laatikko jaetaan kahteen yhtä suureen osaan, joissain tapauksissa taas tason paikka voi vaihdella. Jos laatikon sisällä oleva objekti leikkaa tasoa, se joko tallennetaan tälle tasolle ja siitä tulee molempien osien jäsen, tai se leikataan tason mukaan kahdeksi erilliseksi objektiksi. Tätä jakoprosessia toistetaan rekursiivisesti, kunnes joku ennalta määrätty kriteeri täytetään. Tämä kriteeri on usein käyttäjän määrittelemä puun maksimisyvyys tai yhden laatikon sisällä olevien primitiivien määrän alaraja. (Akenine-Möller & Haines 2002, 350 351.) Kuvion 9 esimerkissä vasemmalla on yksinkertainen näkymä, jonka tilanjaot voivat olla missä tahansa kohdassa akselilla eikä ainoastaan keskellä. Rajoituslaatikot on merkitty kirjaimilla A E. Oikean reunan puunäkymä kuvaa BSP-tietorakennetta, jossa jokainen solmu kuvaa tilanjakotasoa ja niiden alla tason molemmin puolin oleva geometria. Huomioitavaa on, että kuvan kolmio on alueilla C ja E, koska se leikkaa niiden välisen tilanjakotason.
17 KUVIO 9. Perusakselien suuntainen BSP-puu kaksiulotteisessa avaruudessa Polygonisuunnatut BSP-puut Polygonisuunnatussa BSP-puussa (engl. polygon-aligned BSP tree) valitaan yksi polygoni näkymästä ja luodaan tämän pinnan suuntainen taso, joka jakaa näkymän kahteen osaan. Kaikki jakavaa tasoa leikkaavat polygonit jaetaan kahdeksi osaksi tason molemmin puolin. Tämän jälkeen valitaan molemmilta puolilta jälleen yksi polygoni, joka jakaa sen puolen kahteen osaan samoin kuin ensimmäisen polygonin kohdalla. Tämä prosessi toistetaan rekursiivisesti, kunnes kaikki näkymän polygonit ovat BSPpuussa. (Akenine-Möller & Haines 2002, 351 353.) Kuviossa 10 on kuvattu polygonit A-G. Tila on ensin jaettu polygonin A suuntaisella tasolla ja jäljelle jääneet puolet taas jaettu polygonien B ja C suuntaisilla tasoilla. Polygonin B muodostama jakotaso leikkaa vasemmalla alhaalla olevan polygonin, joten se on jaettu kahteen osaan D ja E. Oikealla on kuvattu muodostunut puurakenne.
18 KUVIO 10. Polygonisuunnattu BSP-puu kaksiulotteisessa avaruudessa BSP-puiden käyttö näkyvyysoptimoinnissa BSP-puulla on mielenkiintoinen ominaisuus, joka mahdollistaa sen sisältämän geometrian järjestämisen mitä tahansa pistettä vasten, jos se käydään läpi tietyllä tavalla. Tämä on merkittävä etu rajoitustilavuushierarkioihin (ks. luku 3.2.2) nähden, joilla ei ole mahdollisuutta geometrian järjestämiseen. (Emt., 349.) BSP-puun rakentaminen Esimerkki BSP-puun rakentamisesta C++ pseudokoodina: struct BSP_puu { taso jakaja; // tämän solmun jakotaso lista polygonit; // tason kanssa samanlaiset polygonit BSP_puu *etu, // osoittimet solmun lapsiin *taka; }; void Rakenna_BSP_Puu(BSP_puu *puu, lista polygonit) { polygoni *juuri = polygonit.anna_listasta(); puu->jakaja = juuri->anna_taso(); puu->polygonit.lisää_listaan( juuri ); lista etu_lista, taka_lista;
19 polygoni *poly; while( (poly = polygonit.anna_listasta ())!= 0 ) { int i = puu->jakaja.luokittele_polygoni( poly ); switch( i ) { case SAMANLAINEN: puu->polygonit.lisää_listaan( poly ); break; case TAKANA: taka_lista.lisää_listaan( poly ); break; case EDESSÄ: etu_lista.lisää_listaan( poly ); break; } } case LEIKKAAVA: polygon *etuosa, *takaosa; Jaa_Polygoni( poly, puu->jakaja, etuosa, takaosa ); taka_lista.lisää_listaan( takaosa ); etu_lista.lisää_listaan( etuosa ); break; if( etu_lista.ei_tyhjä() ) { puu->etu = new BSP_puu; Rakenna_BSP_Puu( puu->etu, etu_lista ); } if( taka_lista.ei_tyhjä() ) { puu->back = new BSP_puu; Rakenna_BSP_Puu( puu->taka, etu_lista ); } } (BSP Tree Frequently Asked Questions 1998, kysymys 7).
20 BSP-puun järjestäminen BSP-puu voidaan helposti järjestää kameraa vasten seuraavanlaisella algoritmilla: void Piirrä_BSP_Puu( BSP_puu *puu, piste kamera ) { // katsotaan kummalla puolella jakotasoa kamera on real r = puu->partition.luokittele_piste( kamera ); if( r > 0 ) // kamera on tason edessä { Piirrä_BSP_Puu( puu->taka, kamera ); puu->polygonit.piirrä_polygoni_lista(); Piirrä_BSP_Puu( puu->etu, kamera ); } else if( r < 0 ) // kamera on tason takana { Piirrä_BSP_Puu( puu->etu, kamera ); puu->polygonit.piirrä_polygoni_lista(); Piirrä_BSP_Puu( puu->taka, kamera ); } else // r = 0, kamera on jakotasolla { Piirrä_BSP_Puu( puu->etu, kamera ); Piirrä_BSP_Puu( puu->taka, kamera ); } } (BSP Tree Frequently Asked Questions 1998, kysymys 9). 3.2.4 Kasipuut ja nelipuut Kasipuun (engl. octree) rakenne on samankaltainen kuin perusakselien suuntaisen BSP-puun. Koko näkymän sisältävä perusakselien suuntainen rajoituskuutio jaetaan jokaisen akselin suuntaisesti kahdeksaan osaan täsmälleen keskeltä niin, että jäljelle jää kahdeksan keskenään saman kokoista kuutiota. Jäljelle jääneet kuutiot jaetaan taas samalla tavalla kahdeksaan osaan ja prosessia toistetaan rekursiivisesti kunnes joku ennalta määrätty kriteeri täytetään. Tämä kriteeri on yleensä rekursiotason yläraja tai yhden kuution sisällä olevien geometriaprimitiivien alaraja. Kasipuuta voidaan käyttää samaan tapaan kuin perusakselien suuntaista BSP-puuta. (Akenine-Möller & Haines 2002, 353.)
21 Nelipuu (engl. quadtree) on vastaava tietorakenne kaksiulotteisena. Haluttu alue rajataan ensin neliöllä joka sitten jaetaan neljään yhtä suureen osaan rekursiivisesti samaan tapaan kuin kasipuu. Nelipuita käytetään yleisesti nopeuttamaan maaston ja muiden laajojen suhteellisen tasaisten alueiden piirtoa. Kuviossa 11 nähdään, miten nelipuu rakentuu näkymässä olevien objektien ympärille. Kaikki solut, joiden sisään jää objektin osia, jaetaan aina rekursiivisesti uudestaan pienempiin osiin. Kasipuu rakennetaan täsmälleen samalla tavalla, mutta mukaan otetaan kolmas ulottuvuus, jota ei tässä selkeästi voi kaksiulotteisella kuvalla havainnollistaa. Kasi- ja nelipuuta käytetään usein yhdessä hierarkkisen näköpyramidierottelun kanssa (ks. luku 3.3.4). KUVIO 11. Kuvasarja nelipuun rakentamisesta Mukautuva kasipuu Kasi- ja nelipuusta voidaan tehdä myös mukautuva versio (engl. adaptive octree/quadtree), jossa jakotasojen leikkauspisteen ei tarvitse olla jaettavan alueen keski-
22 pisteessä. Näin geometria voidaan tarvittaessa jakaa tarkemmin ja tasaisemmin. (Kmett, E. 1999.) 3.2.5 Näkymäverkot Kaikki edellä mainitut tilanjakotietorakenteet käyttävät jonkinlaista puuta perustietorakenteenaan ja eroavat toisistaan siinä, miten tilanjako tehdään ja miten geometria tallennetaan. Ne tallentavat kuitenkin vain geometriadataa, kolmiulotteisen näkymän piirtämisessä tarvitaan paljon muutakin. Näkymäverkko (engl. scene graph) on korkean tason puumainen tietorakenne, joka voi sisältää geometriadatan lisäksi mm. tekstuureita, muunnoksia, valonlähteitä ja lähes mitä tahansa muuta tarpeellista. Valonlähteen voi esimerkiksi sijoittaa johonkin sisäiseen solmuun, jolloin se valaisee vain sen alla olevat objektit. (Akenine-Möller & Haines 2002, 355.) Näkymäverkon solmuilla on usein rajoitustilavuus ja se on näin ollen melko samanlainen rakenne rajoitustilavuushierarkian kanssa. Näkymäverkon lehti pitää sisällään geometriadataa ja usein tämän datan voikin tallentaa minkä tahansa tilanjakotietorakenteen avulla, joten näkymäverkon solmu voi pitää sisällään kokonaisen objektin esimerkiksi BSP-puuna. (Emt., 355.) Myös objektien animointi onnistuu näkymäverkon avulla. Eräs tapa on asettaa tarvittavat muunnokset verkon sisäisiin solmuihin, näin kaikki muunnokset ajetaan myös solmun alipuulle. (Emt., 356.) Klassinen esimerkki tästä on aurinkokunta, jossa planeetat kiertävät aurinkoa ja planeettoja kiertää yksi tai useampi kuu. Kuviossa 12 on kuvattuna yksinkertaisen aurinkokunnan muodostava näkymäverkko. Piirtäminen aloitetaan auringosta, sen jälkeen asetetaan pyöritysmuunnos muunnospinon päälle ja piirretään planeetta 1. Tämän jälkeen asetetaan seuraava muunnos muunnospinon päällimmäiseksi ja piirretään kuu 1, joka kiertää ensimmäistä planeettaa. Nyt muunnospinon päällimmäinen muunnos poistetaan, jolloin muunnostaso on jälleen aurinkoa kiertävä. Piirretään planeetta 2 ja asetetaan seuraava muunnos pinon päälle. Nyt voidaan piirtää jäljellä olevat kuut ja tyhjentää muunnospino kokonaan, koska piirrettävää ei enää ole. Planeetoille voitaisiin asettaa omat muunnokset, jolloin ne saataisiin kiertämään aurinkoa eri nopeuksilla. (Foster 2003)
23 KUVIO 12. Näkymäverkon havainnollistaminen Näkymäverkko voi toimia myös rajoitustilavuushierarkiana jos solmuihin tallennetaan solmun ja sen alla oleva rajoitustilavuus. 3.3 Erottelumenetelmät 3.3.1 Yleistä Erottelu (engl. culling) tarkoittaa laumasta poistamista. Tietokonegrafiikassa tämä lauma on koko näkymä joka halutaan piirtää, ja josta on tarkoitus erotella kaikki se geometria jonka ei katsota sisältyvän lopulliseen kuvaan. Grafiikan yhteydessä puhutaankin usein näkyvyyserottelusta (engl. visibility culling). Erottelua voidaan tehdä myös muualla, kuten törmäystarkistuksissa, fysiikan laskemisessa ja tekoälyssä. (Akenine-Möller & Haines 2002, 357.) 3.3.2 Taustapintojen erottelu Taustapintojen erottelu (engl. backface culling) tarkoittaa niiden kolmioiden erottelemista, joiden pinta osoittaa pois päin kamerasta. Suljetuissa objekteissa (esim. pallo)
24 ei pitäisi koskaan olla taustapintoja näkyvissä, ellei ole tarvetta päästä objektin sisään. Taustapintojen erottelu ei ole välttämätöntä lopputulos on joka tapauksessa oikea, koska taustapinnat peittyvät etupuolella olevien pintojen taakse. Näkymättömien kolmioiden piirtoon ei kuitenkaan kannata käyttää turhaan aikaa, joten yleensä taustapintojen erottelu kannattaa tehdä, koska ainakin teoriassa noin puolet kolmioista on taustapintoja. (Dunn & Parberry 2002, 380.) On olemassa ainakin kaksi tapaa tutkia osoittaako kolmion pinta poispäin kamerasta. Ensimmäinen tapa suoritetaan ennen projektiota kamera-avaruudessa. Ideana on tutkia onko kamera kolmion pinnan etu- vai takapuolella, kuviossa 13 on tummalla merkitty ne kolmiot jotka piirretään tämän testin perusteella ja harmaalla ne, jotka jätetään piirtämättä. Kuviosta näkyy myös, että sillä ei ole merkitystä onko kolmio näköpyramidin (ks. luku 3.3.4) sisällä. (Emt., 380.) KUVIO 13. Taustapintojen erottelu Taustapintojen erotteluun kamera-avaruudessa tarvitaan kolmion tason normaalivektori ja vektori kamerasta mielivaltaiseen kolmion kärkipisteeseen. Jos näiden vektorien välinen kulma on yli 90 astetta (pistetulo suurempi kuin 0), niin kolmion pinta osoittaa poispäin kamerasta. (Emt., 381.)
25 Edellistä erottelutapaa käytettiin pääasiassa ohjelmistopohjaisissa 3D-moottoreissa kun kolmion normaalivektori voitiin esilaskea ja tallentaa muistiin kolmioiden mukana. Nykyään suurin pullonkaula on geometriadatan siirtäminen 3Dkiihdytinlaitteistolle, joten kaikki ylimääräinen tieto jätetään pois laitteistolle siirrettäessä. Nykyisillä laitteilla taustapintojen erottelu suoritetaan näyttöavaruudessa riippuen siitä onko kolmion kärkipisteet määritelty myötä- vai vastapäivään (ks. kuvio 7). (Emt., 381.) Kuviossa 14 oikean puoleisen kolmion kärkipisteet ovat vastapäivään kameraan nähden, joten se jätetään piirtämättä. KUVIO 14. Taustapintojen erottelu kolmion kärkipisteiden määrittelysuunnan mukaan 3.3.3 Ryhmitelty taustapintojen erottelu Kolmioista voidaan muodostaa ryhmiä ja suorittaa yksi erottelutesti ryhmää kohden, jolloin saadaan huomattava nopeusetu yksittäisten taustapintojen erotteluun verrattuna. Ryhmiteltyyn taustapintojen erotteluun (engl. clustered backface culling) on olemassa muutamia erilaisia tapoja, mutta perusajatuksena kuitenkin on yleensä muodostaa ns. normaalikartio (engl. normal cone), johon kerätään lähes samansuuntaisten pintojen normaalivektorit. Näin yksi testi kartiota vasten voi kertoa ovatko sen sisältämät taustapinnat näkyvissä vai eivät. (Akenine-Möller & Haines 2002, 362.)
26 3.3.4 Hierarkkinen näköpyramidierottelu Vain ne primitiivit, jotka ovat osittain tai kokonaan näköpyramidin (engl. view frustum) sisällä, pitää piirtää. Eräs tapa nopeuttaa piirtämistä, on verrata objektin rajoitustilavuutta pyramidiin. Jos rajoitustilavuus on pyramidin ulkopuolella, voidaan sen sisältämä geometria jättää piirtämättä. Akenine-Möller ja Haines (2002, 363 365.) kuvaavat menetelmää luvun 3.3.4 mukaisesti. Käyttämällä tilanjakotietorakenteita, tällainen erottelu voidaan suorittaa hierarkkisesti. Rajoitustilavuushierarkialle tämä toimii siten, että käydään puu läpi juuresta alkaen ja testataan jokaisen solmun rajoitustilavuus näköpyramidia vasten. Jos rajoitustilavuus on pyramidin ulkopuolella niin koko sen alipuu voidaan hylätä heti, koska koko alipuu on kyseisen rajoitustilavuuden sisällä. Jos rajoitustilavuus leikkaa näköpyramidia niin puun läpikäyntiä jatketaan ja solmun lapset testataan pyramidia vasten. Jos kyseessä on puun lehtisolmu jolla ei ole lapsia, niin koko geometria joudutaan piirtämään vaikka kaikki sen sisältämät primitiivit eivät välttämättä ole näköpyramidin sisällä. Jos rajoitustilavuus on kokonaan pyramidin sisällä, niin puun läpikäyntiä jatketaan, mutta lapsia ei ole enää syytä tarkistaa pyramidia vasten, koska kaikki ovat varmasti sen sisällä. Kuviossa 15 vasemmalla on kuvattu kuusi objektia ja niiden rajoitustilavuudet (pallot). Oikealla näkyy miten hierarkia käydään läpi. Näkymän juurisolmun rajoitustilavuus leikkaa näköpyramidia, joten kaikki sen lapset käydään läpi. Näkökentän vasemmassa reunassa oleva rajoitustilavuus leikkaa näköpyramidia, joten senkin lapset käydään erikseen läpi. Suunnikkaan rajoitustilavuus leikkaa näköpyramidia ja se piirretään, mutta tähti jää kokonaan pyramidin ulkopuolelle. Etualalla oleva neliö on kokonaan näköpyramidin sisällä samoin kuin oikeanpuoleinen hierarkia kokonaisuudessaan, joten nämä kaikki voidaan piirtää suoraan eikä lapsisolmuja enää tarvitse käydä erikseen läpi.
27 KUVIO 15. Hierarkkinen näköpyramidierottelu Muitakin tietorakenteita kuin rajoitustilavuushierarkiaa voidaan käyttää näköpyramidierottelun kanssa. Hyviä esimerkkejä ovat kasipuut ja binääriset tilanjakopuut. 3.3.5 Portaalit Portaalit (engl. portals) ovat erinomainen tapa nopeuttaa sisätilojen, kuten talojen piirtämistä. Sisätiloissa seinät peittävät usein suuren osan näkymästä, joten ideana on suorittaa näköpyramidierottelu jokaisen portaalin (esim. ikkunat ja ovet) läpi. Kun portaalia käydään läpi, näköpyramidi pienennetään mahdollisimman tarkasti sen ympärille. Kaikki pyramidin ulkopuolelle jäävät portaalit hylätään. (Akenine-Möller & Haines 2002, 365 366.) Portaalierottelumenetelmät esikäsittelevät näkymän jollain tavalla, joko automaattisesti tai manuaalisesti. Näkymä jaetaan soluihin, jotka yleensä kuvaavat rakennuksen huoneita tai käytäviä. Portaalit ovat yleensä ovia tai ikkunoita, jotka yhdistävät vierekkäisiä huoneita toisiinsa. Kaikki solun sisällä olevat objektit ja geometria tallennetaan tietorakenteeseen joka yhdistetään soluun, lisäksi soluun tallennetaan tieto viereisistä huoneista (soluista) ja portaaleista jotka yhdistävät niihin. (Emt., 366.) Kuviossa 16 on kuvattu talo joka on jaettu soluihin A H. Katsoja on solussa A, joten se ja kaikki sen sisältämät objektit piirretään ensimmäisenä. A:n naapurisolut ovat B, E ja F, joista soluun F johtava portaali ei ole näköpyramidin sisällä joten se voidaan hylätä. Näköpyramidi pienennetään soluun B johtavan portaalin kokoiseksi ja solun B sisältö piirretään. Solun B naapureina on C ja D, joista D ei ole näkyvissä joten näkö-
28 pyramidi pienennetään jälleen soluun C johtavan portaalin mukaan. C:ssä ei ole muita naapurisoluja, joten palataan takaisin ja sovitetaan näköpyramidi solusta A soluun E. E:llä on naapurisoluja, mutta kaikki portaalit jäävät näköpyramidin ulkopuolelle, joten piirtäminen voidaan lopettaa. KUVIO 16. Portaalierottelu Portaaleja voidaan käyttää myös muuhunkin kuin näkyvyysoptimointiin. Esimerkiksi peilejä on erittäin helppo tehdä muuntamalla kamera peilitason toiselle puolelle. Myös erilaisia taittoefektejä voidaan tehdä, jolloin näyttää siltä kuin portaalin takana näkyvä maisema olisi vääristynyt. Portaalit voivat olla myös yksisuuntaisia. Esimerkki: pelihahmo kävelee portaalin läpi solusta A soluun B. Jos portaali on yksisuuntainen, niin solusta B ei pääse enää takaisin soluun A, sen sijaan B:stä katsoen voi näkyä kolmas solu C. Tämä on erinomainen tapa luoda esimerkiksi erittäin vaikea sokkelo. (Emt., 368.) Aiheeseen voi tutustua lisää esimerkiksi Jacco Bikkerin (1999) artikkelista, jossa hän kertoo muutamista menetelmistä joilla portaalierottelua voidaan nopeuttaa entisestään.
29 3.3.6 Tarkkuustasot Tuhansista kolmioista koostuva objekti näyttää hyvältä, kun se on lähellä kameraa. Näin tarkan objektin piirtäminen on suhteellisen hidasta, mutta hyväksyttävää visuaalisen näyttävyyden takia. Kun objekti siirretään kauas kamerasta, tuhansien kolmioiden tarjoama tarkkuus ei ole enää huomattavissa, koska objekti saattaa olla ruudulla enää muutaman kuvapisteen kokoinen. Käsiteltävien kolmioiden lukumäärän pitäisi olla suhteellinen siihen, miten suurena objekti näkyy ruudulla. (Eberly 2001, 359.) Kuviossa 17 on kuvattuna teekannu eri tarkkuustasoilla (engl. level of detail, LOD). Ylempi kuva esittää kannun kuvautumista ruudulle, alempi tarkempaa esitystä geometriasta. Karkein versio koostuu 64 kolmiosta, kun taas hienoin sisältää 1600 kolmiota. KUVIO 17. Teekannu ja sen eri tarkkuustasoja Yksityiskohtien erottelu Yksityiskohtien erottelu (engl. detail culling) on menetelmä, jossa kuvan laatua heikennetään nopeuden kustannuksella. Näkymästä voidaan poistaa pieniä ja vähän merkitseviä yksityiskohtia, kun katsoja on liikkeessä. Kun katsoja pysähtyy, yksityiskohtien erottelu kytketään pois päältä jolloin yksityiskohdat tulevat näkyviin. Yksityiskohtien erottelu voidaan suorittaa myös hierarkkisesti näkymäverkossa. (Akenine- Möller & Haines 2002, 368.) 3.3.7 Katve-erottelu Jokainen katsojalle näkyvä objekti on potentiaalinen katvealueen muodostaja (engl. occluder). Tällaisen objektin taakse muodostuu kolmiulotteiseen avaruuteen alue, jossa sijaitsevat objektit tai niiden osat ovat näkymättömissä katsojalle, eli katveessa.
30 Katve-erottelussa (engl. occlusion culling) tällaiset olevat mallit pyritään tunnistamaan ja poistamaan mahdollisimman aikaisessa vaiheessa. Katve-erottelumenetelmät voidaan jakaa kahteen pääryhmään, piste- ja solupohjaisiin menetelmiin. Pistepohjaisessa menetelmässä käsitellään sitä, mikä näkyy yhdestä pisteestä katsottuna. Solupohjaisessa menetelmässä sen sijaan käsitellään jostain tilasta, yleensä pallosta tai kuutiosta, näkyvää geometriaa. Näkymättömän objektin täytyy olla näkymätön kaikista pisteistä solun sisällä. Tässä on etuna se, että tietoa voidaan käyttää yleensä muutaman ruudunpäivityksen ajan, sen sijaan että tehtäisiin näkyvyyslaskelmat joka ruudunpäivityksen yhteydessä. Solupohjaisen menetelmän laskenta on kuitenkin hyvin hidasta, joten se tehdäänkin yleensä esikäsittelyvaiheessa eikä reaaliaikaisena. (Akenine-Möller & Haines 2002, 369 371.) Solupohjaisen menetelmän yhteydessä puhutaan yleensä potentiaalisista näkyvyysjoukoista (engl. potentially visible sets). Tämä on yleisnimitys menetelmälle, jossa jokaista solua kohden tallennetaan tieto siitä, mitä muita soluja on näkyvissä mistä tahansa pisteestä solun sisällä. (Dunn & Parberry. 2002, 402 403.) Katve-erottelumenetelmät voidaan ryhmitellä myös sen perusteella, missä avaruudessa ne toimivat. Kuva-avaruudessa toimivat algoritmit suorittavat näkyvyystestit kaksiulotteisessa avaruudessa projektiovaiheen jälkeen, kun malliavaruuden algoritmit käsittelevät alkuperäisiä kolmiulotteisia objekteja. (Akenine-Möller & Haines 2002, 371.) Katve-erottelu on ehkä laajin ja haastavin näkyvyysoptimoinnin osa-alue ja pelkästään siitä saisi aikaan kymmeniä sivuja tutkimusta. Tästä syystä katve-erottelumenetelmiä ei tässä tarkastella lähemmin, lisäksi katve-erottelun toteuttaminen DieselEngineen vaatisi muutoksia kirjaston sisäiseen toimintaan. Esimerkiksi Akenine-Möller & Haines (2002, 373 389.) käsittelevät teoksessaan kuusi erilaista katveerottelualgoritmia, joilla saa aiheesta hyvän yleiskuvan. 3.4 Muita menetelmiä Edellä kerrottiin yleisesti vain yleisimmistä optimointimenetelmistä. On olemassa huomattava määrä muita, myös edistyneempiä ja monimutkaisempia menetelmiä, joita
31 ei pelkästään niiden suuren määrän takia tässä tarkemmin käsitellä. Aiheesta kiinnostuneiden kannattaa tutustua mm. suomalaisen Hybrid Graphicsin kehittämän dpvs -kirjaston manuaaliin (Aila & Miettinen 2001, 247 283.), jossa kerrotaan tiiviissä paketissa monista erilaisista optimointimenetelmistä. Lisäksi internetistä ja kirjallisuudesta löytyy todella paljon materiaalia aiheesta.
32 4 DIESELENGINEN OPTIMOINTI 4.1 Mobiililaitteiden rajoitukset Vaikka mobiililaitteet ovatkin kehittyneet viime aikoina huimasti, on niissä vielä puutteita erityisesti niiltä osin mitä reaaliaikaisen kolmiulotteisen grafiikan esittäminen vaatii. Simon Jacobs (2005) luettelee paperissaan mm. seuraavia eroja pöytäkoneiden ja mobiililaitteiden välillä. Ei 3D-suoritinta Nykyisissä mobiililaitteissa ei ole kolmiulotteisen grafiikan piirtämiseen tarkoitettua laitteistoa (Jacobs 2005). Tämä saattaa kuitenkin muuttua lähiaikoina, koska esimerkiksi suomalainen BitBoys Oy kehittää mobiililaitteille tarkoitettuja 3Dgrafiikkasuorittimia. Ei liukulukusuoritinta Reaaliaikaisen kolmiulotteisen grafiikan esittämineen tarvitaan suuri määrä liukulukulaskuja. Suuressa osassa mobiililaitteita ei ole lainkaan liukulukusuoritinta, joten kaikki liukulukulaskut joudutaan laskemaan ohjelmistopohjaisesti, mikä on moninkertaisesti hitaampaa laitteistopohjaiseen verrattuna. Ongelma voidaan välttää käyttämällä kiinteän pilkun laskentaa (engl. fixed point arithmetic). Lisäksi täytyy pyrkiä käyttämään esilaskettuja taulukoita mm. trigonometristen funktioiden tuloksista. (Emt.) Vaikka DieselEnginessä onkin valmiit rutiinit kiinteän pilkun laskentaan, niin lyhyt katsaus aiheeseen lienee paikallaan. Kiinteän pilkun laskenta on tapa esittää liukulukuja kokonaislukuina, joissa on kuvitteellinen desimaalipilkku jakamassa kokonaisluku- ja murto-osan. Kuvitteellisen desimaalipilkun vasemmalla puolella olevat bitit määrittelevät kokonaislukuosan ja pilkun oikealla puolella olevat murto-osan (Kolli 2003). Kiinteää pilkkua käytettäessä tarkkuus on huonompi kuin liukuluvuilla, mutta vastaavasti saadaan huomattava nopeusetu liukulukulaskentaan nähden. Erityisesti mobiili-
33 laitteilla huonommasta tarkkuudesta ei ole haittaa pienen näyttökoon ansiosta. Aiheeseen kannattaa tutustua tarkemmin esim. Gopi Kollin artikkelista (2003). Ei laitteistopohjaista jakolaskua Kaikissa laitteissa ei ole laitteistopohjaista kokonaislukujen jakolaskua. (Jacobs 2005.) Suorittimen nopeus Suorittimen nopeus vaihtelee huomattavasti eri laitteiden välillä, mutta on joka tapauksessa paljon hitaampi kuin pöytäkoneissa. Tämä rajoittaa suurten ja monimutkaisten näkymien käsittelyä ja erityisesti valaistusta, jonka laskeminen vaatii paljon tehoa suorittimelta. (Emt.) 4.2 DieselEnginen rakenne DieselEnginen 3D-moottorin ydin on CDiesel3DDevice-luokka, joka hoitaa kaikki 3D-muunnokset, valaistukset ja piirtämisen. Luokan voidaan siis ajatella olevan DieselEnginen esitysliukuhihna. Sen lisäksi toinen tärkeä luokka on CDiesel3DScene, joka toimii varastona 3D-objekteille, valoille, materiaaleille, ym. mitä näkymässä voi olla. Luvussa 4.2 esitetyt DieselEnginen tekniset tiedot löytyvät dokumentaatiosta (DieselEngine SDK documentation 2004). CDiesel3DScene-luokalla on juuriobjekti, jonka lapsiksi voidaan liittää mitä tahansa IDiesel3DObject-rajapinnan toteuttavia objekteja. Kaikki nämä lapsiobjektit päivitetään ja piirretään automaattisesti, kun luokan päivitys- ja piirtometodeita kutsutaan. Kyseessä on siis näkymäverkon kaltainen tietorakenne. Kuvion 18 UML-kaaviossa on kuvattu DieselEnginen 3D-moottorin keskeisiä luokkia ja niiden välisiä suhteita.
34 KUVIO 18. UML-kaavio DieselEnginen 3D-moottorin keskeisistä luokista 4.3 Suunnitelmasta toteutukseen 4.3.1 Lähtökohdat Koska DieselEnginen lähdekoodit eivät ole käytettävissä, täytyy optimointi toteuttaa laajentamalla nykyistä toiminnallisuutta. Tämä rajoittaa erilaisten optimointimenetelmien käyttöä jonkin verran, mutta toisaalta moottorin nykyinen rakenne mahdollistaa muutaman muun menetelmän käytön hyvin. DieselEnginen luokat on rakennettu niin, että niitä voidaan helposti laajentaa periyttämällä niistä uusia luokkia, joissa on laajennettu tai erilainen toiminnallisuus. Näkyvyysoptimointia ajatellen oleellinen luokka on CDiesel3DScene, joka hoitaa koko näkymän objektien päivityksen, ylläpidon ja piirtämisen.
35 4.3.2 Suunnitelma Primitiivitason tilanjakotietorakenteiden kuten binääristen tilanjakopuiden toteuttaminen olisi varsin suuri työ, koska niitä varten täytyisi päästä käsiksi DieselEnginen geometriakäsittelyihin primitiivitasolla. Tämä ei ole käytännössä mahdollista, joten järkevämpää on käyttää näkymäverkkoon sopivia menetelmiä kuten objektitason rajoitustilavuushierarkiat, tarkkuustasot ja hierarkkinen näköpyramidierottelu. Kaikki nämä voidaan toteuttaa laajentamalla nykyistä toiminnallisuutta sen sijaan, että muutettaisiin ja kirjoitettaisiin uudestaan DieselEnginen matalan tason primitiivien käsittelyä. DieselEnginen objektiluokka sisältää jo oman rajoitustilavuutensa, joten rajoitustilavuuksia varten ei ole välttämätöntä tehdä uutta luokkaa. Näköpyramidierottelua varten sen sijaan joudutaan periyttämään CDiesel3DScene-luokasta uusi versio, joka laskee näkymästä näköpyramidin reunat ja tutkii ovatko objektit sen sisä- vai ulkopuolella. Lisäksi tarkkuustasoja varten tarvitaan oma luokka. Kuvion 18 UML-kaaviossa on kuvattu uudet luokat suhteessa entiseen luokkarakenteeseen.
36 KUVIO 19. Päivitetty UML-kaavio CDiesel3DSceneF CDiesel3DSceneF luokka jatkaa normaalin näkymäluokan toimintaa lisäämällä siihen toiminnallisuuden näköpyramidin laskemiseksi ja metodit, joilla voidaan tarkastella onko objektin rajoitustilavuus näköpyramidin sisällä vai ei. Näkymäluokka ei pääse helposti käsiksi kaikkiin sen sisältämiin objekteihin, joten optimoitavaksi valittujen objektien osoitteet täytyy lisätä omaan listaansa AddCullableObject metodilla. Tämän jälkeen optimointi toimii automaattisesti. Taulukossa 1 on eriteltynä luokan päivitetyt metodit ja niiden toiminta.
37 TAULUKKO 1. CDiesel3DSceneF -luokan operaatiokutsut ja kuvaukset OPERAATIOKUTSU SELITYS AddCullableObject(pObject) Lisää objektin optimoitavien listalle. ClassifyPoint(Plane, vpoint) : INT Tutkii onko piste tason positiivisella vai negatiivisella puolella ja palauttaa 1 tai 1 sen mukaan. IsInside(vPoint) : BOOL Tutkii onko parametrina annettu piste näköpyramidin sisällä vai ei. Palauttaa totuusarvon TRUE/FALSE pisteen sijainnista riippuen. Olettaa, että UpdateViewFrustum on kutsuttu asianmukaisesti. IsInside(vPoint, fradius) : BOOL Sama kuin edellä, mutta tutkii pisteen sijaan onko pallo (tai osa siitä), jonka säde annetaan toisena parametrina, näköpyramidin sisällä vai ei. NormalizePlane(Plane) Normalisoi, eli muuntaa tason normaalivektorin yksikkövektoriksi. Taso täytyy normalisoida, jotta voidaan laskea oikea pisteen ja tason välinen etäisyys. PlaneDistanceToPoint(Plane, vpoint) : FLOAT Laskee pisteen etäisyyden tasosta, vaatii että taso on normalisoitu. Update(fFrametime) Kutsuu kantaluokan Update-metodia ja päivittää näköpyramidin. UpdateViewFrustum(bNormalize = FALSE) Päivittää näköpyramidin sen hetkisten näkymäja projektiomatriisien avulla. Parametrilla bnormalize voidaan määritellä normalisoidaanko näköpyramidin reunat määrittävät tasot vai ei.
38 CDiesel3DLODSwitch CDiesel3DLODSwitch luokka toteuttaa näkymäverkon puurakenteeseen tarkkuustasovalinnan. Tarkkuustason valintaan voidaan käyttää kriteereinä objektin liike- tai pyörimisnopeutta tai objektin etäisyyttä kamerasta. Kriteerit valitaan asettamalla objektille liput ede_visibility_lod_range tai ede_visibility_lod_velocity. Luokka ei luo automaattisesti eri tarkkuustasoja, vaan käyttäjän tehtävä on asettaa halutuille objekteille eri tarkkuustasoja näkymää luodessaan. Tarkkuustasot lisätään AddChild-metodilla aloittaen tarkimmasta. Kun tarkkustasot on lisätty, Init metodilla alustetaan objekti, jonka jälkeen se toimii automaattisesti normaalin näkymän päivityksen mukana. Taulukossa 2 on eriteltynä luokan päivitetyt metodit ja niiden toiminta. TAULUKKO 2. CDiesel3DLODSwitch -luokan operaatiokutsut ja kuvaukset OPERAATIOKUTSU SELITYS Init(fFrequency = 2, dwrange = 0) Alustaa objektin. Parametrina voidaan antaa päivitystiheys ja etäisyys, jonka jälkeen tarkkuustaso muuttuu. Jos etäisyyttä ei anneta, se määritellään automaattisesti objektin rajoitustilavuuden perusteella. Alustus täytyy suorittaa aina sen jälkeen, kun tarkkuustasot on lisätty. Render(dwData) : DE_RETVAL Piirtää oikean objektin valitun tarkkuustason perusteella. Update(fFrameTime, dwdata) Päivittää objektin ja valitsee automaattisesti sopivan tarkkuustason.
39 5 TULOKSET 5.1 Miten testattiin? Tulosten tarkastelua varten rakennettiin testisovellus, joka pyörittää näkyvyysoptimoinnin kannalta raskasta näkymää sekä näkyvyysoptimointien kanssa, että ilman optimointeja. Sovellus laskee ajon aikana tilastoa siitä, miten nopeasti näyttöä päivitetään ja kertoo lopuksi keskimääräisen ruudunpäivitysnopeuden eli FPS-arvon. Kehitysvaiheen testeissä kävi ilmi, että tarkkuustaso-optimointi ei nopeuttanut piirtämistä käytännössä yhtään. Yksi syy tähän saattaa olla se, että testisovelluksen objektit ovat varsin yksinkertaisia. Tästä johtuen tarkkuustaso-optimointi jätettiin lopullisesta testisovelluksesta kokonaan pois. Taulukossa 3 on lueteltuna kaikki testatut laitteet ja muutamia oleellisia teknisiä tietoja niistä. TAULUKKO 3. Testatut laitteet LAITE SUORITIN NÄYTTÖ ALUSTA Compaq ipaq (PocketPC) 206 MHz 240 x 320 Windows CE Siemens SX1 130 MHz 176 x 220 Symbian OS 6.1 Nokia 6260 123 MHz 176 x 208 Symbian OS 7.0 Nokia N-Gage 104 MHz 176 x 208 Symbian OS 6.1
40 5.2 Tulosten tarkastelu Pienenä yllätyksenä optimoidun version nopeus oli kaikilla laitteilla lähes kaksinkertainen entiseen verrattuna (ks. kuvio 20). Yllätys tämä oli siksi, että toteutettu optimointi on todella yksinkertainen. Tästä voi kuitenkin päätellä, että käyttämällä tilanjakotietorakenteita ja tehokkaampia optimointimenetelmiä, nopeus voitaisiin saada moninkertaiseksi aiempaan verrattuna. KUVIO 20. Testitulosten vertailu
41 6 AJATUKSIA 6.1 Yleistä Näkyvyysoptimointi on hyvin laaja ja haastava, mutta myös erittäin mielenkiintoinen aihe. Työtä tehdessä huomasi miten monet seikat vaikuttavat siihen, millaisia optimointimenetelmiä käytetään. Mitään yleispätevää ratkaisua ei ole, vaan olemassa olevia menetelmiä joudutaan yhdistämään ja muokkaamaan tarpeiden mukaan, jolloin saadaan aikaan mahdollisimman optimaalinen lopputulos. Aiheesta löytyy todella paljon lähdemateriaalia sekä kirjallisuudesta, että internetistä. Lopulta kuitenkin erityisesti Akenine-Möller & Hainesin (2002) teos osoittautui selkeäksi yleisteokseksi lähes kaikkeen reaaliaikaisen kolmiulotteisen grafiikan muodostamiseen liittyvään. Aiheesta kiinnostuneiden ei kuitenkaan tarvitse kävellä kirjakauppaan, vaan mieleisellään hakukoneella löytää verkosta jo paljon materiaalia millä pääsee alkuun. 6.2 Ongelmat Vaikka näkymäverkon konsepti tuntui aluksi erittäin sopivalta DieselEngineen, oli totuus kuitenkin toinen. Näköpyramidierottelun toteuttaminen järkevästi oli todella hankalaa johtuen siitä miten näkymäluokka ja sen jäsenenä olevat objektit näkevät toisensa. Ongelman pystyi kiertämään luomalla listan niistä objekteista, jotka halutaan ottaa mukaan näkyvyysoptimointiin. Tehokkaamman ratkaisun olisi voinut tehdä suoraan alkuperäiseen näkymäluokkaan jos se vain olisi ollut mahdollista. Kiinteän pilkun laskennan käyttö ei ole DieselEnginessä täydellistä, monia laskutoimituksia lasketaan edelleen liukuluvuilla. Kun kyseessä on mobiililaitteille suunnattu pelimoottori, niin pitäisi ainakin olla mahdollisuus suorittaa aivan kaikki lasku-
42 toimitukset kiinteän pilkun laskennalla. Esimerkkinä tästä mainittakoon muunnosmatriisien pyytäminen, joka palauttaa vain liukulukumatriiseja. 6.3 Mitä jatkossa? Vaikka DieselEngineen toteutetulla yksinkertaisella optimointimenetelmällä saatiinkin aikaan varsin hyviä tuloksia, olisi siihen mahdollista tehdä vieläkin huomattavasti tehokkaampia optimointeja. Tämä ei kuitenkaan ole mahdollista laajentamalla nykyistä toiminnallisuutta kuten tässä työssä tehtiin, vaan itse pelimoottorin sisäiseen toimintaan täytyisi tehdä muutoksia. Kuten työssä saavutetuista tuloksista voi päätellä, että DieselEnginen suorituskykyä voitaisiin vielä huimasti parantaa. Suhteellisen pienellä työmäärällä 3D-moottori saataisiin huomattavasti nopeammaksi. Se toisi samalla DieselEnginen käyttäjille ja monille asiakkaille lisäarvoa sekä parantaisi tuotteen kilpailukykyä, kun voitaisiin tehdä entistä näyttävämpää kolmiulotteista grafiikkaa.