1. fejezet Bevezetés az OpenGL világába Az OpenGL kitûnõ térgrafikai API. De mi tesz egy alkalmazásprogramozási felületet kitûnõ térgrafikai API-vá? Ha ezt a kérdést szoftverfejlesztõknek tesszük fel, a legtöbben valószínûleg az alábbiakat fogják megemlíteni: • Az API-nak lehetõvé kell tennie a valósidejû leképezést, vagyis hatékonynak kell lennie. • Az API-nak széles körû támogatással kell rendelkeznie. • Az API-nak egyszerûen használhatónak kell lennie. Az, hogy valami „hatékony”, persze gyakran attól függ, milyen szempontból nézzük. Sok alkalmazás olyan képkocka-lejátszási sebességet igényel, ami lehetõvé teszi a valósidejû interaktivitást, míg az olyan alkalmazások, mint a kapcsolat nélküli videószerkesz-
2 OpenGL röviden
tõk, illetve a tudományos adatok terabájtjaival dolgozó vizualizációs programok lényegesen kevesebbel is beérik. Amennyiben a szûk keresztmetszet nem az adott alkalmazás, és az API-t is jól tervezték meg, az API hatékonysága mindig a megvalósítástól függ. Egy jó API-nak tehát elõ kell segítenie, és soha nem szabad hátráltatnia a hatékony megvalósítást. Általánosságban azt mondhatjuk, hogy egy API, amelynek a felépítése elõsegíti a hatékonyságot, lehetõvé teszi, hogy grafikushardver-architektúrák és számítógépplatformok széles körén megvalósítható legyen. Ezen felül egy jól tervezett API évekig népszerû marad, és a grafikushardver-architektúrák több generációján is fut (Lichtenbelt 1997). A legtöbb szoftverfejlesztõ számára azonban az API kérdése lényegében a következõ kérdéseket jelenti: „Elérhetõ az API az én fejlesztõplatformomhoz?”, és ami még fontosabb: „Hozzáférhetõ a célközönség rendszerén?”. Az OpenGL hardverarchitektúrák széles körén fut, és különbözõ megvalósításai jól dokumentáltak (Cojot 1996, Carson 1997, Kilgard 1997, McCormack 1998). A használat egyszerûségét viszont nem lehet teljesen objektív módon meghatározni vagy mérni. Szempont lehet, hogy az API gazdag szolgáltatáskészlettel rendelkezik-e, rugalmasan használható-e, következetes-e, vagy hogy miután megtanultuk, hogyan lehet vele megoldani egy adott leképezési problémát, építhetünk-e erre más hasonló feladatok megoldásánál. Ha ezeknek a kérdéseknek a többségére igennel felelünk, az annak a jele, hogy az API egyszerûen használható. Ha a hatékonyságot és a hozzáférhetõséget adottnak vesszük, újonc programozóként a használat egyszerûsége az, ami elsõdleges fontosságú a számunkra. Ebben a bevezetõ fejezetben az OpenGL általános felépítését vázoljuk fel, és megmutatjuk, milyen könnyû összeállítani egy egyszerû tesztprogramot.
Amit a fejezetbõl megtanulhatunk Az 1. fejezetben az alábbiakkal foglalkozunk: • Az OpenGL magas szintû leírása – ebben a részben meghatározzuk, mi is az OpenGL, illetve bemutatjuk architektúráját, nyelvtanát, szolgáltatásait, illetve állapotlekérdezési mintáit. • Segédkönyvtárak – itt két olyan általánosan használt segédkönyvtárat (GLUT és GLU) mutatunk be, amellyel a legtöbb OpenGL-programozó biztosan találkozik munkája során. • A fejlesztõkörnyezet – ebben a részben az OpenGL-alkalmazások fejlesztéséhez szükséges fejállományokat és függvénykönyvtárakat mutatjuk be. • Egy példaprogram – elkészítünk egy kis méretû példaprogramot, hogy megmutassuk, milyen és hogyan fest egy OpenGL-program. • Az OpenGL története – ebben a részben arról tudhatunk meg többet, hogy honnan származik az OpenGL.
1. fejezet • Bevezetés az OpenGL világába 3
Amit a fejezetbõl nem tanulhatunk meg A fejezet olvasása közben tartsuk észben a következõt: • Amint a fejezetben felsoroljuk az OpenGL szolgáltatásait, olyan szolgáltatásokat és témákat is megemlítünk, amelyekkel az OpenGL® röviden nem foglalkozik.
1.1. Mi az OpenGL? Az OpenGL a The OpenGL Graphics System: A Specification címû szabványleírásban meghatározott grafikus API. Az OpenGL-programok ennek a szabványnak a megvalósításait használják síkbeli (2D) és térbeli (3D) geometriai adatok és képek megjelenítésére. Az OpenGL-szabvány szerint „a programozó számára az OpenGL egy parancskészlet, amely mértani testek leírását teszi lehetõvé két vagy három dimenzióban, más parancsai pedig azt szabályozzák, hogy ezek az objektumok hogyan képezhetõk le a képkockatárba (framebuffer).” 1 Az OpenGL-t jellemzõen belépési pontok könyvtáraként valósítják meg (a GL az OpenGL névben a Graphics Library, vagyis grafikus könyvtár rövidítése [Kilgard 1997]), illetve olyan grafikus hardverként, amely támogatja ezt a könyvtárat (lásd az 1.1. ábrát). A 3D grafikához tervezett számítógéprendszerekben a hardver közvetlenül támogatja az OpenGL-nek szinte minden szolgáltatását. Ennek eredményeképpen az OpenGL könyvtár pusztán egy vékony réteg, amely lehetõvé teszi az alkalmazásoknak, hogy hatékonyan érjék el a hardver szolgáltatásait. A hardveresen nehezen megvalósítható magasszintû szolgáltatások – például a magasszintû elemi típusok, látványgráfok és segédfüggvények támogatása – nem részei az OpenGL-szabványnak (Carson 1997), de több könyvtár is rendelkezésre áll, amelyek támogatják ezeket. Egy ilyen könyvtárat késõbb, a fejezet GLU címû részében be is mutatunk. Az OpenGL nem tartalmaz támogatást az ablakok, a bemenet (egér, billentyûzet stb.) vagy a felhasználói felületi szolgáltatások kezeléséhez, mert a számítógéprendszerek jellemzõen platformfüggõ támogatást nyújtanak ezekhez. A GLUT könyvtár (lásd a fejezet késõbbi, GLUT címû részét) platformfüggetlen módon támogatja ezeket a szolgáltatásokat, és képességei kielégítik a legtöbb kis méretû alkalmazás és bemutatóprogram igényeit. Az egyes platformokhoz kötõdõ könyvtárakkal a kötet 8. fejezetében foglalkozunk.
1 Segal, Mark és Akeley, Kurt: The OpenGL Graphics System: A Specification, 2.0-s változat, 2004. szeptember, 2. oldal.
4 OpenGL röviden
Ügyfél
Alkalmazás Alkalmazás
OpenGL
OpenGL-könyvtár
Kiszolgáló
OpenGL-könyvtár
OpenGL-könyvtár
Grafikus hardver
Grafikus hardver
(a)
(b)
1.1. ábra Két jellemzõ OpenGL-megvalósítás. Az (a) ábrán az alkalmazások OpenGL belépési pontok egy olyan könyvtárához kapcsolódnak, amely egy OpenGL alapú grafikus hardvernek ad át parancsokat. A (b) ábra egy ügyfél–kiszolgáló rendszerû megvalósítást mutat. Itt az alkalmazások OpenGL belépési pontok egy olyan könyvtárához kapcsolódnak, amely egy hálózati protokollon keresztül adja át a parancsokat. A (helyi vagy távoli) kiszolgáló megkapja az utasításokat, és saját hardvere segítségével végzi el a leképezést.
1.1.1. Alapok és architektúra Az OpenGL állapotgép. Az alkalmazások az OpenGL függvényeit meghívva OpenGLállapotokat állítanak be, amelyek feladata, hogy meghatározzák egy elemi alakzat (primitív) végsõ megjelenését a képkockatárban. Alább azt láthatjuk, hogy egy alkalmazás vörösre állítja a színt, kirajzol egy pont primitívet, a színt kékre állítja, majd kirajzol egy második és egy harmadik pontot is: glColor3f( 1.f, 0.f, 0.f ); // a vörös RGB színhármasként glBegin( GL_POINTS ); glVertex3f( -.5f, 0.f, 0.f ); // az elsõ pont XYZ koordinátái glEnd(); glColor3f( 0.f, 0.f, 1.f ); // a kék RGB színhármasként glBegin( GL_POINTS );
1. fejezet • Bevezetés az OpenGL világába 5
glVertex3f( 0.f, 0.f, 0.f ); // a második pont XYZ koordinátái glEnd(); glBegin( GL_POINTS ); glVertex3f( .5f, 0.f, 0.f ); // a harmadik pont XYZ koordinátái glEnd();
Ebben az esetben az OpenGL az elsõ pontot vörös színnel jeleníti meg, a másodikat pedig kékkel. Mivel a kód nem változtatja meg a színállapotot a harmadik pont kirajzolása elõtt, az OpenGL az utolsó pontot is kékkel jeleníti meg. (A fenti kódban a glColor3f() egy RGB-színértéket határoz meg, a glVertex3f() egy csúcs xyz koordinátáit, az egyes primitíveket pedig a glBegin() és glEnd() függvények írják le, de ezeknél hatékonyabbak is léteznek, amelyeket a 2. fejezetben mutatunk be.) Az elemi alakzatok vagy primitívek egy vagy több csúcsból (vertex) állnak. A fenti példában minden pontot egyetlen csúcs jelölt; a vonalak és kitöltések két vagy több csúcsot igényelnek. A csúcsok saját színnel, mintázatkoordinátákkal és normál állapottal (illetve más csúcsállapotokkal) rendelkeznek. A vörös és kék pontokat kirajzoló fenti kódot így is átírhatnánk: glBegin( GL_POINTS ); glColor3f( 1.f, 0.f, 0.f ); // a vörös RGB színhármasként glVertex3f( -.5f, 0.f, 0.f ); // az elsõ pont XYZ koordinátái glColor3f( 0.f, 0.f, 1.f ); // a kék RGB színhármasként glVertex3f( .5f, 0.f, 0.f ); // a második pont XYZ koordinátái glEnd();
Az OpenGL mindig abban a sorrendben hajtja végre a parancsokat, ahogy azokat az alkalmazás elküldi neki. Ügyfél–kiszolgáló alapú megvalósításnál (lásd az 1.1. ábrát) a parancsok az ügyféloldalon átmeneti tárba (buffer) helyezhetõk, így nem muszáj, hogy azonnal végrehajtódjanak, de az alkalmazások a glFlush() meghívásával vagy tárcserével kényszeríthetik az OpenGL-t, hogy végrehajtsa a tárolt parancsokat. A korszerû processzorokhoz hasonlóan az OpenGL is csõvezetékrendszert (pipeline) használ a leképezéshez. Az OpenGL a képpont- és csúcsadatokat négy szakaszban – csúcsmûveletek, képpontmûveletek, elemekre bontás (raszterizáció) és töredékmûveletek – dolgozza fel, az eredményt pedig a képkockatárban vagy a mintázatmemóriában tárolja (lásd az 1.2. ábrát). Az alkalmazások kétféle adatot adhatnak át az OpenGL-nek leképezésre: csúcsadatokat és képpontadatokat. A csúcsadatokból képezõdnek le a síkbeli és térbeli mértani primitívek, például a pontok, a vonalak és a kitöltött primitívek (errõl további információt a 2. fejezetben találunk). A képpontadatokat az alkalmazások képpontok (pixelek) tömbjeiként határozzák meg, és arra utasíthatják az OpenGL-t, hogy a képpontokat közvetlenül a képkockatárban jelenítse meg, vagy másolja azokat a mintázatmemóriába, hogy késõbb mintázat-
6 OpenGL röviden
térképekként legyenek felhasználhatók. Ezen kívül az alkalmazások ki is olvashatnak képpontadatokat a képkockatárból, valamint a tár egyes részeit a mintázatmemóriába másolhatják. Ezzel a témával részletesebben az 5. és 6. fejezetben foglalkozunk majd. Megjegyzés Az OpenGL mindig is rögzített funkciójú csõvezetéket alkalmazott; szolgáltatásainak rögzített készletét az alkalmazások az OpenGL állapotain keresztül vezérelhetik. A 2.0-s változattól kezdõdõen azonban az OpenGL megengedi, hogy az alkalmazások felülbíráljanak bizonyos csúcs- és töredékmûveleteket, úgynevezett csúcs- és töredékárnyékolókkal (shader), amelyek olyan apró programok, amelyeket egy árnyékolónyelven írtak. Az OpenGL-árnyékolók írásához az OpenGL® Shading Language az alapmû, de a témát röviden érintjük az A függelékben is. Az OpenGL® röviden csak a rögzített funkciójú csövezetékkel foglalkozik, az árnyékolókkal nem, mert kívül esnek a kötet tárgykörén.
Csúcsadatok
Csúcsmûveletek
Elemekre bontás
Képpontadatok
Képpontmûveletek
Mintázatmemória
Töredékmûveletek
Képkockatár
1.2. ábra Az OpenGL csõvezetékrendszere
1.1.1.1. Csúcsmûveletek Az OpenGL az alkalmazásunktól kapott csúcsok mindegyikén a következõ mûveleteket hajtja végre: • Transzformáció – az OpenGL a csúcsokat objektumkoordináta-térbõl ablakkoordináta-térbe transzformálja. Ez persze jelentõs leegyszerûsítése a transzformáció folyamatának, amellyel részletesen a 3. fejezetben foglalkozunk. • Megvilágítás – amennyiben az alkalmazás bekapcsolta a megvilágítást, az OpenGL kiszámítja az egyes csúcsok megvilágítási értékeit. A megvilágítás a 4. fejezet témája lesz majd. • Levágás – amikor az OpenGL arra a megállapításra jut, hogy az adott primitív egyáltalán nem lesz látható, mert kívül esik a nézet terén, minden csúcsot elvet. Ha egy
1. fejezet • Bevezetés az OpenGL világába 7
primitív csak részben látható, az OpenGL csonkolja, hogy csak a látható részt kelljen elemekre bontania. A csúcsmûveletek után az OpenGL elemeire bontja a primitívet, és a kapott adatokon töredékmûveleteket hajt végre. 1.1.1.2. Képpontmûveletek Az OpenGL az alkalmazásoktól kapott vagy azoknak továbbított minden képpontadattömbön képpontmûveleteket végez. Ezek a mûveletek gondoskodnak a bájtcserérõl, a feltöltésrõl és az eltolásról, amelyek ahhoz szükségesek, hogy a képpontokat különféle formátumokban lehessen fogadni és elküldeni. Egyes képpontmûveletek, például a képpontátvitel (pixel transfer; megfeleltetés, méretezés, kiegyenlítés), illetve a képkezelõ mûveletek részhalmaza kívül esik könyvünk keretein. Ha többet szeretnénk tudni róluk, lapozzuk fel az OpenGL® Programming Guide 8. fejezetét (Drawing Pixels, Bitmaps, Fonts, and Images). 1.1.1.3. Elemekre bontás Az elemekre bontás (raszterizáció) során a geometriai adatokból töredékek (fragment) jönnek létre. A töredékek olyan helyzeti, szín-, mélység- és mintázatkoordináták, illetve más adatok, amelyeket az OpenGL feldolgoz, mielõtt végül a képkockatárba írna. A töredékek tehát különböznek a képpontoktól, amelyek azok a fizikai helyek a képkockatármemóriában, ahol a töredékek tárolódnak. Az OpenGL egy töredéket jellemzõen egyetlen képponthelyhez társít, de azok a megvalósításai, amelyek támogatják a többszörös mintavételezést (multisampling), a töredékeket képpont alatti szinten tárolják. Az elemekre bontás szabályaival a The OpenGL Graphics System 3. fejezete foglalkozik. A legtöbb programozónak nem kell ismernie ezeket a szabályokat, hacsak nem olyan alkalmazásokat fejleszt, amelyek pontos pixelesítést követelnek meg. Az OpenGL az elemekre bontást a képpont alatti szinten határozza meg: a pont primitíveket matematikai pontokként, a vonal primitíveket matematikai vonalakként, a kitöltött primitíveket pedig olyan területekként kezeli, amelyeket matematikai vonalak határolnak. A többszörös mintavételezéstõl eltekintve a programozónak az ablakkoordináta-teret úgy kell elképzelnie, mint egy Descartes-féle hálót vagy rácsot, amelyben minden rácscella egyetlen képpontnak felel meg a képernyõn. Ezt észben tartva tekintsük át, hogyan hajtja végre az OpenGL az elemekre bontást: • Pont primitívek elemekre bontásakor az OpenGL akkor hoz létre töredéket, ha a pont a képponthatáron belülre esik. • A vonalakat az OpenGL úgy bontja elemeikre, hogy végpontjaik közé esõ töredékeket állít elõ. Általános szabályként azt mondhatjuk, hogy az OpenGL nem hoz létre a vonalszegmensek második, más néven kioldó csúcsának (triggering vertex) meg-
8 OpenGL röviden
felelõ töredéket – ez gondoskodik róla, hogy az azonos dõlésszögû összekötött vonalrészek ne rajzolják ki kétszer ugyanazt a képpontot. • Az OpenGL a kitöltött alakzatok esetében akkor hoz létre töredéket, ha a képpont közepe az alakzat matematikai határain belülre esik. A matematikai határra esõ képpontközepekre külön szabályok vonatkoznak, amelyek biztosítják, hogy két, egymást át nem fedõ, de közös éllel rendelkezõ kitöltött primitív ne rajzolja ki kétszer ugyanazt a képpontot. • Amikor az OpenGL az alapértelmezett nagyítási szinten bontja elemeire képpontadatok egy tömbjét, a képpontblokk minden képpontjához létrehoz egy töredéket. Ez persze csak leegyszerûsítése a raszterizáció folyamatának, de a kötetben nem foglalkozunk olyan, az elemekre bontás fázisát érintõ témákkal, mint a simítási pontok, vonalak és sokszögek, a széles pontok és vonalak, vagy a képpontnagyítás. Ezekkel részletesen az OpenGL® Programming Guide-ban ismerkedhetünk meg. 1.1.1.4. Töredékmûveletek Az OpenGL minden töredéket alaposan feldolgoz, hogy meghatározza annak a képkockatárba írandó végsõ értékeit, illetve hogy egyáltalán a tárba kell-e írni az adott töredéket. Az OpenGL az elemekre bontás eredményeképpen kapott minden töredék esetében a következõ mûveleteket végzi el: • Képponttulajdonlási teszt – Ez a teszt lehetõvé teszi az OpenGL-nek, hogy akkor is helyesen jelenítse meg az adatokat, ha több egymást átfedõ ablak van jelen. Azok a töredékek, amelyek a képkockatárban olyan helyeknek felelnek meg, amelyeket eltakar egy ablak, elvethetõk vagy háttértárba menthetõk. • Ollóteszt – Amennyiben a képkockatár egy adott helye kívül esik az alkalmazás által meghatározott ablakkoordináták téglalapján, az OpenGL elveti a töredéket. • Többszörös mintavételezési töredékmûveletek – Az OpenGL a töredékek áttetszõségi (alfa) és fedési értékeit a többszörös mintavételezés szabályainak megfelelõen módosítja. • Áttetszõségi teszt – Ha egy töredék áttetszõségi vagy alfa értéke nem felel meg az alkalmazás támasztotta követelményeknek, az OpenGL elveti. Ez a részben vagy teljesen átlátszó töredékek elvetéséhez hasznos. • Stencilteszt – Az OpenGL összehasonlítja a képkockatár adott helyén tárolt stencilértéket, és az alkalmazás aktuális állapota alapján meghatározza, mit tegyen a töredékkel, és hogyan módosítsa a tárolt stencilértéket. A stencilteszt több célra is alkalmazható, például nem négyszögletes levágásra, árnyékokhoz, valamint építõ vagy konstruktív szilárdtest-geometriához (CSG, constructive solid geometry). • Mélységteszt – A mélységteszt akkor vet el egy töredéket, ha a töredék mélységértékének és a mélységtárban (depth buffer) tárolt értéknek az összehasonlításakor nem kap egyezést. Az OpenGL mélységtesztje a z-tárazás (z-buffering) egyik formája. • Takarási lekérdezés – Amikor egy takarási lekérdezés (occlusion query) aktív, az OpenGL minden olyan töredék esetében növeli egy számláló értékét, amely át-
1. fejezet • Bevezetés az OpenGL világába 9
megy a mélységteszten. A takarási lekérdezés gyors láthatóságpróbát tesz lehetõvé az összetett jelenetekben. • Keverés – A keverés (blending) a töredék RGB szín- és alfaértékeit kombinálja a képkockatár adott helyén tárolt RGB- és alfaértékekkel. Az alkalmazások a keverést jellemzõen áttetszõ objektumok utánzására használják. • Kiegyenlítés – Amikor a képkockatár színpontossága kisebb a töredék RBG- és alfaértékeinek pontosságánál, az OpenGL egy megismételhetõ algoritmussal kiegyenlíti a töredék színét és áttetszõségét. A kiegyenlítést (dithering) a 24 bites képkockatárak széles körû elérhetõsége miatt ritkán használják. • Logikai mûveletek – Az OpenGL a végsõ színértéket az alkalmazás által meghatározott logikai mûveleteknek megfelelõen írja a képkockatárba. Mivel könyvünket tömör útmutatónak szántuk, a kötetben nem térünk ki minden töredékmûveletre. Az olló- és stencilteszttel, a többszörös mintavételezéssel, a takarási lekérdezéssel, a kiegyenlítéssel és a logikai mûveletekkel kapcsolatban az OpenGL® Programming Guide ad részletes felvilágosítást.
1.1.2. Nyelvtan Az OpenGL-t több programozási nyelv is támogatja, és mindegyik saját kötést alkalmaz. A kötetben és a C++ példakódokban C típusú kötést használunk. A következõkben a C típusú kötés nyelvtani szabályait ismertetjük. 1.1.2.1. Típusok A C-kötésnél minden OpenGL-típusnevet a GL elõtaggal látunk el. A logikai Boolean típus például GLboolean lesz, a kétszeres pontosságú lebegõpontos érték típusa pedig GLdouble. A GLbyte, GLshort és GLint adattípusoknak elõjel nélküli változataik is vannak (GLubyte, GLushort és GLuint). Az OpenGL legfontosabb adattípusait az 1.1. táblázatban foglaltuk össze. 1.1. táblázat Az OpenGL adattípusai OpenGL-típus GLboolean GLbyte GLubyte GLshort GLushort GLsizei
Bitek legkisebb száma 1 8 8 16 16 32
Parancsutótag nincs b ub s us
nincs
Leírás Logikai érték Elõjeles egész Elõjel nélküli egész Elõjeles egész Elõjel nélküli egész Nemnegatív egész méret
10 OpenGL röviden
OpenGL-típus GLsizeiptr GLint GLuint GLfloat GLclampf
GLenum GLbitfield GLdouble GLvoid*
Bitek legkisebb száma egy mutató bitjeinek száma 32 32 32 32
Parancsutótag nincs
32 32 64 egy mutató bitjeinek száma
nincs nincs
i ui f
nincs
d
nincs
Leírás Mutató egy nemnegatív egész méretre Elõjeles egész Elõjel nélküli egész Lebegõpontos érték Lebegõpontos érték a [0, 1] tartományra szorítva Felsoroló érték Becsomagolt bitek Lebegõpontos érték Mutató bármilyen adattípusra; egyenértékû a C/C++ void* típusával
1.1.2.2. Parancsok A C-kötés az OpenGL-parancsokat C-hívású függvényekként valósítja meg, amelyek gl elõtagot kapnak. Az OpenGL Enable parancsának végrehajtásához például az alkalmazásunknak a glEnable() függvényt kell meghívnia. Kötetünkben az „OpenGL-parancs” és az „OpenGL-függvényhívás” ugyanazt jelenti. Általánosságban, az OpenGL-ben nincs parancstúlterhelés.2 Az azonos szerepû, de másmás számú és típusú argumentumot váró parancsok támogatásához az OpenGL egy legfeljebb négy karakterbõl álló utótagot fûz a parancsnevekhez. Az elsõ karakter az argumentumok számát jelzi, a második karakter vagy a második-harmadik karakterpár a paraméter típusát, az utolsó karakter pedig (ha jelen van) a v, ami arra utal, hogy a függvénynek argumentumként egy címet kell átadni. Nézzünk egy példát: // RGB-színérték meghatározása három lebegõpontos értékkel GLfloat red=1.f, green=1.f, blue=1.f; glColor3f( red, green, blue ); // RGBA-színérték meghatározása négy elõjel nélküli bájttal GLubyte r=255, g=255, b=255, a=255; glColor4ub( r, g, b, a ); // RGB-érték meghatározása három short címével GLshort white[3] = { 32767, 32767, 32767 }; glColor3sv( white );
2 Az OpenGL 1.5-ös változata túlterheli az átmeneti tár objektumaival dolgozó parancsokat, ahogy ezt a 2. és 7. fejezetben be is mutatjuk.
1. fejezet • Bevezetés az OpenGL világába 11
1.1.2.3. Felsorolók A C-kötés az OpenGL felsoroló értékeit a C elõfeldolgozó segítségével határozza meg, és a neveiket GL_ elõtaggal látja el. Például ha az OpenGL-változatot jelzõ karakterláncot szeretnénk lekérdezni a VERSION felsorolással, az alkalmazásnak a glGetString( GL_VERSION ); hívást kell alkalmaznia.
1.1.3. Állapotok és lekérdezések Állapotgépként az OpenGL nem mûködhet egy hely nélkül, ahol az érvényben levõ állapotértékeket tárolhatja. Az OpenGL az állapotát egy úgynevezett GL-környezetben (GL context) tárolja. Az ezt megtestesítõ leképezési környezetek (rendering context) létrehozását, megsemmisítését és másolását platformfüggõ felületek teszik lehetõvé. A leképezési környezetek kezelésével a 8. fejezetben foglalkozunk, de a könyv során a GLUT könyvtárat használjuk majd, amely a háttérben kezeli a leképezési környezetet. További információ a fejezet késõbbi, GLUT címû részében található; most elég annyit tudnunk, hogy az OpenGL az állapotot egy leképezési környezetben tárolja. Ahogy az alkalmazásunk az OpenGL-állapotot megváltoztató parancsokat ad ki, ezeket a változásokat az aktuális leképezési környezet tartja számon. Az OpenGL több parancsot is a rendelkezésünkre bocsát az állapot beállításához. A programozók a leggyakrabban a glEnable() és glDisable() parancsokkal találkozhatnak:
void glEnable( GLenum cél ); void glDisable( GLenum cél ); Ezekkel a parancsokkal OpenGL-szolgáltatásokat helyezhetünk bekapcsolt vagy kikapcsolt állapotba. A cél adja meg, melyik szolgáltatást kívánjuk bekapcsolni vagy letiltani. A fontosabb cél felsorolásokat a könyv az egyes szolgáltatások ismertetésekor tárgyalja. Az érvényes cél felsorolások teljes listáját az OpenGL® Reference Manual „glEnable” címszavánál találhatjuk meg. OpenGL-változat: 1.0 vagy újabb
A glEnable( GL_DEPTH_TEST ) meghívásával például a mélységteszt (vagy z-tárazás) szolgáltatást kapcsolhatjuk be. Miután az alkalmazásunk kiadta ezt a parancsot, az OpenGL a következõ primitíveket mélységteszttel fogja leképezni. Ha a kódunk késõbb meghívja a glDisable( GL_DEPTH_TEST)-et, hogy kikapcsolja a szolgáltatást, az ezt követõ primitívek leképezéséhez az OpenGL már nem fog mélységtesztet alkalmazni. Alaphelyzetben az OpenGL legtöbb szolgáltatása kikapcsolt állapotban van. Valójában ekkor csak két szolgáltatás él: a kiegyenlítés (GL_DITHER) és a többszörös mintavételezés (GL_MULTISAMPLE).
12 OpenGL röviden
1.1.3.1. A glIsEnabled használata Azt, hogy egy szolgáltatás jelenleg bekapcsolt vagy kikapcsolt állapotú-e, a glIsEnabled() paranccsal kérdezhetjük le:
GLboolean glIsEnabled( GLenum érték ); Azt adja vissza, hogy egy OpenGL-szolgáltatás be van-e kapcsolva vagy sem. Az érték bármilyen OpenGL-felsorolás lehet, ami érvényes cél paramétere a glEnable(), illetve a glDisable() parancsnak. A glIsEnabled() GL_TRUE-t ad vissza, ha az érték bekapcsolt állapotú, és GL_FALSE-t, ha nem. OpenGL-változat: 1.0 vagy újabb
Egy alkalmazásban a megvilágítást például a glEnable( GL_LIGHTING ) paranccsal kapcsolhatjuk be, és a glDisable( GL_LIGHTING ) paranccsal tilthatjuk le, a megvilágítás állapotát pedig a glIsEnabled( GL_LIGHTING ) hívással kérdezhetjük le. 1.1.3.2. A glGet használata Az alkalmazásoknak különféle okokból lehet szükségük arra, hogy lekérdezzék az OpenGL-állapotot. Ezt rendszerint induláskor teszik meg, hogy olyan megvalósításfüggõ értékekhez jussanak hozzá, mint a GL_MAX_ATTRIB_STACK_DEPTH vagy a GL_MAX_LIGHTS. Az OpenGL az alkalmazásnak minden olyan érték lekérdezését megengedi, amelyet az beálllíthat. Az aktuális színt – amelyet a glColor3f() állít be – például a GL_CURRENT_COLOR érték lekérdezésével lehet megtudni. A különbözõ állapotértékek lekérdezésére az OpenGL általános módszert nyújt:
void glGetBooleanv( GLenum pnév, GLboolean* param); void glGetDoubledev( GLenum pnév, GLdouble* param); void glGetFloatv( GLenum pnév, GLfloat* param); void glGetIntegerv( GLenum pnév, GLint* param); Ezekkel az eljárásokkal az OpenGL-állapotértékeket típus szerint kérdezhetjük le. A pnév a lekérdezendõ állapotelemet jelöli, a param pedig egy GLboolean, GLdouble, GLfloat vagy GLint memóriahelyre mutat, amely az állapotértéket tárolja. OpenGL-változat: 1.0 vagy újabb
1. fejezet • Bevezetés az OpenGL világába 13
Mindig az állapotelem típusának megfelelõ függvényt kell használnunk. A GL_MAX_LIGHTS például egész érték, ezért a kódban a glGetIntegerv() függvényt kell meghívnunk: GLint maxLights; glGetIntegerv( GL_MAX_LIGHTS, &maxLights );
A könyvben több állapotelemet is ismertetünk, amelyeket az alkalmazásunk lekérdezhet, teljes listát pedig az OpenGL® Reference Manual „glGet” címszavánál, illetve a The OpenGL Graphics System 6. fejezetében (State and State Requests) találhatunk. Megjegyzés A teljesítményre (sebességre) érzékeny kódrészekben törekedjünk arra, hogy az alkalmazás lehetõleg ritkán használjon lekérdezést.3 1.1.3.3. A jellemzõverem Az állapotváltozások mentésére és tárolására az OpenGL verem-adatszerkezeteket biztosít. A jellemzõverem tetején az éppen érvényben levõ OpenGL-állapotot találjuk. Miután alkalmazásunk a verem tetejére helyezett (push) egy új állapotot, a verem legfelsõ elemének eltávolításával (pop) állíthatjuk vissza az OpenGL-t a „push” parancs elõtti állapotába. Az OpenGL-kiszolgálóállapothoz és az OpenGL-ügyfélállapothoz külön-külön verem tartozik. A két állapot közötti különbségekkel a 2. fejezet foglalkozik.
void glPushAttrib( GLbitfield maszk ); void glPopAttrib( void ); void glPushClientAttrib( GLbitfield maszk ); void glPopClientAttrib( void ); Ezek a függvények az OpenGL állapotvermeibe írnak elemeket, illetve elemeket távolítanak el onnan. A maszk egy bitenkénti OR mûvelet azokon az OpenGL által meghatározott bitértékeken, amelyek a verembe írandó állapotcsoportokat jelölik. A kiszolgálóverem és az ügyfélverem különbözõ állapotértékeket ment és állít vissza. A glPushAttrib() és a glPopAttrib() a kiszolgálóvermet érintõ mûveletek, míg a glPushClientAttrib() és a glPopClientAttrib() az ügyfélveremre vonatkoznak. OpenGL-változat: glPushAttrib()/glPopAttrib(), 1.0 vagy újabb glPushClientAttrib()/glPopClientAttrib(), 1.1. vagy újabb
3 A takarási lekérdezések viszont növelhetik az alkalmazás sebességét. (További információért lásd az A függeléket.)
14 OpenGL röviden
A maszk paraméter lehetõvé teszi az alkalmazásnak, hogy megadja, mely állapotérték-halmazokat mentsék és állítsák vissza az írási (push) és kiolvasási-eltávolítási (pop) parancsok. A glPushAttrib( GL_LIGHTING_BIT ) hívás eredményeképpen például az ennek megfelelõ glPopAttrib() csak azokat az állapotértékeket állítja vissza, amelyek az OpenGLmegvilágításra vonatkoznak, például az anyag- és fénytulajdonságok változásait. Ha minden verembe helyezhetõ kiszolgálóállapotot a verembe szeretnénk írni, a glPushAttrib() függvényt a GL_ALL_ATTRIB_BITS maszkkal kell meghívnunk. Ehhez hasonlóan, a glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS ) a verembe helyezhetõ összes ügyfélállapotot a verembe írja. Bár az OpenGL-szabvány azt mondja, hogy a glPushAttrib() és a glPushClientAttrib() úgy viselkedik, mintha az aktuális OpenGL-állapotot a verem tetejére másolná, a legtöbb mai OpenGL-termék ezt minimális adatmásolással valósítja meg. Ennek eredményeképpen az összes állapot verembe írása nem olyan költséges, mint ahogy várnánk. Bár minden állapotot verembe írni természetesen költségesebb, mint csak néhányat, a fejlesztõk gyakran úgy találják, hogy a GL_ALL_ATTRIB_BITS és a GL_CLIENT_ALL_ATTRIB_BITS elõnyei újrahasznosításkor felülmúlják azokat a hátrányokat, amelyek a sebességcsökkenésbõl következnek. Az összes lehetséges maszk értéket az OpenGL® Reference Manual „glPushAttrib” és „glClientPushAttrib” címszavánál, illetve a The OpenGL Graphics System 6. fejezetében (State and State Requests) találhatjuk meg. A jellemzõverem mélysége a megvalósítástól függ, de legalább 16. A jellemzõverem legnagyobb mélységét az alkalmazások a glGetIntegerv( GL_MAX_ATTRIB_STACK_DEPTH, …), illetve glGetIntegerv( GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, …) hívással kérdezhetik le 1.1.3.4. A glGetString használata Az OpenGL a megvalósításról karakterlánc formában ad felvilágosítást. Ezeket az értékeket a glGetString() függvénnyel kérdezhetjük le:
const GLubyte* glGetString( GLenum név ); Ez a függvény karakterlánc alapú állapotértékeket ad vissza. A név a lekérdezendõ karakterlánc-állapotelem neve. OpenGL-változat: 1.0 vagy újabb
A glGetString() a név értékétõl függõen különbözõ értékeket ad vissza: • GL_VENDOR – Az adott OpenGL-megvalósítás készítõjét adja vissza. • GL_VERSION – Az adott OpenGL-változatot adja vissza.
1. fejezet • Bevezetés az OpenGL világába 15
• GL_EXTENSIONS – Az adott OpenGL-megvalósításban elérhetõ bõvítményeket adja vissza. • GL_RENDERER – A gyártó leképezõjérõl ad információt. (A név értéke lehet GL_SHADING_LANGUAGE_VERSION is; további információért lásd az OpenGL® Shading Language leírást.) Az alkalmazások induláskor jellemzõen a GL_VERSION és GL_EXTENSIONS értékeket kérdezik le, hogy meghatározzák a támogatott szolgáltatások halmazát. A GL_EXTENSIONS értékkel a 7. fejezetben részletesebben foglalkozunk majd. Megjegyzés Ahogy azt a fejezet korábbi, Állapotok és lekérdezések címû részében leírtuk, az OpenGL az állapotot egy leképezési környezetben tárolja. A kezdõkkel gyakran elõfordul, hogy az alkalmazás indításakor véletlenül meghívják a glGetString() függvényt, hogy lekérdezzék az OpenGL-változatot és a támogatott bõvítményeket, miközben még nem áll rendelkezésükre leképezési környezet. Ilyen esetben a viselkedés nem meghatározható, és a glGetString() általában NULL-t ad vissza. Ezt úgy akadályozhatjuk meg, hogy a glGetString()-et csak a leképezési környezetet létrehozó és a GLUTban érvényre juttató glutCreateWindow() hívás után hívjuk meg. A glGetString( GL_VERSION ) által visszaadott változat-karakterlánc fõszám.alszám vagy fõszám.alszám.kiadás formájú, és egy szóköz is követheti, majd további, a gyártóra vonatkozó információk. Az alkalmazások a változat-karakterláncot az alábbi kóddal dolgozhatják fel, hogy kinyerjék belõle a fõ (major) változatszámot, illetve az alváltozat számát (minor): std::string ver((const char*) glGetString(GL_VERSION)); assert( !ver.empty() ); std::istringstream verStream( ver ); int major, minor; char dummySep; verStream >> major >> dummySep >> minor;
A változat-karakterlánc feldolgozását követõen a fõ- és alváltozatszámból az alkalmazások képesek meghatározni a használandó OpenGL-szolgáltatáskészletet. Ha a fõváltozat (major_version) 1, az alváltozat (minor_version) pedig 4, ez az OpenGL 1.4-et jelöli – ilyen esetben például nem használhatjuk az alkalmazásban az OpenGL 1.5 alapfelületét az olyan szolgáltatásokhoz, mint a tárobjektumok vagy a takarási lekérdezések. A könyv írásakor az OpenGL lehetséges verziószámai a következõk voltak: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 és 2.0.
16 OpenGL röviden
1.1.3.5. Azonosítók lekérdezése Az OpenGL azonosítókat hoz létre az átmeneti tárakhoz, megjelenítési listákhoz és anyagmintázatokhoz. Az alkalmazások a glIsBuffer(), glIsList() és glIsTexture() függvényekkel kérdezhetik le, hogy egy azonosító érvényes átmeneti tár, megjelenítési lista vagy anyagmintázat-e. Ezekkel a függvényekkel késõbbi fejezetekben foglalkozunk. 1.1.3.6. Egyéb lekérdezõfüggvények Az OpenGL más lekérdezõfüggvényei a megvilágítással, illetve a mintázattérképezéssel kapcsolatos értékek lekérdezését teszik lehetõvé az alkalmazásoknak. A megvilágítási és mintázattérképezési állapotparaméterek beállításával a 4. és 6. fejezetben foglalkozunk, de a kötetben nem tárgyaljuk ezen értékek lekérdezését. Kíváncsiságunkat az OpenGL® Reference Manual „glGetLight”, „glGetMaterial”, glGetTexEnv”, „glGetTexGen”, „glGetTexImage” és „glGetTexParameter” címszavait fellapozva elégíthetjük ki. 1.1.3.7. Hibák Alkalmazásunk többféle módon is hibákat válthat ki az OpenGL-ben. Ha az alkalmazás olyan hibát vált ki, amelyet az OpenGL észlel, a hibát a glGetError() függvénnyel kérdezhetjük le:
GLenum glGetError( void ); Ez a függvény az aktuális hibaállapotot adja vissza. A glGetError() által visszaadott hibaértékeket az 1.2. táblázatban foglaltuk össze. Amennyiben az OpenGL nem rögzített hibát, a glGetError() GL_NO_ERROR-t ad vissza. OpenGL-változat: 1.0 vagy újabb
Amikor az OpenGL hibát észlel, egy hibakódot rögzít, és folytatja a parancsok feldolgozását. A rögzített hibakódot, amely GLenum típusú, az alkalmazás a glGetError() meghívásával tudakolhatja meg. Az OpenGL-hibákat az 1.2. táblázat foglalja össze. Tegyük fel például, hogy alkalmazásunk az alábbi hívást adja ki: glEnable( GL_MAX_LIGHTS );
Az OpenGL ekkor GL_INVALID_ENUM hibát jegyez fel, mert a GL_MAX_LIGHTS a glGetIntegerv() esetében érvényes felsorolás, a glEnable() esetében azonban nem. Az OpenGL addig tárolja a hibakódokat, amíg le nem kérdezzük azokat a glGetError() függvénnyel, tehát ha az alkalmazásnak az említett függvény hibát jelez, a hibát a megfelelõ OpenGL-parancsok bármelyike okozhatta.
1. fejezet • Bevezetés az OpenGL világába 17
1.2. táblázat A glGetError()* által visszaadott OpenGL-hibakódok GLenum-hibakód GL_INVALID_ENUM GL_INVALID_OPERATION GL_INVALID_VALUE GL_OUT_OF_MEMORY GL_STACK_OVERFLOW GL_STACK_UNDERFLOW
Leírás Egy OpenGL-parancs érvénytelen felsorolást kapott. Az aktuális állapotnak nem megfelelõ vagy abban érvénytelen OpenGL-parancsot adtunk ki. A megengedett tartományon kívül esõ értéket adtunk át az OpenGL-nek. Az OpenGL nem tudott elegendõ memóriát lefoglalni egy parancs végrehajtásához. Egy parancs túlcsordulást idézett elõ egy OpenGL-veremben. Egy parancs alulcsordulást idézett elõ egy OpenGL-veremben.
*. A felsorolt hibákon kívül az OpenGL GL_TABLE_TOO_LARGE hibát is visszaadhat. Errõl további információt a The OpenGL Graphics System-ben találhatunk.
Amikor egy OpenGL-parancs hibát vált ki, az OpenGL viselkedése általában elég jól meghatározható: a hibát okozó hívás üres utasításként (no-op) viselkedik (vagyis a függvény nem lesz hatással az OpenGL állapotára vagy a képkockatárra), és ha értéket ad vissza, az érték nulla lesz. A GL_OUT_OF_MEMORY hiba kivétel; ebben az esetben az OpenGL nem meghatározható állapotba kerül.4 Jellemzõ használat
Az OpenGL-hibákat kezeljük az alkalmazásunk kijavítandó programhibáiként. Egy üzemi környezetbe helyezett végleges kódnak nem szabad OpenGL-hibákat kiváltania. Az alkalmazások általában az OpenGL-elõkészítõ kódok lefutása, illetve egy animáció minden képkockájának leképezése után hívják meg a glGetError() függvényt. A programozók jellemzõen csak fejlesztés közben alkalmaznak glGetError() hívásokat, a végleges üzemi kódban nem, hogy azok ne legyenek negatív hatással az alkalmazás sebességére. Ez egy assert() hívással érhetõ el: assert( glGetError() == GL_NO_ERROR );
A fejlesztõk rendszerint CPP-makrókkal ellenõrzik az OpenGL-hibákat, illetve hajtanak végre ehhez kapcsolódó mûveleteket (például hibaüzenetet jelenítenek meg egy ablakban, vagy kivételt váltanak ki), a makrókat pedig no-opként határozzák meg az üzemi kódban. A könyvhöz tartozó példakód ezt a megközelítést az OGLDIF_CHECK_ERROR CPPmakróval mutatja be. Az említett makró bevezetését a D függelékben láthatjuk, ahol az OpenGL-hibákat kiváltó kódok hibáinak megkereséséhez tanácsokat is adunk.
4 Ha a glEndList() vált ki GL_OUT_OF_MEMORY hibát, az OpenGL állapota meghatározott marad; lásd a „glEndList” címszót az OpenGL Reference Manual-ben.
18 OpenGL röviden
1.2. GLUT A fejezet korábbi, Állapotok és lekérdezések címû részében említettük, hogy az állapotokat betokozó OepnGL leképezési környezetek létrehozásáért platformfüggõ ablakkönyvtárak felelnek, ami sajnos azzal jár, hogy platformfüggõ kódot kell írnunk. Az OpenGL Utility Toolkit (GLUT) azonban szerencsére platformfüggetlen könyvtár, amellyel az ablakokat, az adatbevitelt és a leképezési környezeteket kezelhetjük. Nem helyettesíti teljes egészében a platformfüggõ kódot, de példaprogramokhoz és bemutatókhoz, valamint egyes egyszerû alkalmazásokhoz elegendõ hasznos szolgáltatást nyújt. A GLUT-függvények neve a glut*() formát követi; a tárcsere GLUT-függvénye például a glutSwapBuffers(). Jelenleg a GLUT 3.7-es változata a legfrissebb. A GLUT teszi lehetõvé, hogy kötetünkben az OpenGL-re összpontosíthassunk, ne pedig az ablakok és leképezési környezetek platformfüggõ létrehozására és kezelésére. Ezekkel a részletekkel a 8. fejezetben foglalkozunk majd, amikor már ismerõsebben csengenek az OpenGL fogalmai. A GLUT (illetve a GLX) alapmûve az OpenGL Programming for the X Window System (Kilgard 1996), de a GLUT-ról az OpenGL webhelyén is szerezhetünk további ismereteket, ha ellátogatunk a következõ címre: http://www.opengl.org/resources/libraries/index.html
Fejezetünk késõbbi, Egy egyszerû példa címû részében részletesebben is megismerkedünk a GLUT néhány alapfüggvényének használatával.
1.3. GLU A GLU az OpenGL Utility Library rövidítése. Ez egy olyan, az ARB által jóváhagyott függvénykönyvtár, amely a legtöbb OpenGL-megvalósításban hozzáférhetõ. A könyvtár olyan, az OpenGL-ben közvetlenül nem elérhetõ, magasabb rendû mûveletekhez és szolgáltatásokhoz nyújt támogatást, mint a kamera helyzetének és tájolásának szabályozása, a magasabb rendû görbék és felszínek, a több sokszögbõl álló primitívek (például hengerek és gömbök), az ablak-koordinátarendszerre vagy onnan való vetítés, illetve a sokszög-mozaikolás. A GLU-függvények neve a glu*() formát követi; a nézettranszformáció GLU-függvénye például a gluLookAt(). Jelenleg a GLUT 1.3-as változata a legfrissebb.
1. fejezet • Bevezetés az OpenGL világába 19
Az OpenGL® röviden csak néhány gyakran használatos GLU-eljárást mutat be. Ha mindent tudni szeretnénk a GLU-ról, töltsük le a The OpenGL Graphics System Utility Library címû GLU-szabványt az OpenGL webhelyérõl, a www.opengl.org/resources/libraries/ index.html címrõl, de a GLU-hoz kitûnõ kézikönyv az OpenGL® Reference Manual is.
1.4. A fejlesztõkörnyezet OpenGL-programok fejlesztéséhez rendelkeznünk kell egy fejlesztõkörnyezettel, amely tartalmazza a szükséges könyvtárakat és fejállományokat: • az OpenGL fejállományt és könyvtárat, • a GLU fejállományt és könyvtárat, • a GLUT fejállományt és könyvtárat, vagy az adott platformnak és ablakrendszernek megfelelõ fejállományokat és könyvtárakat. Az, hogy ezek az összetevõk hogyan szerezhetõk be, a választott fejlesztõplatformtól függ. Kötetünkben három népszerû rendszert vizsgálunk: az Apple Mac OS X-et, a Linuxot és a Microsoft Windowst. A példaprogram forráskódjának lefordításához a fentiek mellett szükségünk lesz még a libTIFF-re és a GNU Make-re. Arról, hogy a választott fejlesztõplatformunkhoz illõ összetevõket hogyan szerezhetjük be, a könyv webhelyén találhatunk információkat. Ha egy jó hírû számítógépgyártótól olyan új számítógépet vásárolunk, amelynek grafikus kártyája támogatja az OpenGL-t, nagy az esély rá, hogy a rendszert már beállították az OpenGL hardveres gyorsítást alkalmazó futtatására. A grafikus kártyák gyártói azonban gyakran frissítik az eszközmeghajtóikat, hogy újabb szolgáltatásokkal bõvítsék azokat, hibákat javítsanak ki bennük, vagy növeljék a sebességet. Ha frissíteni szeretnénk grafikus kártyánk eszközmeghajtóját (illesztõprogramját), töltsük le az új változatot a számítógép vagy a grafikus kártya gyártójának webhelyérõl, és kövessük a telepítési utasításokat.
1.4.1. Apple Mac OS X Az Apple Developer Connection kimerítõ mennyiségû OpenGL-információhoz nyújt hivatkozásokat, köztük olyan dokumentációkhoz, amelyekbõl megtudhatjuk, hogyan fejleszthetünk OpenGL-alkalmazásokat Mac OS X rendszeren, milyen eszközökkel végezhetünk bennük hibakeresést, és hogyan optimalizálhatjuk Mac OS X-es OpenGL-alkalmazásainkat.
20 OpenGL röviden
A Macintosh OpenGL Programming Guide, amely az alábbi webhelyen érhetõ el, kitûnõ információforrás az Apple rendszeren fejlesztett OpenGL-alkalmazásokhoz: http://developer.apple.com/graphicsimaging/opengl
Az OpenGL fejállományokat és könyvtárakat Mac OS X rendszerünknek az OpenGL keretrendszerhez tartozó mappájában találhatjuk meg: /System/Library/Frameworks/OpenGL.framework
Az AGL az Apple egyik platformfüggõ OpenGL-felülete, amellyel a 8. fejezetben foglalkozunk részletesebben. Ha olyan kódot szeretnénk építeni, amely az AGL-t használja, az AGL keretrendszer mappájában levõ fejállományokra és könyvtárakra lesz szükségünk: /System/Library/Frameworks/AGL.framework
Példáink többsége a platformfüggetlen GLUT felületet használja, de az Apple rendszerek ezt a felületet is támogatják: /System/Library/Frameworks/GLUT.framework
OpenGL-alkalmazásokat Macintoshon kétféleképpen fejleszthetünk: az Xcode IDE segítségével vagy egy a GNU fordítókat közvetlenül használó makefile rendszerrel. A könyvhöz tartozó példaprogram kódja ez utóbbi megoldást alkalmazza, és módosítás nélkül lefordíthatónak kell lennie egy parancshéjból. Ha az Xcode-ot szeretnénk használni, elõbb be kell azt állítanunk a megfelelõ keretrendszer használatára. Az Apple Mac OS X rendszer alábbi mappájában több OpenGL-mintaprogramot is találunk, amelyek az Xcode-ot használják: /Developer/Examples/OpenGL/GLUT
Az itt található példákat útmutatóként használhatjuk, ha az Xcode-dal szeretnénk fejleszteni. A példakód letöltéséhez látogassunk el az OpenGL® röviden webhelyére, ahol arról is friss információkat találhatunk, hogy a kód hogyan fordítható le Apple Mac OS X rendszeren.
1.4.2. Linux Az OpenGL® röviden példakódját Red Hat Linux Enterprise WS rendszeren készítettük, de az OpenGL-fejlesztés más Linux- és Unix-rendszereken is hasonlóan történik. A teljes Linux-terjesztéseknek rendszerint része egy fejlesztõkörnyezet, amely tartalmazza az OpenGL-t, a GLU-t, a GLUT-ot vagy a freeglut-ot, valamint GLX-fejállományokat és -könyvtárakat. Ha Linux rendszerünkrõl ezek az összetevõk hiányoznak, célszerû besze-
1. fejezet • Bevezetés az OpenGL világába 21
rezni a Mesát. A Mesa egy 3D API, amelynek felülete nagyon hasonló az OpenGL-éhez, és tartalmazza az OpenGL-t, a GLU-t, a GLUT-ot, valamint a GLX-fejállományokat és -könyvtárakat. Több Linux-terjesztés is a Mesát használja a hardveres gyorsítású OpenGLhez. Ha többet szeretnénk tudni róla, vagy le szeretnénk tölteni a Mesa forráskódját, látogassunk el a Mesa webhelyére az alábbi címen: http://www.mesa3d.org
Ha Linux rendszerünket egy jó hírû számítógépgyártótól vásároltuk, nagy az esély rá, hogy a rendszert teljes hardveres gyorsításra állították be. Ezt a glxinfo programmal ellenõrizhetjük. Ha Linux rendszerünket újonnan telepítjük, és grafikus kártyánknak nincs nyílt forrású eszközmeghajtója, el kell látogatnunk a grafikus kártya gyártójának webhelyére, hogy beszerezzük a linuxos meghajtót, másképpen az OpenGL-leképezés gyorsítás nélkül történik majd. Amennyiben OpenGL-fejállományaink a szokásos helyen (/usr/include/GL) találhatók, nem kell az –I kapcsolóval meghatároznunk a helyüket; a fordítónak önmûködõen meg kell találnia azokat. Az OpenGL-programok összeszerkesztési kapcsolói jellemzõen a következõk: -L/usr/X11R6/lib –lglut –lglu -lgl.
1.4.3. Microsoft Windows A Microsoft Windowshoz tervezett OpenGL-fejlesztõkörnyezet a Windows 98 óta stabil.5 Az alábbi információk tehát érvényesek a Windows 98, a Windows ME, a Windows 2000 és a Windows XP rendszerre is, valamint a Windows NT 3.5-ös és annál újabb változataira. A Microsoft Visual Studio .NET az OpenGL 1.1 fejállományait és exportbekötõ könyvtárfájljait tartalmazza. Amikor egy alkalmazás összeszerkesztünk (linking), az opengl32.lib állományt kell megadnunk bemeneti könyvtárként. A Microsoft az operációs rendszerhez mellékeli az opengl32.dll nevû futásidejû dinamikus csatolású könyvtárat (dll-t). Az 1.1-esnél újabb szolgáltatások és bõvítmények eléréséhez az 1.1-es fejállományon kívül szükségünk van a glext.h fejállományra is. Ennek legfrissebb változata az OpenGL Extension Registry webhelyrõl szerezhetõ be: http://oss.sgi.com/projects/ogl-sample/ABI/glext.h
A Microsoft fordítóprogramjainak része a GLU 1.2 fejállomány és exportbekötõ könyvtárfájl. Alkalmazásainkat a glu32.lib-bel kell összeszerkesztenünk; a GLU futásidejû dinamikus csatolású könyvtára része a Microsoft Windows operációs rendszernek. 5 A Microsoft bejelentette, hogy jövõbeli operációs rendszereiben már frissített, az újabb változatokhoz igazodó OpenGL-támogatást kíván nyújtani.
22 OpenGL röviden
A GLUT-ot az OpenGL webhelyérõl, a http://www.opengl.org címrõl tölthetjük le. Mivel itt számos letöltési lehetõséget találunk, gyõzõdjünk meg róla, hogy a Microsoft Windowshoz elõre lefordított binárisokat töltjük le, nem pedig más platformhoz készített forráskódokat vagy bináris állományokat. A GLUT-ot letöltése után saját kezûleg kell telepítenünk. Ehhez a következõ állományokkal kell rendelkeznünk: • glut.h – A GLUT fejállománya • glut32.lib – Az exportbekötõ könyvtárfájl • glut32.dll – A futásidejû dinamikus csatolású könyvtár Helyezzük a glut.h állományt fordítónk alapértelmezett include könyvtárának GL alkönyvtárába. Ennek elérési útja fordítóprogramonként különbözik, de a vége mindig …\include\GL. A gl.h és glu.h állományoknak már ebben a könyvtárban kell lenniük. Helyezzük a glut32.lib állományt a fordító alapértelmezett lib könyvtárába, végül pedig tegyük a glut32.dll-t az operációs rendszer System32 mappájába (például C:\Windows\System32) vagy bármely olyan mappába, amely a DLL-ek keresési útvonalában szerepel.
1.5. Egy egyszerû példa Az 1.1 példában szereplõ OpenGL-kód, bár igen egyszerû, bevezetésnek tökéletesen megfelel. A program neve SimpleExample, vagyis EgyszerûPélda, és letölthetõ a könyv webhelyérõl. A program egy fehér háromszöget rajzol fekete háttérre, ahogy az 1.3. ábrán is láthatjuk. 1.1. példa
A SimpleExample program
#include
#include #include #include <string> #include <sstream> #include // Azonosító meghatározása a „Quit” menüponthoz static const int QUIT_VALUE( 99 ); // Globális változó a megjelenítési lista azonosítójának tárolásához GLuint listID; // // GLUT megjelenítési visszahívás. A GLUT akkor hívja meg, amikor // az ablakot újra kell rajzolni. static void display() {
1. fejezet • Bevezetés az OpenGL világába 23 glClear( GL_COLOR_BUFFER_BIT ); // Modelltranszformáció; az alakzat hátrébb helyezése // a Z tengelyen 4 egységgel glLoadIdentity(); glTranslatef( 0.f, 0.f, -4.f ); // Az alakzat kirajzolása glCallList( listID ); // Tárcsere (a leképezett kép megjelenítése) glutSwapBuffers(); assert( glGetError() == GL_NO_ERROR ); } // // GLUT átméretezési visszahívás. A hívásra akkor kerül sor, amikor // az ablak mérete megváltozik. static void reshape( int w, int h ) { // A nézetablak frissítése a teljes ablakra rajzoláshoz glViewport( 0, 0, w, h ); // A vetületmátrix / oldalarány frissítése glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 50., (double)w/(double)h, 1., 10. ); // Modell–nézet módba kapcsolás a megjelenítési eljáráshoz glMatrixMode( GL_MODELVIEW ); assert( glGetError() == GL_NO_ERROR ); } // // GLUT menü-visszahívás. Akkor hívódik meg, ha a felhasználó kiválaszt // egy menüpontot. static void mainMenuCB( int value ) { if (value == QUIT_VALUE) exit( 0 ); } static void init() { // A kiegyenlítés alapállapotban be van kapcsolva, de nincs rá // szükség, ezért kikapcsoljuk. glDisable( GL_DITHER ); // Annak a meghatározása, hogy elérhetõk-e csúcstömbök. // Más szóval: OpenGL v1.1-gyel van dolgunk? std::string ver((const char*) glGetString( GL_VERSION )); assert( !ver.empty() ); std::istringstream verStream( ver ); int major, minor; char dummySep; verStream >> major >> dummySep >> minor; const bool useVertexArrays = ( (major >= 1) && (minor >= 1) ); const GLfloat data[] = {
24 OpenGL röviden -1.f, -1.f, 0.f, 1.f, -1.f, 0.f, 0.f, 1.f, 0.f }; if (useVertexArrays) { // Felkészülés a csúcstömbök használatára glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, data ); } // Új megjelenítési lista létrehozása listID = glGenLists( 1 ); glNewList( listID, GL_COMPILE ); if (useVertexArrays) // Az OpenGL 1.1-ben vannak csúcstömbök, ezért ezeket használjuk glDrawArrays( GL_TRIANGLES, 0, 3 ); else { // Az OpenGL 1.0 Begin/End felületének használata glBegin( GL_TRIANGLES ); glVertex3fv( &data[0] ); glVertex3fv( &data[3] ); glVertex3fv( &data[6] ); glEnd(); } glEndList(); assert( glGetError() == GL_NO_ERROR ); // A megjelenítési és átméretezési visszahívások bejegyzése a GLUT-ba glutDisplayFunc( display ); glutReshapeFunc( reshape ); // Jobb egérgombos menü létrehozása, hogy a felhasználók kiléphessenek int mainMenu = glutCreateMenu( mainMenuCB ); glutAddMenuEntry( "Quit", QUIT_VALUE ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } int main( int argc, char** argv ) { glutInit( &argc, argv ); // Egyetlen, 300x300 képpontos, RGB módú, kétszeres tárazású GLUT-ablak // létrehozása. Az ablak neve legyen „Simple Example” glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); glutInitWindowSize( 300, 300 ); glutCreateWindow( "Simple Example" ); init(); // Ciklus az eseményekhez glutMainLoop(); return 0; }
1. fejezet • Bevezetés az OpenGL világába 25
1.3. ábra A SimpleExample program kimenete
Az elsõ három fejállomány – a , a és a – a GLUT, a GLU és az OpenGL számára tartalmaznak meghatározásokat. Ezeket néhány szabványos C/C++ fejállomány követi. A fenti kód a C elõfeldolgozó segítségével határoz meg egy értéket, amely a Quit (Kilépés) menüpontot azonosítja. Amikor a felhasználó kinyitja a menüt, és kiválasztja belõle a Quit elemet, a GLUT átadja ezt az azonosítót a menü visszahívó függvényének. A modern programozási stílus kerüli a globális változók használatát, bár azok továbbra is hasznosak, és az ehhez hasonló, kis méretû bemutatóprogramokban elfogadottak. Esetünkben a kód egy OpenGL megjelenítési listához határoz meg globális azonosítót. Az alkalmazás az init() függvényben ad neki kezdõértéket, és a display() visszahívásban hivatkozik rá. Az OpenGL megjelenítési listái sokféle OpenGL-parancsot tárolhatnak, ebben a listában azonban csak rajzolási parancsok találhatók. A megjelenítési listákról a 2. fejezetbõl tudhatunk meg többet. A GLUT szükség szerint hívja meg a display() visszahívó függvényt, amikor frissítenie kell az ablakot. A példában ez a függvény olyan OpenGL-hívásokat tartalmaz, amelyek törlik az ablak tartalmát, betöltenek egy transzformációs mátrixot, meghívják a háromszöget kirajzoló parancsokat tároló megjelenítési listát, illetve hibákat keresnek, de találunk benne egy GLUT-hívást a tárak cseréjéhez is.
26 OpenGL röviden
A következõ függvény a reshape(), amely szintén GLUT-visszahívás, és a végrehajtására akkor kerül sor, amikor a felhasználó átméretezi az ablakot. A függvény által kiadott OpenGL-parancsok a teljes ablakra rajzolnak, és gondoskodnak a helyes oldalarányról, illetve a megfelelõ vetítésrõl. Ez a függvény is tartalmaz hibaellenõrzést. A mainMenuCB() függvényt akkor hívja meg a GLUT, amikor a felhasználó kiválaszt egy elemet a menübõl. Ez a függvény csak azt vizsgálja, hogy a választott menüpont a Quit-e, és ha igen, befejezi a program futását. Az OpenGL beállításáról és a megjelenítési lista létrehozásáról az init() függvény gondoskodik. Az OpenGL-ben sokféleképpen lehet primitíveket rajzolni. A példa egyszerûsége érdekében itt csak két módszert mutatunk be: a glBegin(), illetve glEnd(), valamint a csúcstömbök használatát. Mivel ez utóbbiak az OpenGL 1.0-ban nem érhetõk el, a kód elõször lekérdezi az OpenGL-változatot. Amennyiben a változat száma 1.1 vagy magasabb, a kód a háromszög kirajzolásához egy csúcstömb-leképezési parancsot alkalmaz; más esetben a glBegin() és glEnd() függvényeket. Az OpenGL-alkalmazásokban megszokott, hogy a változattól függõen választják ki a használandó szolgáltatásokat. Az alkalmazás a használt módszertõl függetlenül egy megjelenítési listában tárolja a rajzolási parancsokat. Megjegyzendõ, hogy a megjelenítési listák csak OpenGL-parancsokat tárolnak. A változatot ellenõrzõ feltételes rész végrehajtására csak a megjelenítési lista létrehozásakor kerül sor, hogy meghatározhassuk annak tartalmát. Amikor a display() függvény végrehajtja (vagy meghívja) a listát, csak a tárolt OpenGL-parancsok hajtódnak végre – vagy a csúcstömbhívás, vagy a glBegin()/glEnd hívások, de a feltételes rész nem. A megjelenítési listák hatékonyan csökkentik a függvényhívások számát, és egyben szükségtelenné teszik a futásidõben állandó feltételek vizsgálatát. Utolsó lépésként az init() függvény bejegyzi a visszahívó függvényeket, és létrehozza a GLUT-menüt. A main() eljárás feladata a GLUT felkészítése az ablak és a leképezési környezet létrehozására. Miután létrehozta az ablakot, a main() meghívja az init()-et, hogy további elõkészítést végezzen, végül pedig a glutMainLoop() hívása következik, amely eseményeket és üzeneteket vár, és szükség esetén visszahívja az alkalmazást.
1.6. Az OpenGL története Ahhoz, hogy megértsük, hogyan és miért fejlesztették ki az OpenGL-t, át kell tekintenünk a grafika helyzetét az OpenGL elõtt.
1. fejezet • Bevezetés az OpenGL világába 27
1.6.1. 1992 elõtt 1980-ban a SIGGRAPH Core csoport újjáalakult, hogy kidolgozzon egy új ANSI-szabványként mûködõ grafikus API-t, amelyet PHIGS-nek (Programmer’s Hierarchical Interactive Graphics System) neveztek. Az 1980-as évek végére a PHIGS/PHIGS+ API-t a legtöbb grafikus terminál és munkaállomás támogatta. Mindazonáltal a megvalósítások rendszerrõl rendszerre különböztek, ami a független szoftvergyártókat (ISV, independent software vendor) megakadályozta abban, hogy platformfüggetlen alkalmazásokat készítsenek. A Silicon Graphics Inc.6 1982-ben megkezdte saját, szabadalmaztatott API-jának, az Iris GLnek a fejlesztését. Ahogy a PHIGS fejlõdött az 1980-as években, az Iris GL egyre népszerûbbé vált: az SGI egyértelmûen vezetõ szerepet töltött be az interaktív számítógépes térgrafika piacán, mind a fejlesztõk, mind a végfelhasználók körében. Ennek részben az volt az oka, hogy a fejlesztõk úgy érezték, hogy az Iris GL rugalmasabb és könnyebben használható volt a korszak más API-jainál. Az 1980-as évek végére az SGI olyan vállalatoknak engedélyezte az Iris GL használatát, mint az IBM vagy az Nth Portable Graphics. Az ISV-k számára azonban az egységes megvalósítás és teljesítmény hiánya továbbra is probléma maradt. A független szoftvergyártóknak azzal a gonddal is szembe kellett nézniük, hogy az ablakrendszerek sem voltak egységesek. Számos munkaállomás-gyártó saját ablakkezelõ rendszert használt; ilyen volt például a NeWS vagy az OpenWindows. 1985-re azonban az ipar megállapodott egy közös ablakrendszer mellett: ez volt az X Window System (Scheifler 1986). Az X Window rendszer azzal a reménnyel kecsegtetett, hogy a hordozhatósággal kapcsolatban legalább néhány kérdést megold, azzal, hogy az alkalmazások számára észrevétlenné teszi a hálózatokat. Minden számítógép, amely támogatta az X Window protokollt, képes volt kimenetet megjeleníteni bármely X Window alkalmazásból. 1987-ben a DEC és a Sun közösen kidolgozott egy bõvítményt az X Window rendszerhez a hálózatfüggetlen interaktív térgrafikai alkalmazások támogatására, amelynek az X3D 7 nevet adták, de PEX 8 néven jobban ismerik. Ezt a bõvítményt eredetileg a hálózati PHIGSalkalmazásokhoz szánták, de késõbb a PEXlib API vált belõle, amely egy vékony réteg az X3D protokoll felett. Teljes PEX-megvalósítások az 1980-as évek végétõl érhetõk el (Rost 1989). Az 1980-as évek végére az Iris GL alapú alkalmazásokat készítõ független szoftvergyártók azt követelték, hogy az SGI alakítsa egységes nyílt szabvánnyá az Iris GL-t. Allen Akin, az SGI OpenGL-csapatának mûszaki igazgatója, így idézi fel az OpenGL kifejlesztésére irányuló nyomást:
6 A Silicon Graphics Inc.-re évekig hivatkoztak SGI néven, míg a cég 1999-ben hivatalosan is SGI-re változtatta a nevét. 7 Nem keverendõ a Web3D azonos nevû VRML-utódjával. 8 A PEX a PHIGS Extension to X Windows rövidítése, de a bõvítmény valódi neve X3D.
28 OpenGL röviden
„A fõ ok az volt, hogy a szoftvergyártók azt mondták, a túlélés érdekében bõvíteniük kell az általuk támogatott hardvereszközök körét, és ezért választás elé állították az SGI-t: vagy megnyitja valamilyen formában az Iris GL-t, vagy nem támogatják többé az SGI hardverét, hanem nagyobb volumenben gyártott munkaállomásokat keresnek. Az egyik lehetséges API, amely felé ezek a szoftvergyártók fordulhattak volna, ha az OpenGL nem születik meg, a PEXlib volt.”9 Az SGI 1989-ben indította el az OpenGL projektet, 1991-ben pedig megalakította az OpenGL ARB-t, mely testületben több cég is képviselteti magát, és felügyeli a fejlesztést. Az ARB 1992 júniusában jelentette meg az OpenGL-szabvány 1.0-s változatát, amelyet hamarosan követtek a DEC, az IBM és az SGI kereskedelmi megvalósításai. Az OpenGL-t az SGI-vel kapcsolatban álló ISV-k széles körének igényei alapján tervezték, ezért már az 1.0-s változat szolgáltatásai is széles skáláját támogatták a 3D-s grafikai programoknak.
1.6.2. Az OpenGL és az Iris GL Az OpenGL fejlesztését az SGI az Iris GL-re építette, így természetes, hogy sok köztük a hasonlóság. Vegyük például az alábbi Iris GL-kódot, amellyel egy ötoldalú sokszöget rajzolhatunk: bgnpolygon(); c3s( red ); v3f( v0 ); c3s( green ); v3f( v1 ); c3s( blue ); v3f( v2 ); c3s( black ); v3f( v3 ); c3s( white ); v3f( v4 ); endpolygon();
A fenti kód a színekre és csúcsokra vonatkozó parancsokat a bgnpolygon() – endpolygon() pár közé zárja. A kód a csúcsokat úgy határozza meg, hogy átadja a címüket a v3f() függvénynek (a függvény neve egy három lebegõpontos érték által kijelölt csúcsra utal), az egyes csúcsok színét pedig a c3s()-nek (itt a név egy három short értékkel kijelölt színre utal). Figyeljük meg, mennyire hasonlít mindez az OpenGL 1.0 megfelelõjére: glBegin( GL_POLYGON ); glColor3sv( red ); glVertex3fv( v0 ); glColor3sv( green ); glVertex3fv( v1 ); glColor3sv( blue ); glVertex3fv( v2 );
9 Levél a comp.graphics.api.opengl Usenet-hírcsoportban, 2001. március 6.
1. fejezet • Bevezetés az OpenGL világába 29
glColor3sv( black ); glVertex3fv( v3 ); glColor3sv( white ); glVertex3fv( v4 ); glEnd();
1.6.3. Az OpenGL és a PEX Az SGI Analysis of PEX 5.1 and OpenGL 1.0 (Akin 1992) címû mûszaki tanulmánya az OpenGL és a PEX közötti különbségeket vizsgálta. A szolgáltatásokban mutatkozó különbségek némelyike jelentõs volt: a PEX az OpenGL 1.0 megjelenésekor például nem támogatta az olyan szolgáltatásokat, mint a mintázattérképezés vagy az áttetszõségkeverés. Az OpenGL a filozófiáját tekintve is több ponton eltávolodott a PEX-tõl: • Az OpenGL meghatároz egy API-t, és tartalmaz egy adatátviteli protokollt is a hálózatos mûködéshez (GLX). A PEX ezzel szemben csak egy adatátviteli protokollt határoz meg, amelyet eredetileg a PHIGS API támogatására szántak. • Az OpenGL 1.0-s változata minden megvalósítástól megköveteli az összes szolgáltatásának a támogatását, míg a PEX részhalmaz alapú megközelítése megengedte a megvalósításoknak, hogy csak egyet támogassanak a három, egymást kölcsönösen kizáró mûködési mód közül. A PEX-részhalmazok akadályt gördítettek a hordozható alkalmazások fejlesztése elé, ráadásul a PEX számos nem kötelezõ szolgáltatással rendelkezik, ami tovább nehezíti a hordozhatóságot. • A PEX-et kizárólag X Window környezetekhez tervezték, míg az OpenGL független az ablakrendszertõl, így létezhetnek megvalósításai X Window, Microsoft Windows, Apple Mac OS X, sõt nem ablakos környezetekre is. Az 1990-es évek végére a legtöbb független szoftvergyártó átültette PHIGS és PEXlib alapú alkalmazásait OpenGL-re. Mára a PHIGS és a PEX fejlesztése lényegében leállt.
1.6.4. Az OpenGL töretlen fejlõdése Az OpenGL bõvíthetõsége lehetõvé teszi a gyártóknak és az ARB-nek, hogy a grafikai technológia fejlõdésével összhangban új szolgáltatásokkal egészítsék ki az API-t (lásd a 7. fejezetet). A mintázattérképezés, amely az 1990-es évek elején a kutatás és fejlesztés középpontjában állt, erõsen épített erre. Hamarosan több OpenGL-megvalósítás is támogatta a mintázatobjektumokhoz, a mintázatok és almintázatok másolásához, a mintázatok hardveres támogatásának lekérdezéséhez, illetve a különféle mintázatformátumokhoz készített bõvítményeket. Végül 1996 januárjában az ARB megjelentette az OpenGL 1.1-es változatát, amely formálissá tett jónéhányat ezek közül a bõvítmények közül. Az 1990-es években az olcsó kereskedelmi asztali PC-k egyre erõsebbé váltak, és ennek megfelelõen a hagyományos munkaállomások piacán is egyre nagyobb részesedést értek
30 OpenGL röviden
el. A mûszaki fejlõdés az egyetlen lapkára épített, olcsó grafikus hardver fejlesztésének is lökést adott, kezdetben csak az OpenGL töredékmûveleteihez, végül azonban a teljes OpenGL-csõvezetékhez gyorsítást nyújtva (Geist 1998, McCormack 1998). Az 1990-es évek végére a grafikus hardver fejlõdése egyre növekvõ számú OpenGLbõvítményt eredményezett, ami bonyolultabbá tette az OpenGL-alkalmazások fejlesztését. Erre válaszul az ARB egyre gyorsabb ütemben adta ki az OpenGL újabb és újabb változatait. Az OpenGL 1.2 és annak 1.2.1-es alváltozata egyaránt 1998-ban jelent meg, és mindkettõ számos mintázattérképezési kiegészítést tartalmazott. Az OpenGL 1.3 2001 augusztusi kiadása óta az ARB évente jelentet meg új változatokat; az OpenGL 2.0 2004-ben került a piacra. 2002-ben az OpenGL ES (OpenGL for embedded systems, vagyis OpenGL beágyazott rendszerekhez) a kézi eszközökön és más korlátozott memóriával vagy számítási teljesítménnyel rendelkezõ platformokon is elérhetõvé tette az OpenGL-t. Az OpenGL ES az OpenGL szolgáltatásainak csak egy részhalmazát támogatja, de támogatást nyújt a rögzített pontos adattípusokhoz is.
1.7. További információk Az OpenGL-rõl további általános információkat találunk az OpenGL® Programming Guide 1. fejezetében (Introduction to OpenGL), az OpenGL webhelyén, a http://www.opengl.org címen pedig mûszaki és nem mûszaki áttekintést is olvashatunk. A Computer Graphics: Principles and Practice és a 3D Computer Graphics egyaránt ismerteti azokat az általános térgrafikai fogalmakat, amelyeket ebben a fejezetben is érintettünk (megvilágítás, elemekre bontás, mintázattérképezés, mélységtárazás stb.). A történelemrajongóknak ajánlott elolvasniuk az OpenGL Programming for the X Window System címû kötetet (Kilgard 1996), amely olvasmányosan ismerteti az OpenGL történetét, és további áttekintést ad az OpenGL-rõl. A könyv egyben a GLUT programozási alapmûve és nélkülözhetetlen kézikönyve. A fejezet korábbi, Egy egyszerû példa címû részében látott program, valamint más teljes példaprogramok forráskódja letölthetõ könyvünk webhelyérõl, de kódforrásokat az OpenGL webhelyén is találhatunk.
1. fejezet • Bevezetés az OpenGL világába 31
1.8. Források (Akin 1992) Akin, Allen: Analysis of PEX 5.1 and OpenGL 1.0. SGI mûszaki tanulmány, 1992. augusztus. (Carson 1997) Carson, George S.: Standars Pipeline: The OpenGL Specification. In: ACM SIGGRAPH Computer Graphics 31:2, 1997. május. (Cojot 1996) Cojot, Vincent S.: OpenGL Programming on Linux. In: Linux Journal, 1996. november. (Geist 1998) Geist, Robert és Westall, James: Bringing the High End to the Low End: High Performance Device Drivers of the Linux PC. In: ACM Southeast Regional Conference Proceedings, 1998. április. (Kilgard 1996) Kilgard, Mark J.: OpenGL Programming for the X Window System. Reading, MA, Addison-Wesley, 1996. (Kilgard 1997) Kilgard, Mark J.: Realizing OpenGL: Two Implementations of One Architecture. In: Proceedings of the ACM SIGGRAPH/EUROGRAPHICS Workshop on Graphics Hardware, 1997. augusztus. (Lichtenbelt 1997) Lichtenbelt, Barthold: Design of a High Performance Volume Visualization System. In: Proceedings of the ACM SIGGRAPH/EUROGRAPHICS Workshop on Graphics Hardware, 1997. augusztus. (McCormack 1998) McCormack, Joel és mások: Neon: A Single-Chip 3D Workstation Graphics Acccelerator. In: Proceedings of the ACM SIGGRAPH/EUROGRAPHICS Workshop on Graphics Hardware, 1998. augusztus. (Rost 1989) Rost, Randi J., Friedberg, Jeffrey D. és Nishimoto, Peter L.: PEX: A NetworkTransparent 3D Graphics System. In: IEEE Computer Graphics and Applications, 1989. (Scheifler 1986) Scheifler, Robert W. és Gettys, Jim: The X Window System. In: ACM Transactions on Graphics, 1986. április.