|
v. 1.0.h
Esipuhe
OpenGL on grafiikkarajapinta, jonka avulla voidaan piirtää nopeaa grafiikkaa hyödyntäen näytönohjaimesi laitteistopohjaista kiihdytystä. Useat pelit nykypäivänä tehdään OpenGL:llä, vaikka yleisempi rajapinta peleissä taitaa olla Microsoft DirectX. Olen usein nähnyt ja kuullut väittelyjä siitä kumpi rajapinnoista on parempi DirectX vai OpenGL. OpenGL on ainakin joidenkin mielestä suorituskykyisempi, joskin mitään virallista testiä en itse ole nähnyt. Ainakaan mistään hitaasta rajapinnasta ei ole kyse. Onhan esimerkiksi Quake 3 tehty OpenGL:llä. Ainakin OpenGL:n eduksi voidaan laskea se, että se toimii muuallakin kuin Windowsissa. Lisäksi OpenGL tulee näytönohjaimesi ajureiden mukana, joten päivityksetkin hoituvat kuin itsestään. Toisaalta joidenkin näytönohjaimien tuki OpenGL:lle on hiukan lapsenkengissä, mutta pääosin OpenGL tuki löytyy. On toki myös kortteja joissa ei ole DirectX tukea, mutta ne ovat lähinnä ammattilaiskäyttöön tarkoitettuja erikoiskortteja, kuten NVIDIA Quadro ja ATI FireGL kortit. En muista tukevatko nuo mainitut kortit DirectX:ää, mutta syy miksi 3d-mallinnukseen tarkoitetut kortit eivät automaattisti tue DirectX:ää on se, ettei 3d-ohjelmat tue DirectX:ää. Ainostaan 3D Studio Max minun tuntemistani 3d-ohjelmista tukee DirectX:ää. Yksi suuri syy tähän varmasti on, ettei DirectX tue NURBS pintoja, mutta OpenGL sen sijaan tukee myös niitä. Me emme kuitenkaan tule tässä tutoriaalissa käsittelemään NURBSeja lainkaan. Älkää silti luulko että muka osaisin sellaisia OpenGL:llä tehdä J. Niiden kanssa ongelmia jo 3d-ohjelmallakin.
Vaikka mieleni tekisikin kirjoitella tähän puuta heinää pari sivullista niin säästän itseäni, koska eihän näitä ylitärkeitä esipuheita kukaan oikeasti lue :).
1.0 OpenGL toimintakuntoon!
Käytän tässä tutoriaalissa Borland CBuilder:ia, mutta samat ohjeet toimivat ihan hyvin Visual C++:ssa. Tosin Visualissa on hiukan erilainen tapa kirjoittaa koodia, mutta OpenGL koodi toimii ihan samalla tavalla huolimaatta siitä millä ohjelmalla sitä kirjoittaa.
Eli käynnistetään Borland CBuilder! ( Tästä ei sentään kuvaa :) )
Tee aivan normaali Application projekti.
Kuva 1.0
Siirry koodi-ikkunaan.
Kuva 1.1
Kuten yleensäkin grafiikkaohjelmoinnissa ei ikkunaosaan juuri tarvitse koskea, koska emmehän me käytä tässä mitään käyttöliittymä komponentteja. Anna halutessasi ikkunalle nimi ja otsikko, mutta OpenGL:n kannalta sillä ei toki ole merkitystä.
Jokainen OpenGL:ää käyttävä ohjelma tarvitsee ainakin gl.h otsikkotiedoston. Eli sisällytä gl.h tiedosto ohjelmaasi.
include <gl/gl.h>
Lisäksi otamme käyttöön glu.h ja glaux.h otsikkotiedostot. Tiedostot ovat apukirjastoja, jotka sisältävät monia ohjelmointia helpottavia funktioita.
include <gl/glu.h>
include <gl/glaux.h>
OpenGL ohjelma tarvitsee myös seuraavat osoittimet.
HDC hdc;
HGLRC hglrc;
HDC on osoitin näytönohjaimeesi ja HGLRC on osoitin renderointilaitteeseen. Edellä mainitut osoittimet on hyvä laittaa globaaleiksi.
Nyt voimme aloittaa varsinaisen koodaamisen. Ja ensimmäisenä kiinnitetään huomiomme tilanteeseen jolloin ohjelma käynnistyy eli sovellusikkunamme onCreate-tapahtuma.
Kuva 1.2
//---------------------------------------------------------------------------
// Ikkunan luonti (tapahtuma)
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// OpenGL:n asennus tähän!!!
}
Aivan ensimmäisenä onCreate-tapahtumassa haluamme saada laitekontekstin (eng. device context). Saamme sen seuraavalla koodirivillä.
hdc = GetDC(Form1->Handle);
Seuraavaksi asetetaan pikseliformaatti. Pikseliformaatilla määrätään ohjelman grafiikkaominaisuuksia, kuten esimerkiksi tuki OpenGL:lle, kaksoispuskurointi, Z-puskuri, ym. Seuraavassa on käyttämäni pikseliformaatti. Pikseliformaatti määritellään PIXELFORMATDESCRIPTOR nimiseen rakenteeseen.
int nPixelFormat;
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // Rakenteen koko
1, // Versio numero
PFD_DRAW_TO_WINDOW | // Tuki ikkunalle
PFD_SUPPORT_OPENGL | // Tuki OpenGL:lle
PFD_DOUBLEBUFFER, // Kaksoispuskurointi
PFD_TYPE_RGBA, // RGBA formaatti
32, // 32 bittinen värisyvyys
0, 0, 0, 0, 0, 0, // väribitit
0, // Ei aplha puskuria
0, // Vaihtobitti
0, // Summauspuskuri
0, 0, 0, 0, // Summausbitit
16, // Z-puskurin bittimäärä
0, // Stencil-puskuri
0, // Apupuskuri
PFD_MAIN_PLANE, // Ensisijainen piirtotaso
0, // Varattu
0, 0, 0 // Tasomaskit
};
Älä välitä vaikka et ymmärtäisikään joka kohtaa sillä tätä joutuu muuttamaan vain harvoin. Ja kun sellainen tilanne tulee eteen tiedät jo varmasti mitä tehdä.
Nyt etsimme Windowsilta määrittelymme mukaisen pikseliformaatin.
nPixelFormat = ChoosePixelFormat(hdc, &pfd);
En tee tässä mitään tarkistuksia, jotta koodi olisi mahdollisimman selkeän näköistä. Tarkistukset olisi kuitenkin syytä tehdä. Meidän pitäisi tarkistaa löysikö Windows määrittelymme mukaista pikseliformaattia ennen, kuin yritämme asettaa sellaista. No kuten sanottu emme tee tarkistuksia, joten luotamme siihen, että Windows on löytänyt tarvittavan formaatin. (Luvun 1 esimerkkiohjelman lähdekoodissa tarvittavat tarkistukset on tehty.)
Alla oleva koodirivi asettaa pikseliformaatin, jos Windows on sellaisen löytänyt.
SetPixelFormat(hdc, nPixelFormat, &pfd);
Kun pikseliformaatti on saatu voidaan luoda yhteys renderointilaitteeseen. Tämä onnistuu seuraavalla koodirivillä.
hglrc = wglCreateContext(hdc);
Tämän jälkeen aktivoidaan renderointilaite.
wglMakeCurrent(hdc, hglrc);
Nyt OpenGL on toiminnassa!
Älkää kuitenkaan uhotako, että ohjelmoimme C++:lla. Eli kaikki sonta on myös tuhottava, jottei niitä jäisi muistiin roikkumaan. Homma onnistuu seuraavasti.
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
ReleaseDC(Form1->Handle,hdc);
Puhdistus koodi kannattaa laittaa sovellusikkunana onClose-tapahtumaan.
Kuva 1.3
2.0 Perspektiivi ja kolmio.
Pelkän harmaan ikkunan katselu ei oikein jaksa kiinnostaa mitenkään hirveän pitkään, joten laitetaan ikkunaan hiukan kolmiota. Ennen kun voimme piirtää kolmion pitää meidän asettaa perspektiivi ohjelmallemme. Lisäksi meidän pitää hiukan puhua matriiseista, mutta älkää pelästykö. Mihinkään matemaattisiin toimii matriisien kanssa emme joudu.
2.1 Perspektiivi
Aloitetaan asettamalla näkymäalue johon OpenGL piirtää. Tämä näkymän määrittelyt laitetaan sovellusikkunan onResize-tapahtumaan.
Kuva 2.0
Tapahtuman onResize koodilohko näyttää tältä, kun siihen ei ole kirjoitettu koodia.
//---------------------------------------------------------------------------
// Ikkunan koon muutos (tapahtuma).
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
}
Eli aletaan kirjoittaa koodia onResize-tapahtumaan. Aivan ensimmäiseksi tarkistetaan ettei ikkunan korkeus ole nolla, jottei se aiheuttaisi virhettä projektiossa.
int gl_leveys = Form1->ClientWidth, gl_korkeus = Form1->ClientHeight;
// Tarkistetaan ettei ikkunan korkeus ole 0.
//
if (gl_korkeus == 0) // Korkeus on 0.
gl_korkeus = 1;
Tämän jälkeen voimme asettaa näyttöalueen.
glViewport(0, 0, gl_leveys, gl_korkeus);
Nyt kun näyttöalue on asetettu voimme määritellä projektiomatriisin. Eli projektiomatriisi on vastuussa siitä miten kappaleet piirretään 3d-avaruuteen. Tosin sanoen se määrittelee ohjelmamme perspektiivin. Eli ensin valitaan projektiomatriisi käyttöön.
glMatrixMode(GL_PROJECTION);
Sitten alustetaan matriisi.
glLoadIdentity();
Ja nyt voimme määritellä perspektiivin, joka on 45 asteen kulma, lähietäisyys 0.1 ja kaukoetäisyys 100. Rivissä oleva jakolasku määrittelee perspektiivin suhteen ikkunan kokoon nähden.
gluPerspective(45.0f,(GLfloat)gl_leveys/(GLfloat)gl_korkeus,0.1f,100.0f);
Perspektiivin asettamisen jälkeen asetamme MODELVIEW matriisin. Eli matriisi joka on vastuussa mallien asennosta ja paikasta. Eli valitaan MODELVIEW matriisi.
glMatrixMode(GL_MODELVIEW);
Sitten vain alustetaan mallimatriisi ja tämä vaihe on tehty.
glLoadIdentity();
2.2 Shade ja perspektiivikorjaus
Asetetaan vielä pari asetusta ennen, kuin piirrämme kolmion. Eli OpenGL:n käyttöönotto vaiheen lopuksi voisi määritellä millaista varjostusta ja perspektiivikorjausta ohjelma käyttää.
Ensin tulee taustaväri.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Sitten asetetaan ohjelmalle Smooth-varjostus, joka tekee kappaleista sileämmän näköisiä mitä ovatkaan.
glShadeModel(GL_SMOOTH);
Ja sitten vielä perspektiivikorjaus, joka estää grafiikan vääristymistä z suunnassa.
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
2.3 Ja sitten se polygoni ruutuun
Laitetaan polygonin piirtokoodi ikkunan onPaint-tapahtumaan.
Kuva 2.1
Tapahtuman koodi on seuraavanlainen ennen, kuin siihen on lisätty koodia.
//---------------------------------------------------------------------------
// Ikkunan piirto (tapahtuma).
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
}
Ensimmäiseksi haluamme tyhjentää ruudun täyttämällä se taustavärillä.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Tämän jälkeen varmistetaan, että mallimatriisi on alustettu jotta alamme liikuttaa polygonia 0 pisteestä.
glLoadIdentity();
Sitten liikutetaan kolmiota hieman poispäin ruudusta (6 yksikköä).
glTranslatef(0.0f, 0.0f, -6.0f);
Nyt kun kolmiomme tai pikemminkin mallimatriisimme on transformoitu alamme piirtää kolmiota ko. kohtaan. Kolmio koostuu kolmesta kärkipisteestä eli vertekseistä. Nämä kärkipisteet annetaan glBegin() ja glEnd() rivien välissä.
glBegin(GL_TRIANGLES);
// Verteksien arvot.
glEnd();
Verteksien paikat annetaan funktiolla glVertex3f() ja verteksin väri glColor3f(). Huomaa että verteksin väri on annettava ennen, kuin sen paikka. Alla oleva koodi tulee laittaa glBegin(GL_TRIANGLES) ja glEnd() rivien väliin.
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
Lopuksi piirretään taustapuskurin sisältämä tieto ruudulle.
SwapBuffers(hdc);
Ja tässä lopputulos!
Kuva 2.2
|