torstai 7. joulukuuta 2017

Siili, pikseli ja matriisi

(Valokuva: Kalle Gustafsson/Flickr. CC-BY 2.0.)

Viime kerralla kehitimme pelin, jossa siili napsii mansikoita. Siinä kuitenkin puhuimme ainoastaan pelimekaniikoista; nyt vuorossa on pelin siirtäminen ruudulle. Sitä ennen tarvitaan pieni selitys tietokonegrafiikan toiminnasta.

Nykytietokoneessa on kaksi suoritinta, jotka tekevät yhtäaikaisesti töitä. Ensimmäinen on yleispätevä ja pystyy monenlaisiin temppuihin. Se voi käsitellä tekstiä tai simuloida siilin nälkäistä mahaa. Toinen suoritin puolestaan keskittyy pelkästään grafiikkaan ja osaa lähinnä laskea vektoreilla. Ennen kaikkea se osaa laskea niillä valtavan paljon nopeammin kuin tavallinen suoritin.

Kolmiulotteinen grafiikka vaatii tätä laskentatehoa. Jokainen kappale voidaan jakaa kolmioiksi, joihin liittyvät laskutoimitukset ovat suhteellisen helppoja mutta lukumäärältään runsaita. Ja koska kaksiulotteinen maailma on siivu kolmiulotteista, samoja menetelmiä voidaan käyttää myös yksinkertaiseen siilipeliimme.

Tässä on suorakulmio. Se koostuu kahdesta kolmiosta, jotka kumpikin koostuvat kolmesta pisteestä. Jokaiseen kolmion kärkeen voidaan liittää koordinaatti siilin kuvassa.

Tietokoneen grafiikkapiirin silmin pelimaailmamme näyttää siis tältä:

Vektoreiden käsittelemiseen on yksi aivan erinomainen työkalu: matriisit. (Viimeksi matriisit pelastivat apteekkarin bisneksen.) Kokeillaan esimerkiksi kasvattaa siili kaksinkertaiseksi ja kääntää sitä hitusen vastapäivään.

Valitaan koordinaatit siten, että nolla on siilin keskellä. Nyt jokaiselle kärkipisteelle — esimerkiksi vaikka vasemmalle yläkulmalle — tehdään pikkuinen temppu:

\[ \begin{bmatrix} 1.9696 & -0.3473\\ 0.3473 & 1.9696 \end{bmatrix} \begin{bmatrix} -1\\ 1 \end{bmatrix} = \begin{bmatrix} -2.317\\ 1.622 \end{bmatrix} \]

Ta-daa! Matriisien avulla skaalaukset ja kierrot jonkin pisteen suhteen muuttuvat helpoiksi. Pienellä silmänkääntötempulla myös siirrot onnistuvat, mutta sitä varten pitäisi mennä yksityiskohtiin. Tässä tapauksessa matriisi saatiin kertomalla keskenään kaksi matriisia: ensimmäinen suurentaa kaksinkertaiseksi ja toinen kääntää kymmenen astetta.

\[ \begin{bmatrix} 2 & 0\\ 0 & 2 \end{bmatrix} \cdot \begin{bmatrix} \cos(10^\circ) & -\sin(10^\circ)\\ \sin(10^\circ) & \cos(10^\circ) \end{bmatrix} \]

Kolmiulotteisessa grafiikassa matriiseilla (jotka itse asiassa ovat neliulotteisia) saadaan kääntöjen, vääntöjen sekä siirtojen lisäksi aikaan tärkeä temppu: projektio. Kolmiulotteinen maailma pitää siirtää kaksiulotteiselle ruudulle, eikä siihen tarvita enempää kuin kameran paikan ja kuvakulman mukaan laskettu matriisi.

Toinen tapa ajatella (ja hyödyntää) projektiota on varjo. Jos (ja tämä on tähän aikaan vuodesta iso jos) liikut ulkona aurinkoisena päivänä, aurinko projisoi sinun kolmiulotteisuutesi maanpinnan kaksiulotteiseen tasoon. Toisaalta auringon näkökulmasta varjoa ei näy, vaan jos siellä olisi kamera, se näkisi vain sinut.

Lukiokamallakin on väliä!

Matriisit ovat todella nerokas keksintö, mutta ne kuitenkin ovat yliopistomatematiikkaa (pienin poikkeuksin). Onneksi myös lukion oppimäärällä saa paljon aikaan. Viimeksi emme muuta käyttäneetkään, nyt tehdään sen avulla graafista työtä.

Otetaan lamppu ja kolmio. Lamppu on kolmion päällä ja kolmio on jossakin kulmassa siihen nähden. Kuinka paljon lamppu valaisee kolmiota?

Ilmiselvästi valovoima on suurin, kun kolmio on kohtisuorassa lamppua kohden. Samoin selvästi kolmio ei saa yhtään valoa, jos se on suoraan valon suuntainen. Valon suunta on helppo piirtää, kuten myös kolmion suunta piirtämällä sille normaali:

Nyt siis valon halutaan olevan täysi, kun normaali on yhdensuuntainen valon kanssa, ja nolla, kun normaali on kohtisuorassa valoa vasten. Saatat muistaa, että kahden kohtisuoran vektorin pistetulo on $0$. Erityisesti jos kummankin vektorin pituus on yksi, niin $\bar a \cdot \bar b = \cos(a, b)$, enintään siis $1$. Juuri tätähän haimme, ja kosinin käytölle löytynee fysikaalinenkin perusta! Kaiken kukkuraksi pistetulo on aika simppeli laskea kolmiulotteisessa maailmassa:

\[ \bar v_1 \cdot \bar v_2 = x_1x_2 + y_1y_2 + z_1z_2. \]

Jokaiselle kolmiolle lankeava valo saadaan pistetulon avulla. Tämän ansiosta näytönohjaimet ovat pistetulohirmuja! Mutta mistä tuo normaali putkahti? Tähän mennessähän olemme puhuneet vain kärkipisteistä. No, lukion oppimäärän rajoilla loikoilee iloinen juttu nimeltä ristitulo.

Kahden vektorin ristitulo tuottaa vektorin, joka on kohtisuorassa kumpaakin vektoria vastaan. Toisin sanoen ottamalla kaksi kolmion sivua ja laskemalla niiden ristitulo saa kolmion normaalin! (Teknisesti ottaen saa kaksi vastakkaista normaalia. Näistä valitaan "ulospäin" osoittava, koska kolmiot ajatellaan yksipuolisiksi.)

Lukiossa vektoreista saa hyvän ja käytännöllisen maistiaisen, mutta pidemmälle mennessä niiden voima paljastuu toden teolla. Vektorit ja vektoriavaruudet ovat yhtä aikaa käytännönläheisiä ja teoreettisesti kiinnostavia, ja ne levittäytyvät oikeastaan kaikille tieteenaloille. Ja mitä pelien tekemiseen tulee, alasta haaveilevalle vektorikurssi voi osoittautua yhdeksi lukion tärkeimmistä.

Ei kommentteja:

Lähetä kommentti

Kommentit ovat moderoituja - yritän hyväksyä kommenttisi mahdollisimman pian. Voit kirjoittaa kommenttiin LaTeX-koodia - lue lisää Kommentointi-sivulta.