OpenGL/Perspektiiviprojektio

Kolmiulotteinen vektorigrafiikka perustuu huijaukseen, syvyysvaikutelmaan. Projektio kuvaa kolmiulotteisen avaruuden pisteitä kaksiulotteiselle tasolle. Tapoja on useita. Keskusprojektio vastaa ihmissilmän ja kameran toimintaa: kaukana olevat kohteet näyttävät pienemmiltä. Yhdensuuntaisprojektiossa ei ole syvyysvaikutelmaa, mutta se on erittäin hyödyllinen esimerkiksi pohjapiirustusten tekemiseen.

Projektion toteuttaminenMuokkaa

Syvyysvaikutelma ei ole vaikea ohjelmoida: Yksinkertainen perspektiiviprojektio saadaan aikaan jakamalla x ja y komponentilla z. Tällöin pakopiste on origossa. Helppoa, eikö totta?

OpenGL toteuttaa projektiot tietenkin matriisien avulla. Nurkkapisteet määritellään kolmiulotteisesti, mutta projektiomatriisi muuntaa ne lopullisiin paikkoihinsa xy-tasolla. Projektioille on oma matriisipino, GL_PROJECTION, joka suoritetaan viimeiseksi. Se on erillinen, koska yleensä sitä täytyy muuttaa vain harvoin. Kiinnostuksen herättämiseksi alla on lähdekoodi yleisimpien projektioiden aikaansaamiseksi:

void pikselikoordinaatisto(int leveys, int korkeus)
{
    glOrtho(0.0, (double) leveys, (double) korkeus, 0.0, -1.0, 1.0);
    // tai: gluOrtho2D(0.0, (double) leveys, (double) korkeus, 0.0);
}

void perspektiiviprojektio(int leveys, int korkeus)
{
    // lahi = 0.1; reuna = 0.05; muodostaa noin 70° näkökentän pystysuunnassa
    double etu = 0.1, taka = 100.0;
    double reuna = 0.05;
    double kuvasuhde = (double) leveys / (double) korkeus;
    glFrustum(-reuna * kuvasuhde, reuna * kuvasuhde, -reuna, reuna, etu, taka);
}

void perspektiiviprojektio2(int leveys, int korkeus)
{
    double etu = 0.1, taka = 100.0;
    double nakokentta = 90.0;
    double kuvasuhde = (double) leveys / (double) korkeus;
    gluPerspective(nakokentta, kuvasuhde, etu, taka);
}

void koon_muutos(int leveys, int korkeus)
{
    glViewport(0, 0, leveys, korkeus);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    pikselikoordinaatisto(leveys, korkeus);
    glMatrixMode(GL_MODELVIEW);
}

Keskusprojektio luo perspektiivikuvanMuokkaa

Kolmiulotteisen vektorigrafiikan kulmakivi, syvyysvaikutelma, saadaan aikaan keskusprojektiolla. Keskusprojektio voidaan määritellä seuraavalla tavalla: Avaruudessa on projektiotaso, joka kuvaa näyttöruutua tai kameran kennoa. Vain tason etupuolella olevat pisteet projisoidaan. Tason takana on piste, projektiokeskus. Piirrettävien muotojen jokaisesta nurkkapisteestä vedetään suora projektiokeskukseen. Suora leikkaa projektiotason. Leikkauspisteistä muodostuva kuva on perspektiivikuva, jossa on syvyysvaikutelma.

(Oikeasti OpenGL ei tee keskusprojektiota etsimällä leikkauspisteitä. Pisteiden kolmas ulottuvuus ei saa kadota, vaan syvyysarvokin täytyy laskea: sitä tarvitaan syvyyspuskuroinnissa ja perspektiivikorjatussa pintakuvioinnissa. Lisäksi näkymä on rajoitettu, sillä ikkunan koko on rajallinen. Sen ulkopuolelle jäävistä esineistä halutaan leikellä vain näkyvä osa, jotta rasterointi olisi nopeampaa. Sisäisesti OpenGL tekeekin keskusprojektion puristamalla näkymäpyramidi kuutioksi; tätä kutsutaan näkymäpyramidin normalisoinniksi.)

Reunoilta rajoitettua näkymää kuvaa näkymäpyramidi eli näkymäkartio (engl. view frustum tai view pyramid). Pyramidin kärki on projektiokeskuksessa. Tahkot määräävät kuva-alueen rajat. OpenGL:ää varten täytyy myös määrittää kuva-alueen etu- ja takaseinä; etuseinä siis katkaisee pyramidin kärjen. Muodot projisoidaan etuseinälle, ja liian edessä tai kaukana olevat muodot jätetään piirtämättä.

Näkymäpyramidin mukainen keskusprojektio toteutetaan matriisina:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(vasen, oikea, ala, ylä, etu, taka);
glMatrixMode(GL_MODELVIEW); // seuraavaksi siirrellään esineitä

glFrustum kertoo nykyisen matriisin matriisilla, joka tekee annettua näkymäpyramidia vastaavan projektion. Parametrit vasen, oikea, ala ja ylä määräävät näkymäpyramidin etuseinän reunat. Parametrit etu ja taka ovat etu- ja takaseinän etäisyydet projektiokeskuksesta. Projektiokeskus on aina origossa ja pyramidin etu- ja takaseinät xy-tason suuntaisia: kameran sijainnin määrittely ei yleensä kuulu projektiomatriisipinon tehtäviin.

Näkymäpyramidin rajojen määrittely on hieman hankalaa: yleensä näkyvä alue halutaan ilmaista näkökenttänä eli näkymäkulmana (engl. field of view, angle of view). Ihmisen näkökenttä on leveyssuunnassa lähes 180°, mutta tyypillisen kameran noin 45°. Lähelle zoomatussa kuvassa näkökenttä voi olla hyvinkin kapea. Tietokonepeleissä ja 3d-mallinnusohjelmissa käytetään melko laajaa näkökenttää, kuitenkin niin että kuva ei vääristy liikaa.

GLU-apukirjasto tarjoaa valmiin keinon näkökentän määrittelemiseen:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(näkökenttä, kuvasuhde, etu, taka);
glMatrixMode(GL_MODELVIEW);

gluPerspective helpottaa glFrustum-komennon käyttöä. Parametri näkökenttä on näkymäpyramidin pystysuuntainen kulma, ja kuvasuhde leveyssuuntaisen näkymäkulman suhde korkeussuuntaiseen. Kuten glFrustum-komennossa, etu ja taka ovat pyramidin etu- ja takaseinien etäisyydet projektiokeskuksesta.

OrtogonaaliprojektioMuokkaa

Ortogonaaliprojektio on yhdensuuntaisprojektio eli se muodostaa kolmiulotteisesta datasta kaksiulotteisen kuvan ilman syvyysvaikutelmaa. Projektio sopii pohjapiirustuksiin. Sitä käytetään etenkin erilaisissa 3d-mallinnusohjelmissa, tärkeimpänä ehkä CAD-ohjelmistot. OpenGL:ssä se on myös keino saada aikaan 2d-grafiikkaa, sillä kaikki nurkkapisteet käsitellään aina kolmiulotteisina.

Ortogonaaliprojektiossa näkökenttä ei ole pyramidi, vaan suorakulmainen särmiö (laatikko). Projektiokeskusta ei ole, tai sen voi kuvitella olevan äärettömän kaukana. Ortogonaaliprojektio kuvaa pisteitä suoraan xy-tasolle. Särmiön etu- ja takatasoja käytetään vain leikkelyyn: liian kaukana tai lähellä olevat esineet jätetään näyttämättä.

Kuvaportin kuvasuhdeMuokkaa

Jos kuvaportin kuvasuhde ei ole sama kuin näkymäkulmien kuvasuhde, kuva vääristyy. Niinpä kuvaportin kokoa muutettaessa

HuomautuksiaMuokkaa

  • Moni vanha tietokonepeli käytti perspektiiviprojektiota, jossa riitti jakaa x ja y komponentilla z. Tämä sopi etenkin peleihin, joissa kuvakulma pysyi paikallaan ja tausta oli bittikarttakuva.
  • Vapaassa Blender-mallinnusohjelmassa käytetään runsaasti vaihtoa yhdensuuntais- ja keskusprojektion välillä. Numeronäppäimistön painike 5 vaihtaa projektioiden välillä.