Tartalmi kivonat
Az OpenGL története és általános jellemzõi Az OpenGL-t (akkor még IrisGl) a Silicon Graphics (SGI) nevû amerikai cég fejlesztette ki, eredetileg saját grafikus munkaállomásainak programozására. Ezek a munkaállomások nagyon gyors grafikus célhardverek voltak, amelyek ultragyorsan végeztek olyan mûveleteket, amelyek szükségesek a grafikai számításokban, így a Silicon Graphics számítógépek rendkívül gyorsan végeztek például mátrixtranszformációkat. Azért, hogy a programokat más rendszerekre is át lehessen vinni, az SGI átdefiniálta az IrisGl-t, és az OpenGL nevet adta neki. Az egyik legfontosabb szempont az OpenGL kifejlesztésénél a hordozhatóság volt, ezért az OpenGL, az IrisGl-el szemben más rendszerek felé is nyitott (OpenGL = Open Graphics Library). Az elsõ OpenGL specifikációt 1992 júl. 1-én mutatták be Öt nappal késõbb, a legelsõ Win32 fejlesztõi konferencián az SGI az OpenGL mûködését egy Iris Indigo gépen
demonstrálta több grafikus alkalmazáson keresztül, mint pl. orvosi képfeldolgozó rendszerek, ill. részletek a Terminator 2 címû filmbõl. Ezután az SGI és a Microsoft együtt fejlesztették tovább az OpenGL-t. Az OpenGL egy szoftver interfész a grafikus hardverhez. Ez a szoftver interfész pár száz eljárásból és függvénybõl áll, melyek lehetõvé teszik 2 és 3 dimenziós grafikai objektumok létrehozását, és ezeken az objektumokon mûveletek elvégzését. Az OpenGL tehát egy eljárás- és függvénygyûjtemény, melyek 2 és 3 dimenziós geometriai objektumok specifikációját tartalmazzák; ezenkívül olyan eszközöket is nyújt, melyekkel szabályozni lehet ezen objektumok leképezését a képpufferbe, amelyben az OpenGL az eredményként létrejövõ képet tárolja. Ennek megjelenítése már az operációs rendszer, vagy az ahhoz tartozó ablakozó rendszer feladata. Itt elérkeztünk egy fontos dologhoz: az OpenGL nem tartalmaz ablakozó
rendszert, és nem támogatja az input eszközök kezelését sem, tehát ezeket a dolgokat az adott nyelven a programozónak kell megoldania (késõbb majd látni fogjuk, hogy azért mégsem ennyire rossz a helyzet). A Unix-os (Linux-os) OpenGL rendszerek grafikus felülete többnyire az X-Window, amely tartalmazza az ablakozást. Windows 95, 98 és NT esetén maga az operációs rendszer szolgáltatja a grafikus felületet. Az OpenGL platformfüggetlenségét az adatbeviteli és megjelenítési rendszertõl való függetlenség biztosítja. Ugyanezen okok miatt az OpenGL nem tartalmaz olyan parancsokat sem, amelyek magasszintû háromdimenziós objektumok specifikálására szolgálnak, így ezeket a modelleket geometriai primitívekbõl kell felépíteni (vonalak, pontok, sokszögek). Az OpenGL az ügyfél-kiszolgáló (kliens-szerver) felépítést követi. Ezáltal lehetõvé válik, hogy a grafikus alkalmazást futtató, és a végeredményt létrehozó gép egyazon, vagy
két különbözõ gép legyen. A grafikus alkalmazás - mint ügyfél - parancsokat ad az OpenGL kiszolgálónak, amely létrehozza a képet. Mivel a parancsok átadására szabványos protokollt dolgoztak ki, ezért az ügyfél és a kiszolgáló gépek különbözõ típusúak is lehetnek. Az OpenGL funkciói: • • • • • • • • a színtér definiálása háromdimenziós primitívekkel a nézõpont specifikálása megvilágítási modellek alkalmazása a megvilágított színtérrõl árnyalt modell készítése árnyékok és textúrák alkalmazása antialiasing (élsimítás) motion blur (mozgó objektumok körvonalainak elmosása) atmoszféra effektusok kezelése (pl.:köd) Az OpenGL alapfogalmai Az OpenGL primitíveket rajzol. A primitívek grafikai alapelemek Az OpenGL geometriai primitívei a pontok, a szakaszok és a sokszögek (poligonok).A geometriai primitíveket vertexek (csúcspontok, 3D pontok) definiálják. Egy vertex definiálhat egy pontot, egy
szakasz végpontját, vagy egy polygon csúcspontját, tehát minden OpenGL geometriai primitívet meg tudunk határozni a vertexeivel. A vertexek struktúrák, melyek tartalmazzák az illetõ csúcspont térbeli koordinátáit, színét és egyéb adatait. Az OpenGL minden vertexet függetlenül, rendezetten és ugyanúgy kezel. Az OpenGL más struktúrákat is használ, például pixelnégyszögeket, bittérképeket. Ezeket raszterprimitíveknek nevezzük Fontos, hogy megkülönböztessük a geometriai és raszterprimitíveket, mivel azokat az OpenGL eltérõ módon kezeli. Az OpenGL-t állapotautomataként is fel lehet fogni, mivel rendelkezik egy ún. state-tel (állapot) Ezen state tartalmazza azokat az érvényes adatokat, amelyek szükségesek a specifikált objektumok leképezéséhez. Tárolja, hogy pl. a világítás, azon belül mely fényforrások, az élsimítás, az árnyalás, stb. engedélyezve van, vagy le van tiltva Ezeket az információkat általában egyetlen
bit tárolja, ha a bit 1 akkor engedélyezett, ha 0 akkor nem. Az OpenGL-ben minden felhasznált paraméter rendelkezik egy iniciális vagy alapértelmezett (default) értékkel, pl: az alapértelmezett RGBA szín az (1.0, 10, 10, 10); az alapértelmezett transzformáció és vetítési mátrix pedig az egységmátrix. Koordináta-rendszerek Az OpenGL a megjelenítéskor a Descartes féle koordináta rendszert használja (Cartesian coordinate system), tehát a bázis olyan vektorokból áll, melyek mindegyike merõleges a többire. A koordinátákat a megszokott x, y, z hármassal jelöljük. Mivel a számítógépes grafikában leggyakrabban a jobbsodrású rendszerek használatosak, ezért az OpenGL is ezt használja. Jobbsodrású koordináta-rendszer esetén a (0, 0, 0) pontban van az origó, az x, y tengely pozitív része az origótól jobbra ill. fölfelé található, a z tengely pozitív része a képernyõbõl kifelé mutat. Szín módok Az OpenGL kétféle szín módot
használ: az RGBA szín módot, illetve a szín index módot. Az RGBA szín módban minden színt négy komponens definiál, a vörös (Red), zöld (Green), kék (Blue), illetve az alpha (Alpha) komponens. Minél nagyobb a komponens értéke, annál intenzívebben vesz részt a létrejövõ színben. Szín index módban minden színt egy lebegõpontos érték ír le, és minden ilyen lebegõpontos értékhez hozzá van rendelve három 8 bites érték a memóriában, rendre a három szín intenzitása. Szín mélység Mint ahogy a képernyõ felbontásának finomsága, a megjeleníthetõ színek száma is befolyásolja a kép részletességét, valósághûségét. A szín mélység azt jelenti, hogy a pixelek színét hány biten ábrázoljuk. 8-bites színmélység esetén 256 különbözõ szín megjelenítésére van lehetõségünk. 24-bites színmélység esetén egy pixel színét 24 bittel írjuk le, mégpedig úgy, hogy mindhárom színkomponenst intenzitását 8 biten
ábrázoljuk. A teljesítmény növelése érdekében egyes videokártyák más szín módokkal is rendelkezhetnek, pl. a 32 bites, vagy true color szín móddal A 32 bites szín módban ugyan nem tudunk több színt kikeverni, mint a 24 bites szín módban, de teljesítmény szempontjából a 32 bites szín mód van elõnyben, mivel itt gyorsabb a memóriahozzáférés, viszont van 8 elvesztegetett bit. Homogén koordináták Az OpenGL parancsok általában 2 és 3 dimenziós vertexekkel dolgoznak. Az OpenGL minden vertexet olyan 3 dimenziós vertexként tárol, melynek 4 koordinátája van. Minden (x, y, z, w) oszlop vektor egy homogén vertexet reprezentál, ha a vektorban legalább az egyik komponens nem nulla. Ha az a valós szám nem nulla, akkor (x, y, z, w) és (a*x, ay, az, aw) ugyanazt a homogén vertexet reprezentálja. Egy (x, y, z) 3 dimenziós euklideszi pont az (x, y, z, 1.0) homogén vertexnek, egy (x, y) 2 dimenziós euklideszi pont pedig az (x, y, 0.0, 10)
homogén vertexnek felel meg Ha w nem nulla, akkor az (x, y, z, w) homogén vertex az (x/w, y/w, z/w) 3 dimenziós pontnak, ha w = 0.0 akkor pedig egy végtelen távoli ideális pontnak felel meg. A végtelen távoli pontok megértéséhez tekintsük az (1, 2, 0, 0) pontot, és az (1, 2, 0, 1), (1, 2, 0, 0.01), (1, 2, 0, 00001) pontokat; ezek a pontok az (1, 2), (100, 200), (10000, 20000) euklideszi pontoknak felelnek meg. Ez a sorozat a 2x = y egyenes mentén távolodik a végtelenbe. Most már látjuk, hogy az (1, 2, 0, 0) pont ezen egyenes irányában, végtelen messzi van. Homogén koordináták használata több okból is indokolt lehet a számítógépes grafikában. Használatukkal lehetõvé válik, hogy a grafikában alkalmazott transzformációkat (eltolás (translate), forgatás (rotate), nagyítás (scale), nyírás (sheering)), és ezen transzformációk kompozícióját mátrixszorzásokkal el lehet végezni. Fontos szerepet kapnak a homogén koordináták a vágási
feladat megoldásánál is. Normálok Egy felülethez tartozó normál vektor (vagy normális) egy, a felületre merõleges egységnyi hosszúságú vektor. Minden normális egy háromdimenziós vektor, tehát egy normálisnak x, y és z komponense van, és mivel minden normális egységnyi hosszúságú, ezért sqrt(x^2 + y^2 + z^2) = 1. Egy sík felület esetén, a merõleges irány a felület összes pontjára ugyanaz, de egy nem egyenletes felület esetén, a normális a felület minden pontján más és más lehet. Az OpenGL-ben minden polygonhoz és minden vertexhez tartozik normális. Ugyanazon polygon vertexei ugyanazzal a normálissal is rendelkezhetnek, de lehetnek különbözõ normálisaik is. Egy polygon normálisát az OpenGL általában a vertexeinek a normálisaiból számítja ki. Normálisokat azonban csak vertexekhez specifikálhatunk Egy objektum normál vektora a felszínének az irányát definiálja általában a fényforrásokhoz relatívan. A normálisokat
a számítógépes grafikában több helyen is alkalmazzák. Ahhoz, hogy egy sík egyenletét le tudjuk írni, szükségünk van a sík normálisára, vagyis egy, a síkra merõleges vektor koordinátáira. Ekkor a sík egyenlete a következõképpen néz ki: Ax + By + Cz + D = 0 Fontos szerepet kapnak a normálisok az árnyalási feladatnál, a látható felszínek, vonalak meghatározásakor, illetve a megvilágítási feladatnál is. (például polygon határú síklapokkal határolt test hátra nézõ lapjait a normálisok segítségével határozhatjuk meg.) Koordináta transzformációk Vertex transzformáció A vertexeket, normálisokat és textúra koordinátákat az OpenGL transzformálja, mielõtt azokat felhasználná egy kép megalkotásában. A vertex transzformációkat ( forgatás, eltolás, skálázás, nyírás ) 4×4-es mátrixként reprezentálhatjuk. Ha v egy homogén vertexet reprezentál, M pedig egy 4×4-es transzformációmátrix, akkor M*v a v vertex képe az M
transzformáció után. A vertex koordinátákat objektumkoordinátáknak nevezzük, tehát egy vertex objektum koordinátái azok a koordináták, amelyeket a glVertex parancsban megadunk. Az objektum koordinátákat a modell-nézet vagy ModelView mátrix transzformálja ún. szem vagy eye koordinátákká A szem koordinátákból a vetítési vagy Projection mátrix által lesznek az ún. clip koordináták. Ez a transzformáció egy ún viewing volume (látótér)-t definiál (amely párhuzamos vetítés esetén egy téglalap, perspektivikus vetítés esetén pedig egy csonkagúla), úgy, hogy az ezen kívül esõ objektumokból vágott objektumok lesznek, így azok a végsõ képen nem fognak látszani. Ezután egy ún homogén osztás (perspective division) következik, és a clip koordináták normalizált eszköz koordinátákká (normalized device coordinates) transzformálódnak. Ezután már csak egy nézeti (viewport) transzformáció szükséges, és létrejönnek az
ablak koordináták. A gyakorlatban ez így néz ki (jelen esetben a vertexek oszlopvektorral való reprezentálását használjuk, így az adott vertexet mindig balról szorozzuk a transzformációmátrixszal (ez egyébként dr. Kuba Attila jegyzetében is így van, ezért biztosan ismerõs lesz); vannak források, melyek a másik megoldást használják, miszerint sorvektorokat szoroznak jobbról a transzformációmátrixszal, ekkor a transzformációmátrix is némiképp módosul, reméljük ez nem okoz semmi zavart (de hogy ne kelljen feleslegesen gondolkozni, elárulom hogy egyszerûen transzponálni kell a mátrixot;)): Legyenek egy vertex objektum koordinátái (x0, y0, z0, w0) (ez tehát egy oszlopvektor, azaz képzeljünk a vektor kitevõjébe egy T betût), és legyen a modell-nézet mátrix M. ; ekkor a vertex szem koordinátáit a következõképpen kapjuk: (xe, ye, ze, we ) = M*(x0, y0, z0, w0), tehát egy vertex szem koordinátáit úgy kapjuk, hogy az objektumkoordináta
vektorát beszorozzuk balról a modell-nézet mátrixszal. Ugyanígy, ha P a vetítési mátrix, akkor a vertex clip koordinátái: (xc, yc, zc, wc) = P*(xe, ye, ze, we), tehát egy vertex vetítés koordinátáit úgy kapjuk, hogy a szem koordináta vektorát beszorozzuk balról a vetítési mátrixszal. A vertex normalizált eszköz koordinátáit pedig a következõképpen kapjuk: (xd, yd, zd) = (xc/wc, yc/wc, zc/wc), azaz a vetítés koordinátákat leosztjuk a wc homogén koordinátával. Normál transzformáció A normálisok a fényszámításokban (is) vesznek részt. A normálisokat nem úgy transzformáljuk, mint a vertexeket vagy a pozíció vektorokat. Matematikailag jobb, ha a normálisokat nem vektornak, hanem a vektorra merõleges síknak tekintjük. Ekkor a normálisokra vonatkozó transzformációs szabályok a merõleges síkok transzformáció szabályai. Egy homogén síkot egy (a, b, c, d) sorvektorral definiálhatunk, ahol az a, b, c, d komponensek legalább
egyike nem nulla. Ha q egy nemnulla valós szám, akkor (a, b, c, d) és (qa, qb, qc, qd) ugyanazt a síkot reprezentálják. Egy (x, y, z, w) pont pontosan akkor van az (a, b, c, d) síkon, ha ax+by+cz+dw = 0. ( ha w = 1, akkor ez egy euklideszi sík) Ahhoz, hogy egy euklideszi síkot reprezentáljunk, nem lehet az a, b, c ( a sík normálvektora: (a, b, c)) komponensek mindegyike 0. Ha mind 0, akkor (0, 0, 0, d) egy végtelen távoli síkot reprezentál, amely az összes végtelen távoli pontot tartalmazza. Ha p egy homogén sík, v pedig egy homogén vertex, akkor a "v a p síkon fekszik" megállapítás matematikailag a következõt jelenti: p*v = 0, ahol p*v egy szokásos mátrix(vektor) szorzás mûvelet. Ha M egy nemszinguláris vertex transzformáció, akkor p*v = 0 ekvivalens a pM ^(1)Mv = 0 egyenlettel, tehát Mv a pM^(-1) síkon fekszik. Ezért, p*M^(1) az M transzformáció utáni képe a síknak. Vetítések Ha a háromdimenziós objektumokat meg akarjuk
jeleníteni a monitor képernyõjén, akkor ezeket a háromdimenziós modelltérbõl egy kétdimenziós nézetre kell leképezni. Ezért a Számítógépes grafikában kiemelt jelentõségû transzformációk a vetítések. A vetítés dimenziócsökkentõ mûvelet, tehát olyan transzformáció, amely n-dimenziós objektumokat kisebb dimenziós terekbe visz át. A vetítés eredménye a vetület, ami egy térbeli síkon, a vetítési síkon képzõdik. A tárgy- és képpontokon átmenõ egyenest vetítõsugárnak nevezzük. Az egyes tárgypontok képe a vetítõsugár döféspontja a vetítési síkkal. Általában kétféle vetítési típus használatos: párhuzamos, illetve perspektív vetítés. Párhuzamos vetítés esetén a vetítõsugarak párhuzamosak, ilyenkor csupán a vetítési irány van definiálva; perspektív vetítés esetén a vetítõsugarak mindegyike áthalad egy vetítési középponton, a centrumponton (szempozíció). A perspektív vetítés az objektumok
realisztikus ábrázolását teszi lehetõvé, ilyenkor a távolabbi objektumok kisebbnek tûnnek, a vetítési síkkal nem párhuzamos egyenesek egy pont felé tartanak. Párhuzamos vetítés esetén az eredetileg párhuzamos egyenesek párhuzamosak maradnak. Ezt a fajta vetítést a mérnöki tervezésben alkalmazzák, mégpedig azért, mert tudunk olyan párhuzamos vetítést specifikálni, ahol mérhetõek a távolságok és a szögek. Perspektív vetítés esetén a látótér tulajdonképpen egy végtelen piramisként képzelhetõ el, amelynek csúcsa a nézõpont, vagy szem. Az OpenGL-ben azért, hogy a túl közeli és távoli koordinátákkal ne kelljen foglalkozni, a látóteret elülsõ és hátsó síkokkal (vágósíkok) korlátozhatjuk, így a különbözõ megjelenítési eljárásokban csak ezt a korlátozott térrészt vesszük figyelembe. Tehát az OpenGL-ben egy csonkagúla fogja a perspektív vetítés látóterét reprezentálni, amelyet az elülsõ és hátsó
vágósíkokkal adunk meg. Párhuzamos vetítés esetén a látótér egy végtelen hasáb, amelyet itt is vágósíkokkal korlátozhatunk, így az OpenGL-ben a párhuzamos vetítés látótere egy téglatest. Az OpenGL-ben a párhuzamos vetítés azon speciális típusát használhatjuk, ahol a vetítési sík merõleges a z-tengelyre, és a vetítési sík normálisa párhuzamos a vetítési iránnyal, azaz az ortogonális vetítést. Az OpenGL-ben mindkét fajta vetítést úgy specifikálhatjuk, hogy megadjuk a látótér nagyságát és alakját. Világítás/Lighting Az OpenGL a megjelenítés elõtt minden pixel színét kiszámítja, a kiszámolt színértékeket a képpufferben tárolja el. A felmerülõ számítások egy része attól függ, milyen megvilágítást alkalmazunk, és hogy az objektumok ezen megvilágítás fényét hogyan tükrözik illetve nyelik el. Az OpenGL-ben lehetõség van a fényforrások és objektumok tulajdonságainak manipulálására. Az
OpenGL világítási modellben a fényt több, egyenként ki-, illetve bekapcsolható fényforrás határozza meg. A fényforrásoknak csak akkor van hatása, ha vannak olyan felületek, melyek a fényt visszatükrözik, vagy elnyelik. Egy anyagnak lehet saját fénye is, a bejövõ fényt szétszórhatja minden irányban, és tükrözheti a bejövõ fény egy részét egy bizonyos irányba akár egy tükör vagy más tükrözõdõ felszín. Az OpenGL világítási modellben a fénynek három független komponense van: ambient, diffúz és spekuláris. Szórt háttérvilágítás (ambient light) Ebben a modellben az objektumok egyenletesen, minden irányból kapnak fényt. Hatása a nappali fényviszonyoknak felel meg erõsen felhõs égbolt esetén. A számítógépes grafikában azért van rá szükség, hogy a felhasználó az ábrázolt jelenet összes objektumának a megvilágítását szabályozhassa. Ebben a modellben nincs fényforrás, az objektumok "saját"
fényüket bocsájtják ki. Ez megfelel annak, hogy a jelenetet egy irányfüggetlen, szórt fény világítja meg. Diffúz fényvisszaverõdés (diffuse light) A diffúz fényvisszaverõdés a matt felületek jellemzõje. Ekkor a megvilágított felület minden irányban ugyanannyi fényt ver vissza. Fényvisszaverõdés fényes és csillógó felületekrõl (specular light) A sima felületekre általában az a jellemzõ, hogy rajtuk fényes foltokat (specular highlight) is látunk, melyek helye nézõpontunkkal együtt változik. Ezek a felületek bizonyos irányokban visszatükrözik a fényforrásokat. Ekkor a matt felületekre jellemzõ diffúz és a tökéletesen (ideálisan) tükrözõ felületekre jellemzõ visszaverõdés közti átmeneti esetet kell modelleznünk. Anyag színek Az OpenGL aszerint a megközelítés szerint dolgozik, miszerint egy anyag színe a bejövõ fényben szereplõ vörös, zöld és kék fény arányától függ. Például egy tökéletesen piros
labda minden bejövõ piros fényt visszatükröz, és minden zöld és kék fényt elnyel, amely eltalálja õt. Ha a labdát fehér fényben nézzük (a fényt egyenlõ vörös, zöld és kék komponensekbõl kikeverve), akkor egy piros labdát látunk; ha azonban tiszta zöld fénnyel világítjuk meg a labdát, az feketének tûnik (a zöld szín teljesen elnyelõdik, és nincs visszatükrözõdõ fény). Akárcsak a fénynek, az anyagoknak is különbözõ ambient, diffúz és spekuláris színei vannak, amelyek meghatározzák az anyag ambient, diffúz és spekuláris tükrözõdését. Egy anyag ambient tükrözõdése összefüggésben van a bejövõ fény ambient komponensével, a diffúz tükrözõdése a bejövõ fény diffúz komponensével, . Az ambient és diffúz tükrözõdések határozzák meg az anyag színét, ezek többnyire hasonlóak vagy megegyezõek. A spekuláris tükrözõdés általában fehér vagy szürke. Az ambient, diffúz és spekuláris színek
mellett, az anyagoknak emisszív színei is vannak, amelyek a világító objektumok fényét szimulálják, azaz egy anyag emisszív színe az anyag saját fényének a színe. Az OpenGL megvilágítási modellben egy anyag emisszív színe intenzitást ad az objektumnak, de nincsenek rá hatással a fényforrások, és a világító objektum sincs hatással a többi objektumra. A fények és anyagok RGB értékei A specifikált fények szín komponensei némiképp különböznek az anyagokétól. Egy fény esetén, az értékek a teljes intenzitás százalékos kifejezései minden színre. Ha a fény színének mind az R, G és B komponense 1.0, akkor a fény a legfehérebb fehér Ha az értékek mindegyike 0.5, a fény még mindig fehér, de csak fél intenzitással, így az szürkének tûnik. Az anyagok számára, az értékek a színek tükrözõdési arányait specifikálják. pl ha R = 10, G = 05 és B = 00, akkor az anyag minden bejövõ vörös fényt és a zöld fény
50%-át veri vissza, viszont minden kék fényt elnyel. Más szavakkal, ha egy fényforrás szín komponensei (LR,LG,LB) egy anyag megfelelõ fény komponensei pedig (MR,MG,MB), akkor minden más tükrözõdési effekttõl eltekintve a szemünkbe érkezõ fény: (LR*MR,LGMG,LBMB). Megvilágított képek készítése A következõ lépések kellenek ahhoz, hogy a képünkhöz világítást adjunk: 1. minden objektum minden vertexéhez specifikálnunk kell normál vektort. Ezen normál vektorok határozzák meg az objektum irányát a fényforráshoz viszonyítva 2. egy vagy több fényforrás létrehozása, kiválasztása és pozicionálása 3. egy megvilágítási modell létrehozása és kiválasztása 4. az objektumok anyagi tulajdonságainak definiálása Az OpenGL renderelési fázisai Az OpenGL-ben az adott parancsok egy feldolgozási pipeline-on (csõvezeték) haladnak keresztül. A pipeline tartalmazza a megjelenítési listát (display list), a vertexfeldolgozót
(per-vertex operations), pixelfeldolgozót (pixel operations), raszterizálót (rasterization), fragmensfeldolgozót (per-fragment operations) és a képpuffert (framebuffer). Minden fázis egy adott mûvelet elvégzésére képes Az OpenGL parancsok mindig a hívás sorrendjében kerülnek feldolgozásra, bár elõfordulhat a parancs végrehajtása elõtt egy bizonytalan idejû várakozás. A parancsok elsõ lépésben a megjelenítési listába kerülnek, amely egy FIFO rendszerû tároló, amely a parancsokat a feldolgozásukig tárolja. Innen a parancsok két irányba mehetnek tovább A geometriai primitívekre vonatkozó parancsok a vertexfeldolgozóba kerülnek. Itt történnek a geometriai transzformációk, amelyek kiszámítják, hogy adott irányból nézve hol van a vertex képe. Itt történik még az egyenese és a sokszögek vágása is, melynek során az OpenGL levágja azokat a részeket, amelyek kiesnek a látótérbõl. A fázis utolsó mûvelete a vetítés. Azok a
parancsok, amelyek a raszterképekre és bittérképekre vonatkoznak, a pixelfeldolgozóba kerülnek. Ezután az adatok mindkét egységbõl a raszterizáló egységbe kerülnek, amely a primitíveket fragmensekre bontja. A fragmensek pixelek, és hozzájuk tartozó adatok, melyek a képpufferben a primitívek képét adják. Egy pont egyetlen fragmensbõl áll, mely tartalmazza a pont képének koordinátáit, színét és távolságát. A szakaszok annyi fragmensbõl állnak, ahány pixel elõállítja a szakasz képét a képpufferben. A sokszögek a szakaszokhoz hasonlóan több fragmensbõl állnak. A primitíveknél csak a vertexek távolsága, színe, fényessége és egyéb adatai ismertek; a fragmensekre bontás során a fragmensekre vonatkozó adatokat minden fragmensre meg kell határozni, amely elég bonyolult feladat. A végsõ fázisban az elkészült fragmensekbõl a fragmensfeldolgozó hozza létre a képpufferben a képet, de elõtte minden fragmens tesztelésre
kerül, amikor is eldõl, hogy az adott fragmens közelebb vagy távolabb vane annál a fragmensnél, ami a képpufferben ugyanott van. A képpuffer csak akkor frissítõdik, ha az új fragmens közelebb van a megfigyelõhöz, mint az eredeti fragmens. Az OpenGL programok fordításával kapcsolatos információk Általános információk Az OpenGL-nek jelenleg C/C++, ADA, Fortran és Visual Basic implementációja létezik. Ezeken az oldalakon a C/C++ implementációról lesz szó. Az C/C++ programozási nyelven írt OpenGL programokat Windows 9x/NT operációs rendszeren Borland C-vel és Visual C-vel lehet lefordítani. A kettõ közül a Visual C használatát javaslom, nekem az egyszerûbbnek tûnt. A Windows NT 3.5-ös verziójának megjelenése óta az OpenGL része az operációs rendszernek, ezért valószínûleg az OpenGL-é a jövõ (az más dolog, hogy a legtöbb 3D-s játékot még mindig egy másik grafikus rendszerben, a Direct3D-ben írják; viszont egy fontos érv az
OpenGl mellett, hogy platformfüggetlen). A Windows 98 tehát már tartalmazza az OpenGL programok futtatásához szükséges fájlokat. Mint már megjegyeztem, az OpenGL nem tartalmaz az ablakok létrehozását elõsegítõ függvényeket, ezért egy OpenGL program számára szükséges ablakot nekünk kell létrehozni az adott nyelven, amely hiába a Visual C, egyáltalán nem olyan egyszerû. Röviden arról van szó, hogy írni kell egy Windows programot, amelynek annyi a feladata, hogy az OpenGL használatához szükséges minimum követelményeket beállítsa, azaz nyisson egy ablakot, amelybe majd rajzolni lehet. A GDI-nek szüksége van egy Device Context (DC) kontextusra, az OpenGL-nek egy Rendering Context (RC) kontextusra. Az alapvetõ lépések tehát: • Az ablak aktuális pixel formátumának beállítása • Az RC létrehozása • A létrehozott RC aktiválása, azaz a DC összekötése az RC-vel A részletes leírásra nem térek ki, hiszen ez maga több oldalt tenne
ki; akit érdekel, írjon egy e-mailt, és akkor megadom a teljes leírást. A részletezés helyett egy olyan OpenGL kibõvítésre szeretnék kitérni, amely tartalmazza az ablakozási technikákat is, így elõsegíti ablakozó rendszertõl független OpenGL programok írását. Ez a GLUT Ahhoz, hogy egy GLUT programot le tudjunk fordítani Visual C fordítóval, néhány dolgot be kell állítani. Elõszor is be kell másolni a gluth header file-t a Vc98IncludeGl könyvtárba (az OpenGL header file-okat már tartalmazza a VC). Majd ugyanígy be kell másolni a glutlib, glut32lib file-okat a Vc98Lib könyvtárba, a glut.dll, glut32dll pedig a WindowsSystem könvtárba ( ha egy GLUT program exe-jét futtatni akarjuk, a WindowsSystem könyvtárnak tartalmaznia kell a glut32.dll fájlt). Ezek után egy GLUT program lefordítása: 1. variáció : Win32 konzol alkalmazás létrehozása Konzol alkalmazás Egy Win32 konzol alkalmazás egy olyan program, amely egy text módú ablakban
fut. Ez nagyon hasonlít arra, mikor egy DOS programot futtatunk Windows alatt, azzal a kivétellel, hogy ez egy tiszta 32 bites alkalmazás, és elérhetõ a Win32 API-n belül. A konzol programok létrehozhatnak GUI ablakokat az output számára, ahová az OpenGl rajzol. Egy ilyen alkalmazás létrehozása a következõképpen néz ki: 1. hozzunk létre egy új projektet Menjünk a Files menüre, válasszuk ki a New. menüpontot Válasszuk a Projects fület, és azon belül a Win32 Console application-t. Írjuk be a projekt nevét, legyen ez a név: first, majd OK. 2. a megjelenõ ablakban válasszuk az A simple application pontot 3. miután a projekt létrejött, egészítsük ki az stdafxh header file-t a következõ sorral: #include <GLglut.h> 4. menjünk a Projects/Settings menüpontra, majd válasszuk ki a Link fület. Az Objects/Library modules alatti sorba írjuk be a következõket: opengl32.lib glu32lib glut32lib Ezzel hozzáadtuk a projekthez a linkelendõ
könyvtárakat. 5. a source files/firstcpp file-t kiválasztva a következõ kódnak kell megjelennie: #include "stdafx.h" int main(int argc, char *argv[]) { return 0; } készen vagyunk, ide kell beírni a kódot, majd a Build/Build first.exe menüponttal tudjuk a programunkat lefordítani. 2. variáció: Egyszerû Win32 alkalmazás létrehozása (konzol ablak nélkül) Egyszerû Win32 alkalmazás Az OpenGl-lel kapcsolatos könyvek az elõzõ pontban leírt fordítási módszert javasolják, én mégis szeretnék megmutatni egy másikat, ami annyiban más, hogy nem hoz létre egy külön konzol ablakot. 1. hozzunk létre egy új projektet Menjünk a Files menüre, válasszuk ki a New. menüpontot Válasszuk a Projects fület,és azon belül a Win32 application-t. Írjuk be a projekt nevét, legyen ez a név: first, majd OK. 2. a megjelenõ ablakban válasszuk az A simple Win32 application pontot. 3. miután a projekt létrejött, egészítsük ki az stdafxh header file-t
a következõ sorral: #include <GLglut.h> 4. menjünk a Projects/Settings menüpontra, majd válasszuk ki a Link fület. Az Objects/Library modules alatti sorba írjuk be a következõket: opengl32.lib glu32lib glut32lib Ezzel hozzáadtuk a projekthez a linkelendõ könyvtárakat. 5. a source files/firstcpp file-t kiválasztva a következõ kódnak kell megjelennie: #include "stdafx.h" int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. return 0; } Tulajdonképpen készen is vagyunk. Ezután a WinMain függvény lesz a main függvény, ide kell beírni az ablak jellemzõit beállító parancsokat. Ha ezt megtettük, akkor létrejön egy ablak, amelybe már tudunk rajzolni. Ezek voltak a szükséges teendõk, részletesebb leírás a GLUT oldalon található. GLUT A Graphics Library Utility Toolkit(GLUT) A GLUT az OpenGL kibõvítése, amely már tartalmazza az OpenGL ablakok létrehozásához
szükséges eljárásokat, így néhány sor megírásával létre tudunk hozni egy OpenGL renderelésre alkalmas ablakot. A GLUT emellet saját esemény kezelõ rendszerrel is rendelkezik, sõt olyan rutinokat is tartalmaz, amelyekkel karaktereket és magasabb szintû geometriai objektumokat, mint például gömböket, kúpokat, ikozaédereket tudunk specifikálni. A GLUT úgy egyszerûsíti le az eseménykezelést, hogy callback rutinokat rendelhetünk bizonyos eseményekhez, mint például egy billentyû, vagy a bal egérgomb lenyomása. Ezután egy main loop-ba (esemény hurok) lépünk, majd ha egy esemény történik a hurokban, akkor az ezen eseményhez rendelt callback rutinok végrehajtódnak. A GLUT ablak és képernyõ koordináták pixelekben vannak kifejezve. A képernyõ vagy ablak bal felsõ koordinátája (0, 0). Az x koordináta jobbra haladva nõ, az y koordináta pedig lefelé; ez nem egyezik meg az OpenGL koordináta rendszerével, ahol általában a bal alsó
koordinátája (0, 0), de megegyezik a legelterjedtebb ablakozó rendszerek koordináta rendszerével. A GLUT-ban az egész azonosítók 1-gyel kezdõdnek, nem pedig 0-val. pl: az ablak és a menü azonosítók, illetve a menü item indexek. A GLUT header file-ok a következõ include direktívával építhetõk be a forráskódba: #include<GLglut.h> Egy ablak inicializálása és létrehozása Egy ablak létrehozásához specifikálni kell annak tulajdonságait. Ehhez a következõ rutinokat kell alkalmazni: void glutInit(int argc, char *argv); a glutInit eljárást minden más GLUT eljárás elõtt kell meghívni, mert ez inicializálja a GLUT library-t. A glutInit paraméterei megegyeznek a main függvény paramétereivel. megj.: a glutInit függvénynek nem kötelezõ szerepelni az ablak jellemzõit beállító függvények között, de ha szerepel, akkor az összes többi elõtt kell lennie; void glutInitDisplayMode(unsigned int mode); a glutInitDisplayMode a képernyõ
módot specifikálja (egyszeresen vagy kétszeresen pufferelt ablak, RGBA vagy szín index mód,.) pl.: a glutInitDisplayMode(GLUT SINGLE | GLUT RGB) egy egyszeresen pufferelt, RGB módban lévõ ablakot specifikál; megj.: a glutInitDisplayMode-nak meglehetõsen sok lehetséges paramétere van (GLUT DOUBLE, GLUT INDEX, GLUT STEREO, .), de egyszerûbb programok írásához nekünk ezek közül csak néhányra lesz szükségünk. Majd késõbb látni fogjuk, hogy pl kétszeresen pufferelt ablak (GLUT DOUBLE) a jó minõségû animációnál lesz elengedhetetlen. void glutInitWindowSize(int width, int height); az ablak méreteit specifikálja pixelekben. width: szélesség, height: magasság. pl.: a glutInitWindowSize(400, 400) egy 400x400-as ablakot specifikál void glutInitWindowPosition(int x, int y); az ablak bal felsõ sarkának x és y pozíciója. pl.: a glutInitWindowPosition(100, 100) az ablak bal felsõ koordinátái a pontban lesznek int glutCreateWindow(char *name);
megnyit egy ablakot az elõzõ rutinokban specifikált jellemzõkkel. Ha az ablakozó rendszer lehetõvé teszi, akkor a name megjelenik az ablak fejlécén. A visszatérési érték egy egész, amely az ablak azonosítója. Ezt az értéket használhatjuk fel az ablak kontrollálására pl.: a glutCreateWindow("single") egy single névvel ellátott ablakot specifikál. Ablakkezelés és input események Miután az ablakot létrehoztuk, de még mielõtt belépünk az esemény hurokba, kijelölhetjük a callback függvényeket a következõ rutinokkal: void glutDisplayFunc(void(*func)(void)); azt a függvényt specifikálja, amelyet akkor kell meghívni, ha az ablak tartalmát újra akarjuk rajzoltatni. megj.: leegyszerûsítve a dolgot: a func nevû eljárásban kell definiálni azokat a dolgokat, amiket meg szeretnénk jeleníteni. Lásd a példát void glutReshapeFunc(void(*func)(int width, int height)); azt a függvényt specifikálja, amelyet akkor kell meghívni, ha az
ablak mérete vagy pozíciója megváltozik. A func argumentum egy függvényre mutat, amelynek két paramétere van, az ablak új szélessége és magassága. Ha a glutReshapeFunc függvényt nem hívjuk meg vagy NULL az argumentuma, akkor egy alapértelmezett függvény hívódik meg, amely meghívja a glViewport(0, 0, width, height) függvényt. void glutKeyboardFunc(void(*func)(unsigned char key, int x, int y); a függvényt specifikálja, melyet egy billentyû lenyomásakor kell meghívni. key egy ASCII karakter Az x és y paraméterek az egér pozícióját jelzik a billentyû lenyomásakor (ablak relatív koordinátákban). megj.: tehát ha írunk pl egy void keyboard(unsigned char key, int x, int y) { switch(key) { case 27: exit(0); break; default: break; } } függvényt, majd ezt a függvényt átadjuk paraméterként a glutKeyboardFunc eljárásnak a következõképpen: glutKeyboardFunc(keyboard); akkor a programunkból az esc billentyû lenyomásakor léphetünk ki. void
glutMouseFunc(void(*func)(int button, int state, int x, int y); a függvényt specifikálja, amely egy egér gomb lenyomásakor illetve elengedésekor hívódik meg. A button callback paraméter a GLUT LEFT BUTTON, GLUT MIDDLE BUTTON illetve a GLUT RIGHT BUTTON egyike. A state callback paraméter a GLUT UP és a GLUT DOWN szimbolikus konstansok egyike. Az x és y callback paraméterek az egér pozícióját jelzik az egér esemény megtörténtekor (ablak relatív koordinátákban). void glutPostRedisplay(void); az érvényes ablak frissítését eredményezi. megj.: a glutPostRedisplay eljárásra többnyire az animációkészítésnél lesz szükségünk, ugyanis ezzel az eljárással tudjuk az ablakot periodikusan frissíteni. Példa GLUT programra Hozzunk létre egy 200 pixel széles, és 200 pixel magas ablakot a (100, 100) pozícióban. Az ablak neve legyen firstglut Az ablak legyen egyszeresen pufferelt, és RGB szín módú. Rajzoljunk fekete háttérbe egy kék szakaszt
(0.2, 02) pontból a (08, 08) pontba A programból esc-re tudjunk kilépni. #include<GLglut.h> #include<stdlib.h> /*#include "stdafx.h"*/ void init(void) { glClearColor(0.0, 00, 00, 00); // a háttér legyen fekete glMatrixMode(GL PROJECTION); // beállítjuk a vetítési mátrixot glLoadIdentity(); // betöltjük az egységmátrixot glOrtho(0.0, 10, 00, 10, -10, 10); // vágósíkok } void display(void) { glClear(GL COLOR BUFFER BIT); // a képernyõ törlése glColor3f(0.0, 00, 10); // az érvényes szín kék glBegin(GL LINES) { glVertex2d(0.2, 02); glVertex2d(0.8, 08); // az egyenes specifikálása glEnd(); glFlush(); } void keyboard(unsigned char key, int x, int y) { switch(key) { case 27: exit(0); break; } } int main(void) { /* int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) */ glutInitDisplayMode(GLUT SINGLE | GLUT RGB); // egyszeresen pufferelt, RGB szín módú ablak glutInitWindowSize(200, 200); // az
ablak mérete glutInitWindowPosition(100, 100); // az ablak pozíciója glutCreateWindow("firstglut"); // az ablak neve init(); glutDisplayFunc(display); // a képernyõ kezelése glutKeyboardFunc(keyboard); // a billentyûzet kezelése glutMainLoop(); // belépés az esemény hurokba return 0; } /* ha Visual C-ben akarjuk a kódot lefordítani, akkor így fog kinézni a program*/ OpenGL parancsok szintaktikája Egy OpenGL parancs eljárás vagy függvény lehet. Minden OpenGL parancs a gl prefixszel kezdõdik. Egy parancsnak általában több változata is lehet, amelyek az argumentumok átadásában különböznek. Egy OpenGL parancs egy névbõl áll, amelyet maximum 4 karakter követ. Az elsõ karakter az argumentumok számát jelöl; a második karakter vagy karakterpár az argumentumok típusát jelzi: 8 bites egész, 16 bites egész, 32 bites egész, egyszeres pontosságú lebegõpontos, vagy duplapontosságú lebegõpontos szám. Az utolsó karakter, ha van, akkor v,
jelezve azt, hogy az argumentum egy vektorra mutató pointer. Ezek alapján egy OpenGL parancs általános alakja: visszatérési értéktípus Név{#1234}{#b s i f d ub us ui}{#v}([args,] T arg1, . T argN [,args]); ( a # az üres karaktert jelenti ) pl.: a void glVertex3f ( float x, float y, float z ); parancs egy vertexet specifikál az (x, y, z) koordinátákban. Ha a parancs nevének utolsó karaktere v, akkor csak az arg1 van jelen, és az egy N db, adott típusú értéket tartalmazó vektorra mutató pointer; pl.: void glVertex2sv ( short v[2] ); Az OpenGL adattípusai Az OpenGL a jobb hordozhatóság érdekében saját adattípusokkal rendelkezik. Ezek az adattípusok általában megfelelnek a standard C adattípusoknak. Az alábbi táblázat bemutatja az OpenGL adattípusokat és a hozzájuk tartozó C adattípusokat (természetesen használhatjuk a standard C/C++ adattípusokat, többnyire ezen honlap példáiban is ezeket használom). OpenGL adattípus C típus C
szuffix GLbyte GLshort GLint, GLsizei GLfloat, GLclampf GLdouble, GLclampd GLubyte, GLboolean GLushort GLuint, GLenum, GLbitfield Belsõ reprezentáció 8-bites egész 16-bites egész 32-bites egész 32-bites lebegõpontos 64-bites lebegõpontos 8-bites elõjeletlen egész 16-bites elõjeletlen egész 32-bites elõjeletlen egész signed char short long float double unsigned char unsigned short unsigned long b s l f d ub us ui Vertex specifikáció • • • • • A glVertex parancs A glTexCoord parancs A glNormal parancs A glColor parancs A glIndex parancs A vertexek (csúcspontok) az OpenGL alapelemei. Vertexek lehetnek pontok, szakaszok végpontjai, illetve polygonok csúcsai. Minden geometriai primitívet vertexek rendezett sorozataként tudunk specifikálni. Az OpenGL minden vertexet homogén vertexként tárol, így azokat 4 lebegõpontos számmal reprezentálja, amely a vertex négy koordinátája. A negyedik, w koordináta általában imlicit 1.0 Ha a vertexek
koordinátáit kiegészítjük ezzel a plusz koordinátával, akkor lehetõvé válik, hogy minden lineáris transzformációt (eltolás, forgatás, skálázás) mátrixszorzásokkal végezzünk el. Mivel a mátrixszorzás asszociatív, így egyetlen mátrixszorzással akár bonyolult transzformációkat is el tudunk végezni. Mivel minden vertexhez( és az OpenGL-ben csak vertexekhez) színt, koordinátákat, normált és textúra-koordinátákat rendelhetünk, ezért a vertexeket struktúraként is fel lehet fogni. A glVertex parancs Vertexeket úgy specifikálhatunk, ha megadjuk a koordinátáit 2, 3 vagy 4 dimenzióban. Ez a specifikáció a void glVertex{234}{sifd}( T coords ); void glVertex{234}{sifd}v( T coords ); parancsokkal lehetséges. Bármely glVertex parancs négy koordinátát specifikál: az x, y, z és w koordinátákat. A glVertex2 parancs például beállítja az x és y koordinátákat, a z koordinátát 0-ra, a w koordinátát pedig 1-re állítja. A glVertex4 egy
ideális pontot specifikál a projektív térben pl.: a glVertex2f(05, 05) parancs egy 2-dimenziós vertexet specifikál a (0.5, 05) koordinátákban a glVertex3i(10, 10, 10) parancs egy 3-dimenziós vertexet specifikál a (10, 10, 10) koordinátákban egy vertex specifikálása ( glVertex3d(50,50,0)) A glTexCoord parancs A void glTexCoord{1234}{sifd}( T coords ); void glTexCoord{1234}{sifd}v ( T coords ); a homogén textúra koordinátákat állítja be, névszerint az s, t, r és q koordinátákat. A glTexCoord1 parancs az s koordinátát állítja be a meghívott értékre, a t és r koordinátákat 0-ra, q-t pedig 1-re állítja. A glTexCoord4 mind a négy koordinátát beállítja. Mint a neve is mutatja a glTexCoord parancs a textúra koordinátákat állítja be. A glNormal parancs Az érvényes normálist a void glNormal3{bsifd}( T coords ); void glNormal3{bsifd}v ( T coords ); parancsokkal specifikálhatjuk. Az érvényes normálisok a fényszámításokban, az
árnyékolásban, illetve a látható felszín meghatározásában vesznek részt. Mivel minden normális egy 3 dimenziós vektor, ezért a glNormal parancsnak csak egy változata van. pl.: a glNormal3f(10f, 00f, 00f) egy normális specifikációja A glColor parancs Az OpenGL-ben kétféle szín mód használatára van lehetõség. Az egyik az RGB, vagy RGBA szín mód, a másik az indexelt szín mód. Az elsõben minden színt 3 vagy 4 komponens határoz meg. R: vörös, G: zöld, B: kék, A: alpha komponens. Az indexelt szín módban minden színt egy lebegõpontos érték ír le, és minden ilyen értékhez meg van határozva a memóriában három 8 bites komponens. Az RGBA színeket a void glColor{34}{bsifd ubusui}( T components ); void glColor{34}{bsifd ubusui}v( T components ); eljárásokkal állíthatjuk be. A glColor4 az RGBA szín mind a négy komponensét beállítja; a glColor3 az R, G és B komponenseket specifikálja, az A pedig implicit 0.0 A glColor eljárás
lebegõpontos értékeket vár 0.0 és 10 között, ezen kívül esõ értékeket nem fogad el 00 a minimum, 1.0 pedig a maximum értéknek felel meg pl.: a glColor3d(10, 00, 00) a tiszta piros színt specifikálja a glColor3d(0.5, 05, 05) a szürke egy árnyalata A glIndex parancs Az érvényes szín indexet a void glIndex{sifd ub}( T index ); void glIndex{sifd ub}v ( T index ); eljárásokkal állíthatjuk be. A szín index határok gépfüggõek; az eljárás a határon belül esõ értékeket fogadja el, azon kívül esõ értékeket nem. pl.: glIndexd(05); Begin/End objektumok • • • • pontok szakasz sztripek szakasz hurkok független szakaszok • • • • • • sokszögek háromszög sztripek háromszög fanok független háromszögek négyszög sztripek független négyszögek A legtöbb geometriai objektumot glBegin/glEnd párok közt specifikáljuk. A specifikációba beletartozik a vertex, textúra és szín koordináták megadása. A parancsok: void
glBegin( enum mode ); void glEnd( void ); A glBegin parancsnak a következõ argumentumai lehetnek: POINTS, LINE STRIP, LINE LOOP, LINES, POLYGON, TRIANGLE STRIP, TRIANGLE FAN, TRIANGLES, QUAD STRIP, QUADS, attól függõen, hogy milyen geometriai objektumot specifikálunk. POINTS(pontok): független pontok specifikálása. pl.: glBegin(GL LINES); glColor3d(1.0,00,00); glVertex3d(0.5,05,00); glEnd(); egy piros pontot rajzol a (0.5,05,00) koordinátákba LINE STRIP (szakasz sorozat): egy vagy több összekötött szakasz specifikálása a végpontok sorozatának megadásával. Az elsõ vertex specifikálja az elsõ szakasz kezdõpontját, a második vertex az elsõ szakasz végpontját, és a második szakasz kezdõpontját, . Egy szakasz sztrip három vertexszel pl.: az ábrán látható szakasz sorozat specifikálása: glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINE LOOP (szakasz hurkok): ugyanaz, mint a LINE STRIP, de az
utolsóként specifikált vertexet összekötjük az elsõként specifikált vertexszel. Egy szakasz hurok három vertexszel pl.: az elõzõ példa, azzal a különbséggel, hogy az elsõ pont össze van kötve az utolsóval glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINES (független szakaszok): az elsõként specifikált két vertex határozza meg az elsõ szakaszt, a második két vertex a második szakaszt,. Ebben az esetben, ha páratlan számú vertexet specifikálunk a glBegin/glEnd pár között, akkor az utolsóként specifikált vertexet az OpenGL nem veszi figyelembe. POLYGON (sokszögek): polygonokat úgy specifikálhatunk, ha specifikáljuk a határvonalát szakaszok sorozataként, ugyanúgy, mint a LINE LOOP-nál. Figyelem: az OpenGL csak konvex sokszögek helyes kirajzolását garantálja (konkáv sokszögeket tudunk pl. úgy rajzolni, hogy felbontjuk konvex sokszögekre). TRIANGLE STRIP (háromszög sorozat):
háromszögek sorozata közös oldalakkal. Ebben az esetben az elsõ három vertex specifikálja az elsõ háromszöget, minden további vertex egy további háromszöget specifikál úgy, hogy a másik két vertex az elõzõ háromszögbõl származik. Ezt a mûveletet a Gl úgy végzi, hogy mindig eltárol két vertexet, az A és B vertexet, és egy bit mutatót, amely jelzi, hogy melyik eltárolt vertex helyettesítõdik az új vertexszel. A glBegin(TRIANGLE STRIP) hívás után a mutató az A vertexre mutat, minden további vertex átkapcsolja a mutatót, így az elsõ vertex A vertexként, a második vertex B vertexként, a harmadik A vertexként, . van tárolva Minden vertex, a harmadiktól kezdve, egy háromszöget specifikál az A és B vertexszel. 1. ábra háromszög sztrip TRIANGLE FAN (háromszög legyezõ): majdnem ugyanaz, mint a háromszög sorozat, azzal a kivétellel, hogy a két vertex közül az A vertex mindig az elsõként specifikált vertex, az összes
többi pedig helyettesíti a B vertexet. 2. ábra TRIANGLE FAN TRIANGLES (független háromszögek): Minden i-re, a 3i+1-ik, 3i+2-ik illetve 3i+3-ik vertex határoz meg egy háromszöget, ahol i = 0,1,2,. 3. ábra független háromszögek QUAD STRIP (négyszög sorozat): párhuzamos oldalakkal rendelkezõ téglalapokat hoz létre. Ha a specifikált vertexek: v1, v2, , vm, ahol vj a jdikként specifikált vertex, akkor az i-dik négyszög vertexei(sorban) v2i, v2i+1, v2i+2, v2i+3, ahol i = 0,1,., [m/2] QUADS (független négyszögek):minden négy vertexbõl álló csoport egy független négyszöget specifikál. A 4j+1-ik, 4j+2-ik, 4j+3-ik és 4j+4-ik vertex specifikál egy négyszöget, minden j = 0, 1, 2, .-re pl.: a glBegin(GL TRIANGLES); glVertex3d(0.0, 00, 00); glVertex3d(0.0, 08, 00); glVertex3d(0.7, 04, 00); glEnd(); egy olyan háromszöget specifikál, mely csúcsai a (0.0, 00, 00), (00, 08, 0.0) és (07, 04, 00) pontokban vannak Begin/End objektumok • • • •
• • • • • • pontok szakasz sztripek szakasz hurkok független szakaszok sokszögek háromszög sztripek háromszög fanok független háromszögek négyszög sztripek független négyszögek A legtöbb geometriai objektumot glBegin/glEnd párok közt specifikáljuk. A specifikációba beletartozik a vertex, textúra és szín koordináták megadása. A parancsok: void glBegin( enum mode ); void glEnd( void ); A glBegin parancsnak a következõ argumentumai lehetnek: POINTS, LINE STRIP, LINE LOOP, LINES, POLYGON, TRIANGLE STRIP, TRIANGLE FAN, TRIANGLES, QUAD STRIP, QUADS, attól függõen, hogy milyen geometriai objektumot specifikálunk. POINTS(pontok): független pontok specifikálása. pl.: glBegin(GL LINES); glColor3d(1.0,00,00); glVertex3d(0.5,05,00); glEnd(); egy piros pontot rajzol a (0.5,05,00) koordinátákba LINE STRIP (szakasz sorozat): egy vagy több összekötött szakasz specifikálása a végpontok sorozatának megadásával. Az elsõ vertex
specifikálja az elsõ szakasz kezdõpontját, a második vertex az elsõ szakasz végpontját, és a második szakasz kezdõpontját, . Egy szakasz sztrip három vertexszel pl.: az ábrán látható szakasz sorozat specifikálása: glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINE LOOP (szakasz hurkok): ugyanaz, mint a LINE STRIP, de az utolsóként specifikált vertexet összekötjük az elsõként specifikált vertexszel. Egy szakasz hurok három vertexszel pl.: az elõzõ példa, azzal a különbséggel, hogy az elsõ pont össze van kötve az utolsóval glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINES (független szakaszok): az elsõként specifikált két vertex határozza meg az elsõ szakaszt, a második két vertex a második szakaszt,. Ebben az esetben, ha páratlan számú vertexet specifikálunk a glBegin/glEnd pár között, akkor az utolsóként specifikált vertexet
az OpenGL nem veszi figyelembe. POLYGON (sokszögek): polygonokat úgy specifikálhatunk, ha specifikáljuk a határvonalát szakaszok sorozataként, ugyanúgy, mint a LINE LOOP-nál. Figyelem: az OpenGL csak konvex sokszögek helyes kirajzolását garantálja (konkáv sokszögeket tudunk pl. úgy rajzolni, hogy felbontjuk konvex sokszögekre). TRIANGLE STRIP (háromszög sorozat): háromszögek sorozata közös oldalakkal. Ebben az esetben az elsõ három vertex specifikálja az elsõ háromszöget, minden további vertex egy további háromszöget specifikál úgy, hogy a másik két vertex az elõzõ háromszögbõl származik. Ezt a mûveletet a Gl úgy végzi, hogy mindig eltárol két vertexet, az A és B vertexet, és egy bit mutatót, amely jelzi, hogy melyik eltárolt vertex helyettesítõdik az új vertexszel. A glBegin(TRIANGLE STRIP) hívás után a mutató az A vertexre mutat, minden további vertex átkapcsolja a mutatót, így az elsõ vertex A vertexként, a
második vertex B vertexként, a harmadik A vertexként, . van tárolva Minden vertex, a harmadiktól kezdve, egy háromszöget specifikál az A és B vertexszel. 1. ábra háromszög sztrip TRIANGLE FAN (háromszög legyezõ): majdnem ugyanaz, mint a háromszög sorozat, azzal a kivétellel, hogy a két vertex közül az A vertex mindig az elsõként specifikált vertex, az összes többi pedig helyettesíti a B vertexet. 2. ábra TRIANGLE FAN TRIANGLES (független háromszögek): Minden i-re, a 3i+1-ik, 3i+2-ik illetve 3i+3-ik vertex határoz meg egy háromszöget, ahol i = 0,1,2,. 3. ábra független háromszögek QUAD STRIP (négyszög sorozat): párhuzamos oldalakkal rendelkezõ téglalapokat hoz létre. Ha a specifikált vertexek: v1, v2, , vm, ahol vj a jdikként specifikált vertex, akkor az i-dik négyszög vertexei(sorban) v2i, v2i+1, v2i+2, v2i+3, ahol i = 0,1,., [m/2] QUADS (független négyszögek):minden négy vertexbõl álló csoport egy független
négyszöget specifikál. A 4j+1-ik, 4j+2-ik, 4j+3-ik és 4j+4-ik vertex specifikál egy négyszöget, minden j = 0, 1, 2, .-re pl.: a glBegin(GL TRIANGLES); glVertex3d(0.0, 00, 00); glVertex3d(0.0, 08, 00); glVertex3d(0.7, 04, 00); glEnd(); egy olyan háromszöget specifikál, mely csúcsai a (0.0, 00, 00), (00, 08, 0.0) és (07, 04, 00) pontokban vannak Begin/End objektumok • • • • • • • • • • pontok szakasz sztripek szakasz hurkok független szakaszok sokszögek háromszög sztripek háromszög fanok független háromszögek négyszög sztripek független négyszögek A legtöbb geometriai objektumot glBegin/glEnd párok közt specifikáljuk. A specifikációba beletartozik a vertex, textúra és szín koordináták megadása. A parancsok: void glBegin( enum mode ); void glEnd( void ); A glBegin parancsnak a következõ argumentumai lehetnek: POINTS, LINE STRIP, LINE LOOP, LINES, POLYGON, TRIANGLE STRIP, TRIANGLE FAN, TRIANGLES, QUAD STRIP, QUADS, attól
függõen, hogy milyen geometriai objektumot specifikálunk. POINTS(pontok): független pontok specifikálása. pl.: glBegin(GL LINES); glColor3d(1.0,00,00); glVertex3d(0.5,05,00); glEnd(); egy piros pontot rajzol a (0.5,05,00) koordinátákba LINE STRIP (szakasz sorozat): egy vagy több összekötött szakasz specifikálása a végpontok sorozatának megadásával. Az elsõ vertex specifikálja az elsõ szakasz kezdõpontját, a második vertex az elsõ szakasz végpontját, és a második szakasz kezdõpontját, . Egy szakasz sztrip három vertexszel pl.: az ábrán látható szakasz sorozat specifikálása: glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINE LOOP (szakasz hurkok): ugyanaz, mint a LINE STRIP, de az utolsóként specifikált vertexet összekötjük az elsõként specifikált vertexszel. Egy szakasz hurok három vertexszel pl.: az elõzõ példa, azzal a különbséggel, hogy az elsõ pont össze van kötve az
utolsóval glBegin(GL LINE STRIP); glVertex3d(0,0,0); glVertex3d(50,50,0); glVertex3d(50,100,0); glEnd(); LINES (független szakaszok): az elsõként specifikált két vertex határozza meg az elsõ szakaszt, a második két vertex a második szakaszt,. Ebben az esetben, ha páratlan számú vertexet specifikálunk a glBegin/glEnd pár között, akkor az utolsóként specifikált vertexet az OpenGL nem veszi figyelembe. POLYGON (sokszögek): polygonokat úgy specifikálhatunk, ha specifikáljuk a határvonalát szakaszok sorozataként, ugyanúgy, mint a LINE LOOP-nál. Figyelem: az OpenGL csak konvex sokszögek helyes kirajzolását garantálja (konkáv sokszögeket tudunk pl. úgy rajzolni, hogy felbontjuk konvex sokszögekre). TRIANGLE STRIP (háromszög sorozat): háromszögek sorozata közös oldalakkal. Ebben az esetben az elsõ három vertex specifikálja az elsõ háromszöget, minden további vertex egy további háromszöget specifikál úgy, hogy a másik két vertex az
elõzõ háromszögbõl származik. Ezt a mûveletet a Gl úgy végzi, hogy mindig eltárol két vertexet, az A és B vertexet, és egy bit mutatót, amely jelzi, hogy melyik eltárolt vertex helyettesítõdik az új vertexszel. A glBegin(TRIANGLE STRIP) hívás után a mutató az A vertexre mutat, minden további vertex átkapcsolja a mutatót, így az elsõ vertex A vertexként, a második vertex B vertexként, a harmadik A vertexként, . van tárolva Minden vertex, a harmadiktól kezdve, egy háromszöget specifikál az A és B vertexszel. 1. ábra háromszög sztrip TRIANGLE FAN (háromszög legyezõ): majdnem ugyanaz, mint a háromszög sorozat, azzal a kivétellel, hogy a két vertex közül az A vertex mindig az elsõként specifikált vertex, az összes többi pedig helyettesíti a B vertexet. 2. ábra TRIANGLE FAN TRIANGLES (független háromszögek): Minden i-re, a 3i+1-ik, 3i+2-ik illetve 3i+3-ik vertex határoz meg egy háromszöget, ahol i = 0,1,2,. 3. ábra
független háromszögek QUAD STRIP (négyszög sorozat): párhuzamos oldalakkal rendelkezõ téglalapokat hoz létre. Ha a specifikált vertexek: v1, v2, , vm, ahol vj a jdikként specifikált vertex, akkor az i-dik négyszög vertexei(sorban) v2i, v2i+1, v2i+2, v2i+3, ahol i = 0,1,., [m/2] QUADS (független négyszögek):minden négy vertexbõl álló csoport egy független négyszöget specifikál. A 4j+1-ik, 4j+2-ik, 4j+3-ik és 4j+4-ik vertex specifikál egy négyszöget, minden j = 0, 1, 2, .-re pl.: a glBegin(GL TRIANGLES); glVertex3d(0.0, 00, 00); glVertex3d(0.0, 08, 00); glVertex3d(0.7, 04, 00); glEnd(); egy olyan háromszöget specifikál, mely csúcsai a (0.0, 00, 00), (00, 08, 0.0) és (07, 04, 00) pontokban vannak A pontok, szakaszok és sokszögek tulajdonságai • • • Pontok Szakaszok Sokszögek Pontok Pont méret A pontok mérete a void glPointSize(float size); eljárással állítható be. A size specifikálja a pont méretét Az alapértelmezett érték 1.0
00, vagy annál kisebb érték a GL INVALID VALUE hibát generálja. Ha a pont mérete 10, akkor a pont pontosan egy pixelbõl fog állni. Ha a méret 20 akkor minden pontot egy 2×2-es négyzet reprezentál. Furcsának tûnhet, hogy miért lebegõpontos szám jelzi a pont méretét, ugyanis ha az 1.0-ás méret jelenti az egy pixel nagyságot, akkor hogyan tudunk pl. 25 pixelt rajzolni? Az OpenGL-ben azonban a pont méret nem azt jelenti, hogy a pont pontosan ilyen széles négyzet lesz, hanem azt, hogy a pontunk átlagos átmérõje a pont mérettel egyezik meg. Lehetõség van pont élsimítás (antialiasing) használatára is, amely letiltható, ill. engedélyezhetõ, ha a generikus glEnable ill glDisable parancsot a POINT SMOOTH szimbolikus konstanssal hívjuk meg. Alapértelmezés szerint a pont élsimítás le van tiltva. Ha az élsimítás le van tiltva, akkor a pixelek négyzet alakú régiója rajzolódik ki; pl.: ha a szélesség 1.0, akkor egy 1×1-es négyzet, ha 20,
akkor pedig egy 2×2-es négyzet rajzolódik ki. Ha az élsimítás engedélyezett, akkor a pixelek kör alakú régiója rajzolódik ki, úgy, hogy a széleken lévõ pixelek intenzitása kisebb, mint a középen lévõ pixeleké. pl.: glPointSize(5.0); Pontok színe Egy pont színét egyszerûen úgy tudjuk beállítani, hogy a pont specifikációja elõtt megadjuk a kívánt színt a glColor vagy a glIndex paranccsal. pl.: glBegin(GL POINTS); glColor3d(1.0,10,10); glVertex3d(0.0,10,00); glEnd(); egy fehér pontot specifikál a (0.0,10,00) koordinátákba Szakaszok Szakasz vastagság A szakaszok a szakasz sorozat (GL LINE STRIP), szakasz hurok (GL LINE LOOP) illetve független szakasz (GL LINES) Begin/End objektumokból származnak. A raszterizált szakasz szélességét a void glLineWidth(float width); paranccsal állíthatjuk be, úgy, hogy width-ben megadjuk a kívánt szélességet. Az alapértelmezett szélesség 10 Ha width <= 0, akkor ez GL INVALID VALUE hibát
eredményez. A szakaszok élsimítását a generikus glEnable ill. glDisable parancsokkal lehet szabályozni, ha azokat a GL LINE SMOOTH argumentummal hívjuk meg. Ha az élsimítás engedélyezett, akkor nem egész szélességek is megadhatók, és ekkor a szakasz szélén kirajzolt pixelek intenzitása kisebb, mint a szakasz közepén lévõ pixeleké. pl.: glLineWidth(3.0); Szakasz stílus A vonal stílust a void glLineStipple(int factor, ushort pattern); paranccsal állíthatjuk be. A pattern 16 bit hosszú sor csupa 1-esekbõl és 0kból Az 1-esek azt jelentik, hogy rajzolni kell a pixelt, a 0-ás pedig azt, hogy nem az alacsony helyiértékû bittel kezdõdõen. A pattern megnyújtható a factor használatával, amely minden csak 1-esekbõl és csak 0-kból álló részsorozatot megsokszoroz. pl: ha a pattern tartalmaz három 1-est egymás után, és a factor 2, akkor a három 1-es helyén hat 1-es lesz. A szakasz stílust engedélyezni ill. letiltani lehet a generikus glEnable
ill glDisable paranccsal, ha azt a GL LINE STIPPLE szimbolikus konstanssal hívjuk meg. példa vonal stílusra pl: glLineStipple(1, 0x3F07); glEnable(GL LINE STIPPLE); Ebben az esetben a minta: 0011111100000111, ami azt jelenti, hogy 3 pixelt kirajzolunk, utána 5 pixelt nem, 6 pixelt igen, majd 2 pixelt megint nem ( mielõtt nekiállnánk gondolkodni, emlékezzünk, hogy az alacsony helyiértékû bittel kezdünk ). glLineStipple(2, 0x3F07); glEnable(GL LINE STIPPLE); esetén a minta: 00001111111111110000000000111111. Szakasz színe Mivel egy szakaszt két vertex határoz meg, ezért a szakaszhoz tartozó pixelek színe is valamiképpen ezen két vertex színébõl származik. Mivel a két vertex színe különbözõ lehet, a szakasz színe az árnyékolási modelltõl függ. Smooth árnyékolási modellben a szakasz egyes pontjainak színe a két különbözõ színû vertex között átmenetet képez. Flat árnyékolási modellben a szakasz egyszínû lesz, mégpedig olyan
színû, amilyen az utolsóként specifikált vertex színe. Az árnyékolási modellt a void glShadeModel(GLenum mode); paranccsal állíthatjuk be. A mode a GL FLAT illetve GL SMOOTH valamelyike lehet. pl.: glBegin(GL LINES); glColor3d(1.0,00,00); glVertex3d(0.0,00,00); glColor3d(0.0,00,10); glVertex3d(1.0,10,00); glEnd(); Sokszögek Sokszögek színének beállítása A színeket (mint tudjuk) nem primitívekhez, hanem vertexekhez rendeljük hozzá, így a sokszögek színe is valamiképpen a vertexeinek színébõl származik. Az árnyalási modell aszerint dolgozik, hogy a sokszögek egyszínûek (ebben az esetben az adott sokszög színe megegyezik az utolsóként specifikált vertexének színével, kivételt képeznek ezalól a GL POLYGON-nal létrehozott primitívek: itt az elsõként specifikált vertex színe lesz a primitív színe), vagy a belsõ pontok színe a vertexek színébõl jön valamilyen interpolációval. Azt, hogy az OpenGL melyik módszert alkalmazza, a
glShadeModel paranccsal állíthatjuk be. A glShadeModel(GL FLAT); paranccsal érjük el, hogy a sokszögek egyazon színnel legyenek kitöltve, mégpedig a sokszög utolsóként specifikált vertexének a színével. A glShadeModel(GL SMOOTH); paranccsal azt érjük el, hogy a sokszög pontjainak színe a vertexeinek színébõl interpolációval elõállított szín. pl.: glBegin(GL TRIANGLES); glColor3d(1.0,00,00); glVertex3d(0.0,00,00); glColor3d(0.0,00,10); glVertex3d(1.0,00,00); glColor3d(0.0,00,10); glVertex3d(0.5,10,00); glEnd(); Látható felszínek, mélységbeli összehasonlítás A látható felszínek meghatározásának alapelve egyszerû: ha egy pixelt kirajzolunk, akkor hozzárendelünk egy z értéket, amely a pixel megfigyelõtõl való távolságát jelzi. Ezután, ha egy új pixelt akarunk rajzolni ugyanarra a helyre, akkor az új pixel z értéke összehasonlítódik az eredeti pixel z értékével. Ha az új pixel z értéke nagyobb, akkor közelebb van a
megfigyelõhöz, ezért az eredeti pixelt felülírjuk az új pixellel, egyébként marad az eredeti pixel. Ezt az eljárást a mélységi- vagy z-buffer segítségével végzi el az OpenGL. A mélységibeli összehasonlítást engedélyezhetjük illetve letilthatjuk a generikus glEnable parancs GL DEPTH TEST szimbolikus konstanssal való meghívásával. A sokszögek oldalainak megkülönböztetése; a glPolygonMode parancs Egy sokszögnek két oldala van - elülsõ és hátulsó oldal - és ezért különbözõképpen jelenhet meg a képernyõn, attól függõen, hogy melyik oldalát látjuk. Alapértelmezésben mindkét oldal ugyanúgy rajzolódik ki Ezen tulajdonságon a void glPolygonMode(enum face, enum mode); paranccsal lehet változtatni, amely kontrollálja a polygon elülsõ és hátulsó oldalának rajzolási módját. A face paraméter a GL FRONT AND BACK, GL FRONT ill. GL BACK; a mode paraméter pedig a GL POINT, GL LINE ill. GL FILL szimbolikus konstansok valamelyike
lehet, aszerint, hogy csak a poligon pontjai, határvonala legyen kirajzolva, vagy ki legyen töltve. Alapértelmezésben a poligon mindkét oldala kitöltve rajzolódik ki. (kérdés az, hogy melyik oldal az elülsõ oldal; nos az elülsõ oldal alapértelmezésben az, melynek vertexei az óramutató járásával ellentétes irányban vannak specifikálva). A sokszögek elülsõ és hátulsó oldalának definiálása; a glFrontFace parancs Felvetõdhet a kérdés, hogy miért fontos a sokszögek két oldalát megkülönböztetni. Sokszor lehet szükség arra, hogy egy sokszög elülsõ és hátulsó oldalának különbözõ fizikai tulajdonságai legyenek. A hátoldalt el lehet például rejteni, vagy eltérõ színt lehet hozzárendelni. Ha ellenkezõjére akarjuk változtatni az elülsõ és hátulsó oldalak meghatározását, akkor ezt a glFrontFace(GLenum mode); paranccsal tehetjük meg. A glFrontFace parancs tehát azt definiálja, hogy a sokszög melyik oldala lesz az elülsõ
ill. hátulsó oldal mode a GL CW és GL CCW szimbolikus konstansok valamelyike, ahol GL CW azt jelenti, hogy az elülsõ oldal az az oldal lesz, amelynek vertexeit az óramutató járásával megegyezõ irányban specifikáltunk, GL CCW pedig az ellenkezõje. Felszínek elrejtése a jobb teljesítmény érdekében; a glCullFace parancs Ha egy objektumot specifikálunk, akkor elõfordulhatnak olyan felszínek, melyek soha nem fognak látszani. Például egy kockát határoló négyzetek belsõ oldala soha nem látszik. Alapértelmezés szerint az OpenGL azonban minden oldalt kirajzol, tehát a határoló négyzetek belsõ oldalát is. Ha elkerülnénk a belsõ oldalak kirajzolását, sok idõt spórolnánk meg a kép kirajzolásakor. A sokszögek elülsõ vagy hátulsó oldalának figyelmen kívül hagyását culling-nak nevezzük. Tehát a glCullFace(GLenum mode); paranccsal specifikálhatjuk, hogy a sokszögek elülsõ vagy hátulsó oldalát figyelmen kívül hagyjuk a
rajzolásnál. A parancs a sokszög meghatározott oldalán letiltja a világítási, árnyalási és szín számítási mûveleteket. mode a GL FRONT vagy a GL BACK szimbolikus konstan valamelyike lehet. A culling-ot engedélyezhetjük illetve letilthatjuk a glEnable ill. glDisable paranccsal, ha azt a GL CULL FACE paraméterrel hívjuk meg. Kitöltési minta Alapértelmezés szerint a sokszögek teljesen kitöltöttek. A kitöltési mintát (amelyet egy 32×32-es bináris mátrix reprezentál) a void glPolygonStipple(const ubyte *mask); paranccsal lehet beállítani. A mask egy 32×32-es bittérképre mutat, amely 1-esekbõl és 0-sakból áll. 1 esetén a megfelelõ pixel kirajzolódik, 0 esetén nem. A sokszög minta engedélyezhetõ illetve letiltható a generikus glEnable ill. glDisable paranccsal, ha azt a GL POLYGON STIPPLE szimbolikus konstanssal hívjuk meg. példa kitöltési mintára pl.: glEnable(GL POLYGON STIPPLE); glPolygonStipple(minta); <-elõzõ oldal ->
következõ oldal <-- fõoldal Magasszintû objektumok rajzolása Bevezetés Kocka rajzolása Gömb rajzolása Kúp rajzolása Tórusz rajzolása • • • • • Bevezetés Az OpenGL alapból nem tartalmaz olyan eljárásokat, amelyek magasszintû geometriai objektumok rajzolását teszik lehetõvé. Ezeket az eljárásokat a GLUT tartalmazza. ( emlékeztetõül: a GLUT (Graphics Library Utility Toolkit) az OpenGL egy kibõvítése, amely tartalmazza az ablakok létrehozásához és kezeléséhez szükséges függvényeket, eljárásokat). A GLUT segítségével lehetõvé válik egyszerû parancsokkal kocka, gömb, kúp, stb. rajzolása Ahhoz, hogy ezeket a parancsokat használhassuk, szükségünk lesz a glut.h header file-ra #include<glut.h> megj.: ha az elõbbi include direktívát csatoltuk a programunkhoz, akkor az #include<gl.h> #include<glu.h> sorokra már nincs szükség, mert azokat tartalmazza a glut.h Kocka rajzolása Kockát a void
glutSolidCube(GLdouble size); void glutWireCube(GLdouble size); eljárásokkal tudunk rajzolni. size adja meg a kocka éleinek hosszát A glutSolidCube egy kitöltött oldalakkal rendelkezõ kockát rajzol, a glutWireCube pedig csak a kocka éleit rajzolja meg. A kocka középpontja az origóban lesz. pl.: a glutWireCube(1.0); egy 0.4 oldalhosszúságú, origó középpontú kockát rajzol Gömb rajzolása Gömb rajzolására a void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks); void glutWireSphere(GLdouble radius, GLint slices, GLint stacks); eljárásokkal van lehetõség. radius a gömb sugara, slices a z tengely körüli beosztások száma (mint a földrajzi hosszúság), stacks a z tengely menti beosztások száma (mint a földrajzi szélesség). A középpont itt is az origóban lesz. pl.: glutWireSphere(0.4,10,10); Kúp rajzolása Kúpot a void glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks); void glutWireCone(GLdouble base,
GLdouble height, GLint slices, GLint stacks); eljárásokkal tudunk rajzolni. base a kúp alapjának sugara, height a kúp magassága, slices adja meg a z tengely körüli beosztások számát, stacks pedig a z tengely menti beosztások számát jelenti. A kúp alapja z = 0-nál helyezkedik el, teteje pedig z = height-nél. pl.: glutWireCone(0.6,15 10,10); Tórusz rajzolása Tóruszt a void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); eljárásokkal tudunk rajzolni. innerRadius a tórusz belsõ sugara, outerRadius a tórusz külsõ sugara, nsides adja meg a radiális részek oldalainak számát, rings pedig a tórusz radiális beosztásainak számát. A tórusz középpontja koordináta-rendszer középpontjában lesz. pl.: void glutWireTorus(0.2,05,14,14); Az ablak törlése Egy számítógép képernyõjére rajzolás nem egyezik meg egy rajzlapra
rajzolással, ahol a rajzlap fehér, és csak egyetlen dolgunk van, mégpedig az, hogy rajzoljunk. A számítógép memóriája azt a képet tárolja, amelyet utoljára rajzoltunk, így egy új kép megrajzolása elõtt a képet törölnünk kell a háttérszínnel. A háttérszín, amellyel a hátteret töröljük, az alkalmazástól függ. Ezt a mûveletet elvégezhetnénk úgy is, hogy egy háttérszínû, megfelelõen nagy téglalapot rajzolunk a képernyõre. Azonban, mivel az OpenGL-ben lehetõség van a koordináta-rendszer, a szem pozíció és irány beállítására, így ezen téglalap méreteinek kiszámítása nem is olyan egyszerû. Ráadásul a legtöbb számítógép grafikus hardvere több pufferrel rendelkezik azon felül ami a megjelenítendõ kép pixeleinek színét tárolja, így kényelmes egy olyan paranccsal rendelkezni, amely bármelyiket tudja törölni törölni. A grafikus hardverben a pixelek színei ún. bitplane-ekként tárolódnak A tárolásnak két
módja van. Az egyik az, hogy a pixel színének mind a négy komponense eltárolódik (vörös, zöld, kék, alpha), vagy csak egy szín index érték tárolódik. Többnyire az elsõ módszert alkalmazzuk Az aktuális törlõ színt, amelyet az RGBA módban a szín pufferek törlésére alkalmazunk a void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); paranccsal állíthatjuk be. Az alapértelmezett törlõ szín a (0, 0, 0, 0) A pufferek tartalmát a void glClear(GLbitfield mask); paranccsal törölhetjük. A mask argumentum egy bitenkénti vagy kombinációja a GL COLOR BUFFER BIT, GL DEPTH BUFFER BIT, GL STENCIL BUFFER BIT és GL ACCUM BUFFER BIT szimbolikus konstansoknak. pl.: a glClearColor(10, 10, 10, 10); . . . glClear(GL COLOR BUFFER BIT); kódrészlet beállítja a törlõ színt fehérre, majd törli vele a szín puffert. a glClear(GL COLOR BUFFER BIT | GL DEPTH BUFFER BIT); parancs hatása megegyezik a glClear(GL COLOR BUFFER BIT);
glClear(GL DEPTH BUFFER BIT); parancsok hatásával; egy különbség van, az elsõ gyorsabb. Mátrixok • • • • • • A glMatrixMode, glLoadIdentity, glLoadMatrix és glMultMatrix parancs A nézeti transzformáció beállítása, a gluLookAt parancs Lineáris transzformációk A viewport definiálása Vetítési mátrixok Mátrix vermek Ez a fejezet olyan transzformációkról szó. amelyek elengedhetetlenek a számítógépes grafikában. Transzformációval lehet leírni például a 3D koordináták 2D koordinátákká végzõ dimenziócsökkentõ mûveletet, a vetítést, vagy projekciót. Lineáris transzformációk az eltolások, forgatások illetve nagyítások. Ezek a transzformációk tulajdonképpen nem is közvetlenül az objektumokat módosítják, hanem a koordináta-rendszert, amelyben az objektumok elhelyezkednek. Három fõ transzformáció van, amely részt vesz a specifikált vertexek képernyõre transzormálásában: nézeti (viewing), modellezési
(modelling) illetve vetítési (projection) transzformáció. A nézeti transzformáció a kamera vagy szem (eye) pozicionálását és irányának megadását jelenti. A modellezési transzformáció a modell vagy objektumok pozicionálását és orientálását foglalja magába. A nézeti transzformációnak meg kell elõznie a modellezési transzformációt az OpenGL kódban. Az OpenGL transzformációk áttekintése: Transzformáció Alkalmazás specifikálja a kamera helyzetét Modellezési objektumok megfelelõ transzformáció helyre mozgatása az elõzõ két Modell-nézet transzformációt transzformáció foglalja magába Vetítési vágás és látótérbe transzformáció méretezés Ablak-transzformáció az ablakba méretezés Nézeti transzformáció A glMatrixMode, glLoadIdentity, glLoadMatrix és glMultMatrix parancs Az OpenGL-ben mind a vetítési (projekció), mind a modell-nézet mátrix módosítható illetve beállítható. Az érvényes mátrix módot a void
glMatrixMode( enum mode ); eljárással lehet beállítani, ahol a mode a GL TEXTURE, GL MODELVIEW, GL COLOR illetve GL PROJECTION szimbolikus konstansok valamelyike lehet. A vetítési mátrix a szem koordináta rendszer (eye coordinate system) és a vetítés koordináta rendszer (clip coordinate system) közötti transzformációt írja le. Ha az érvényes mátrix mód a GL MODELVIEW, akkor a mátrix mûveletek a modell-nézet mátrixra vannak hatással (magyarul ezen mátrix manipulálásával tudunk geometriai transzformációkat (forgatás-glRotate, eltolás-glTranslate, nagyítás-glScale) végezni az objektumokon), ha GL PROJECTION, akkor a vetítési mátrixra (tehát így tudunk vetítést megadni). A két alapvetõ eljárás, amellyel az aktuális mátrixot inicializálhatjuk, a void glLoadMatrix{fd}( T m[16] ); void glMultMatrix{fd}( T m[16] ); A glLoadMatrix argumentuma egy 16 elemû vektor, amely egy 4×4-ex mátrixot reprezentál úgy, hogy 16 lebegõpontos számot
tárol oszlopfolytonosan(!!). A glLoadMatrix az érvényes mátrixot helyettesíti a megadott mátrixszal. A glMultMatrix ugyanígy mûködik, de nem helyettesíti az érvényes mátrixot, hanem beszorozza azt balról a megadott mátrixszal. Tehát, ha C az eredeti mátrix, M pedig a glMultMatrix argumentuma, akkor C = C*M, és C az új mátrix. A void glLoadIdentity(void); eljárás egyenértékû azzal, mint ha meghívnánk a glLoadMatrix-ot az egységmátrixszal. Tehát a glLoadIdentity() parancs meghívása után az érvényes mátrix az egységmátrix lesz. A gluLookAt parancs Az OpenGL alapból nem tartalmaz nézeti transzformációt beállító eljárásokat. A szem mindig a (0, 0, 0) koordinátákban van Ahhoz, hogy a kamera mozgását érzékeltessük, az objektumokat kell a kamera transzformáció inverzével elmozgatni. A GLU library azonban tartalmaz egy olyan parancsot, mellyel nézeti transzformációt végezhetünk. void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble
eyez, GLdouble centerx, GLdouble centery, Gldouble centerz, GLdouble upx, GLdouble upy, GLdouble upz); paranccsal a nézeti transzformációt állíthatjuk be.(eyex, eyey, eyez) specifikálják a szem pozícióját. (centerx, centery, centerz) egy referenciapontot specifikál, ahová a szem néz; (upx, upy, upz) egy felfelé mutató vektor (up-vektor,VUP). A gluLookAt eljárás kiszámítja a megadott kamera transzformáció inverzét, majd megszorozza a kapott inverz transzformációs mátrixszal az érvényes mátrixot, ezért arra ügyelni kell, hogy az érvényes mátrix mód a GL MODELVIEW legyen. pl.: gluLookAt(0.0,00,20,00,00,00,00,10,00); Lineáris transzformációk A lineáris transzformációk a következõ eljárásokkal specifikálhatók (az itt felsorolt parancsok a MODELVIEW mátrixot módosítják, tehát pl. egy glRotate parancs specifikál egy transzformációs mátrixot, amely forgatást végez, majd ezzel beszorozza az érvényes MODELVIEW mátrixot) A void
glRotate{fd}( T a, T x, T y, T z ); eljárásban az a adja meg, hogy hány fokkal forgatunk; a kiszámolt mátrix egy óramutató járásával ellentétes forgatást specifikál az (x, y, z)-vel megadott tengely körül. pl.: a glRotated(45, 10, 00, 00) egy 45 fokos forgatást specifikál az x tengely körül a glRotated(12.5, 00, 00, 10) egy 125 fokos forgatást specifikál a z tengely körül A void glTranslate{fd}( T x, T y, T z ); argumentumai az eltolás vektort adják meg: ( x, y, z ). pl.: a glTranslated( 50, 0, 0 ) az x-tengely mentén 50 egységgel való eltolást specifikál A void glScale{fd}( T x, T y, T z ); eljárás általános skálázást végez az x, y, z tengely mentén. pl.: a glScaled(05, 05, 05) egy 05-szörös uniform nagyítást végez A viewport definiálása A viewport az az ablakon belüli rész, ahová rajzolunk, tehát a viewport transzformációval adjuk meg a létrejövõ kép méretét. A viewport általában maga az ablak, de elképzelhetõ az is,
hogy a viewport az ablaknak csupán egy része. A következõ két ábra közül az egyik olyan viewportot ábrázol, amely az egész ablakot lefedi, a másiknál a viewport a létrehozott ablak bal alsó sarkánál helyezkedik el. Az aktuális viewportot a void glViewport(int x, int y, GLsizei width, GLsizei height); eljárással specifikálhatjuk. Az eljárás egy téglalapot definiál az OpenGL ablakba, ahová rajzolni szeretnénk. Az (x, y) paraméterek specifikálják a viewport bal alsó sarkát, width és height pedig a viewport téglalap méretét. Alapértelmezésben a paraméterek a (0, 0, winWidth, winHeight) értékeket veszik fel, ahol winWidth és winHeight az ablak méretei. pl.: glViewport(0, 0, w, h) esetén ha az ablak szélessége w, magassága pedig h, akkor a viewport az ablak lesz. Vetítési mátrixok Az OpenGL-ben párhuzamos (ortografikus) és perspektivikus vetítést is specifikálhatunk. Itt némi kiegészítésre van szükség a vetítésekkel
kapcsolatosan: a két alapvetõ vetítési fajtán belül még igen sokféle különbözõ vetítést meg lehet adni; perspektív vetítésnél a vetítéseket az elsõdleges távlatpontok száma szerint osztályozzuk, párhuzamos vetítés esetén pedig a vetítési irány és a vetítési sík egymáshoz való viszonya szerint. Jelen honlapon ha párhuzamos vetítésrõl van szó, akkor mindig olyan párhuzamos vetítésre gondolunk, ahol a vetítési sík merõleges a z tengelyre, és a vetítési sík normálisa párhuzamos a vetítési iránnyal (azaz az ortografikus vetítés egy speciális esete). A vetítéseket úgy adjuk meg, hogy megadjuk a látótér nagyságát és alakját. Ami a látótéren belülre esik, az fog a képernyõn látszani. A void glOrtho( double left, double right, double bottom, double top, double near, double far ); eljárással a párhuzamos vetítés látóterét specifikálhatjuk. left és right adják meg a baloldai és jobboldali függõleges
vágósíkok koordinátáit. bottom és top az alsó és felsõ vízszintes vágósíkok koordinátái. near és far adják meg a közeli és távoli vágósíkok távolságát a szemtõl. A (left, bottom, near) és a (right, top, -near) specifikálják tehát a közeli vágósík pontjait, amelyek ráfeszülnek az ablak bal alsó és jobb felsõ sarkaira ( feltételezve, hogy a szem a (0, 0, 0) pontban van). far adja meg a távoli vágósík távolságát a szemtõl. Tehát a közeli vágósík ablakba esõ részének bal alsó sarka: (left, bottom, -near), a jobb felsõ sarka: (right, top, -near). A távoli vágósík ablakba esõ részének bal alsó sarka: (left, bottom, far), jobb felsõ sarka: (right, top, far). Ha kétdimenziós objektumokat akarunk rajzolni, akkor a void gluOrtho2D(double left, double right, double bottom, double top); eljárással is specifikálhatjuk a vetítési mátrixot. Ekkor a vágási téglalap egyszerûen a (left, bottom, right, top)
koordinátákkal megadott téglalap. A glOrtho (gluOrtho2D) megszorozza az aktuális projekciómátrixot a specifikált mátrixszal és ez lesz az új projekciómátrix. Ebben az esetben a látótér egy téglalap, amelyre példa: 2. ábra pl.: a glOrtho(00, 10, 00, 10, -10, 10) egy párhuzamos vetítést specifikál, ahol a közeli vágósík(azon része, mely a látótérbe esik) bal alsó sarkának koordinátái (0.0, 00, -10) , jobb felsõ sarkának koordinátái (10, 1.0, -10); a távoli vágósík bal alsó sarkának koordinátái (00, 00, 10), jobb felsõ sarkának koordinátái (1.0, 10, 10) Azaz mind a közeli, mind a távoli vágósík 1.0 távolságra van a vetítési síktól A void glFrustum( double left, double right, double bottom, double top, double znear, double zfar ); eljárással a perspektív vetítés látóterét specifikálhatjuk. left és right adják meg a baloldali és jobboldali függõleges vágósíkok koordinátáit. bottom és top az alsó és
felsõ vízszintes vágósíkok koordinátái. znear és zfar adják meg a közeli és távoli vágósíkok távolságát a szemtõl. A (left, bottom,- znear) és (right, top,- znear) specifikálják tehát a közeli vágósík pontjait, amelyek ráfeszülnek az ablak bal alsó és jobb felsõ sarkaira. zfar adja meg a távoli vágósík távolságát a szemtõl. (itt is feltételezzük, hogy a szem a (0, 0, 0) pontban van). Lehetõség van a perspektív vetítési mátrix egy szemléletesebb specifikációjára is, mégpedig a void gluPerspective(double fovy, double aspect, double near, double far); eljárás segítségével, amely szimmetrikus látóteret specifikál. fovy adja meg a látótér szögét az x-z sík irányában, aspect a vágási téglalap szélességének és magasságának arányát, near és far pedig a vágósíkok távolságát. A glFrustum megszorozza az aktuális projekció mátrixot a specifikált mátrixszal és ez lesz az új projekció mátrix. Ennél a
vetítésnél a látótér egy csonkagúla, amely a következõképpen néz ki: 3.ábra 4.ábra pl.: a glFrustum(00, 10, 00, 10, 10, 30) egy perspektivikus vetítést specifikál, ahol a közeli vágósík (azon része, mely a látótérbe esik) bal alsó sarkának koordinátái (0.0, 00, -10), jobb felsõ sarkának koordinátái (10, 1.0, -10); a távoli vágósík bal alsó sarkának koordinátái (00, 00, -30), jobb felsõ sarkának koordinátái (1.0, 10,- 30) Azaz a közeli vágósík 10 távolságra, a távoli vágósík pedig 3.0 távolságra van a szemtõl Mátrix vermek Minden mátrix mód számára van egy mátrix verem. Az érvényes mátrix minden módban a verem tetején lévõ mátrix. A mûveletek: void glPushMatrix( void ); void glPopMatrix( void ); A glPushMatrix és glPopMatrix az érvényes mátrix módnak megfelelõ mátrixokkal dolgoznak. Kezdetben minden veremben egy mátrix van, mégpedig az egységmátrix. A kezdõ mátrix mód a MODELVIEW módA mátrix
veremmûveletekkel lehetõvé válik az egyes mátrixok elmentése, hogy késõbb, ha szükségünk van rájuk, akkor újra használni tudjuk. A mátrix veremmûveleteket többnyire akkor használjuk, amikor olyan objektumot akarunk pl. animálni, mely több egyszerûbb objektumból épül fel, és az egyes részeket eltérõ módon akarjuk mozgatni/transzformálni. Például, ha rajzolunk két kockát, és az egyiket forgatjuk a z-tengely mentén az óramutató járásával ellentétes irányba, a másikat pedig az óramutatóval megegyezõ irányba forgatjuk, akkor ezt úgy tudjuk megoldani, hogy a két kockára két különbozõ transzformációs mátrixunk van. Vágás A primitíveket kirajzolás elõtt be kell vágni a látótérbe, amelyet 6 sík határoz meg. Az OpenGL-ben lehetõség van még ezen kívüli vágósíkok specifikálására is a void glClipPlane( enum plane, const double *equation); eljárással. plane jelzi, hogy melyik vágósíkot állítjuk be. Értéke a
GL CLIP PLANEi valamelyike lehet, ahol i egy 0 és GL MAX CLIP PLANES-1 közötti érték. equation egy négy lebegõpontos értéket tartalmazó vektor címe. Ezen értékek egy sík egyenlet együtthatói objektum koordinátákban: p1, p2, p3, p4 (ebben a sorrendben). A glClipPlane parancs egy félteret specifikál egy négyegyütthatós síkegyenletet felhasználva. A glClipPlane meghívása után a sík-egyenlet transzformálódik a modelview mátrix inverzével, és az eredményként létrejövõ egyenlet fog tárolódni. A modellview mátrix további változtatásai nincsenek hatással az eltárolt sík-egyenlet együtthatóira. Ha egy vertex koordinátáit ezen sík-egyenletbe behelyettesítve pozitív vagy nulla értéket kapunk, akkor a vertex belül van, egyébként kívül. Az így definiált vetítési síkok engedélyezhetõk, illetve letilthatók a generikus glEnable, illetve glDisable utasítással. Mindkét utasítás argumentuma GL CLIP PLANEi, jelezve azt, hogy
melyik síkot akarjuk engedélyezni vagy letiltani. Világítás paraméterek (lighting parameters), anyagtulajdonságok (material properties), normálok • • • • • a megvilágítás modelljei fényforrások létrehozása, pozicionálása és engedélyezése a világítási modell kiválasztása anyagok tulajdonságai felületek normáljai A megvilágítás modelljei Megvilágítási modellekkel írjuk le a modelltér objektumainak és a fényforrásoknak a kapcsolatát. Az OpenGL csak az ún lokális megvilágítási modellekkel foglalkozik, ami azt jelenti, hogy az objektumok színe, világossága csak az objektumoktól, a fényforrásoktól és a nézõponttól függenek, más objektumoktól nem. Ezek a modellek a következõk: Szórt háttérvilágítás (ambient light) Ebben a modellben az objektumok egyenletesen, minden irányból kapnak fényt. Hatása a nappali fényviszonyoknak felel meg erõsen felhõs égbolt esetén. A számítógépes grafikában azért van rá
szükség, hogy a felhasználó az ábrázolt jelenet összes objektumának a megvilágítását szabályozhassa. Ebben a modellben nincs fényforrás, az objektumok "saját" fényüket bocsájtják ki. Ez megfelel annak, hogy a jelenetet egy irányfüggetlen, szórt fény világítja meg. Diffúz fényvisszaverõdés (diffuse light) A diffúz fényvisszaverõdés a matt felületek jellemzõje. Ekkor a megvilágított felület minden irányban ugyanannyi fényt ver vissza. Fényvisszaverõdés fényes és csillógó felületekrõl (specular light) A sima felületekre általában az a jellemzõ, hogy rajtuk fényes foltokat is látunk, melyek helye nézõpontunkkal együtt változik. Ezek a felületek bizonyos irányokban visszatükrözik a fényforrásokat. Ekkor a matt felületekre jellemzõ diffúz és a tökéletesen (ideálisan) tükrözõ felületekre jellemzõ visszaverõdés közti átmeneti esetet kell modelleznünk. Egy fényforrás létrehozása, beállítása
és engedélyezése Minden fényforrás három világítási komponensbõl tevõdik össze: ambient, diffúz és spekuláris. Akárcsak a színeket, a világítási komponenseket is az RGBA értékeivel definiálhatjuk úgy, hogy megadjuk a vörös, zöld és kék intenzitását. A specifikálható fényforrások számának maximuma implementációfüggõ, de legalább nyolc. Ezen fényforrásokat bárhová pozicionálhatjuk, ahová akarjuk, például egész közel az objektumokhoz, vagy végtelen messzire. Az elõbbi esetben pozicionális, az utóbbi esetben pedig direkcionális fényforrásról beszélünk; direkcionális fényforrás esetén a pozícióvektor negyedik koordinátája 0.0 Ezenkívül beállíthatjuk, hogy a fényforrás szûk, fókuszált vagy széles fénysugarat bocsásson ki. Emlékezzünk azonban, hogy minden fényforrásnak szerepe van a megvilágítási egyenletek kiszámításában, így a képalkotásban is. A fényforrásoknak számos tulajdonsága van:
szín, pozíció és irány. Ezen tulajdonságokat a glLight paranccsal állíthatjuk be; a glLight parancsnak három paramétere van: elsõ kijelöli, hogy melyik fényforrás paramétereit szeretnénk beállítani, a második a beállítandó tulajdonságot határozza meg, a harmadik pedig ezen tulajdonságnak az értéke. A void glLight{if}(enum light, enum pname, T param); void glLight{if}(enum light, enum pname, T *param); létrehozza a light-tal jelölt fényforrást, amely a GL LIGHT0, GL LIGHT1, ., GL LIGHT7 szimbolikus konstansok valamelyike lehet pname jelöli ki a beállítandó fényforrás jellemzõt. param jelöli az érték(ek)et amelyekre a pname beállítódik. Ha az eljárás vektoros verzióját használjuk, akkor ez egy vektor, amely értékek egy csoportjára mutat, illetve maga az érték, ha a nemvektoros verziót használjuk. A nemvektoros verziót csak akkor alkalmazhatjuk ha egyértékû jellemzõt állítunk be. A pname által jelölt paraméterek
alapértelmezett értéke Paraméter név Jelentés Alapértelmezett érték GL SPOT EXPONENT (0.0, 00, 0.0, 10) (1.0, 10, 1.0, 10) (1.0, 10, 1.0, 10) (0.0, 00, 1.0, 00) (0.0, 00, 10) 0.0 GL SPOT CUTOFF 180.0 GL AMBIENT GL DIFFUSE GL SPECULAR GL POSITION GL SPOT DIRECTION GL CONSTANT ATTENUATION 1.0 GL LINEAR ATTENUATION 0.0 GL QUADRATIC ATTENUATION 0.0 A fény ambient RGBA intenzitása A fény diffúz RGBA intenzitása A fény spekuláris RGBA intenzitása A fény (x, y, z, w) pozíciója A fény (x, y, z) iránya Reflektorfény exponens Reflektorfény sugárzásának kúpszöge Konstans enyhítõ faktor Lineáris enyhítõ faktor Négyzetes enyhítõ faktor A GL DIFFUSE és GL SPECULAR alapértelmezett értékei csak a GL LIGHT0-val jelzett fényforrásra érvényesek. A többi fényforrásnál az alapértelmezett érték (0.0, 00, 00, 10) mind a GL DIFFUSE-ra, mind a GL SPECULAR-ra. A tulajdonságokat úgy állíthatjuk be, hogy a megfelelõ értékeket vektorba tesszük,
és vele hívjuk meg a függvényt. pl.: float light ambient[] = { 0.0, 00, 00, 10 }; float light diffuse[] = { 1.0, 10, 10, 10 }; float light specular[] = { 1.0, 10, 10, 10 }; float light position[] = { 1.0, 10, 10, 00 }; . . . glLightfv(GL LIGHT0, GL AMBIENT, light ambient); glLightfv(GL LIGHT0, GL DIFFUSE, light diffuse); glLightfv(GL LIGHT0, GL SPECULAR, light specular); glLightfv(GL LIGHT0, GL POSITION, light position); Ezután még ne felejtsük el engedélyezni a fényforrást (glEnable(GL LIGHT0)). Részletesebben: Szín Az OpenGL fényforrásoknak három színkomponensekkel megadható paramétere van, a GL AMBIENT, GL DIFFUSE és a GL SPECULAR. A GL AMBIENT paraméter adja meg a fényben szereplõ ambient komponens RGBA intenzitását. A GL DIFFUSE paraméterrel a diffúz komponens intenzitását specifikálhatjuk, ez jelenti tulajdonképpen a fény színét. A GL SPECULAR paraméterrel a specular komponens intenzitását adhatjuk meg, ami gyakorlatilag az objektumokon
látható fényes folt (specular highlight) színét adja meg. Pozíció Mint már említettem, az OpenGL-ben kétféle fényforrást specifikálhatunk: pozicionális és direkcionális fényforrást. A pozicionális fényforrásoknak meghatározott pozíciója van a modelltérben, amely a MODELVIEW mátrixszal transzformálódik (a PROJECTION mátrix nincs hatással a fényforrások pozíciójára), és szem koordinátákban tárolódik el; ekkor a pozícióvektor w koordinátája 1.0 Direkcionális fényforrások esetén csak a fényforrás irányát adjuk meg, a fényforrás pozícióvektora ekkor is transzformálódik a MODELVIEW mátrixszal; ebben az esetben a pozícióvektor w koordinátája 0.0 Fényintenzitás gyengülés A valós világban a fényforrás távolságával a fény intenzitása csökken. Az OpenGL ezt az intenzitáscsökkenést egy gyengítõ faktor bevezetésével valósítja meg, amelyet a megvilágítási egyenletekben használ fel. A gyengítõ faktor: fatt =
1/(ek + el*||VP|| + en||VP||^2), ha w nem nulla. ek a konstans gyengítõ faktor(GL CONSTANT ATTENUATION), el a lineáris gyengítõ faktor(GL LINEAR ATTENUATION), en a négyzetes gyengítõ faktor(GL QUADRATIC ATTENUATION), ||VP|| pedig a vertex és a fényforrás távolsága ( a V vertex fényét szeretnénk meghatározni, ha a P fényforrás az egyedüli fényforrás). Direkcionális fényforrásoknál(w = 0.0) , hogy a fényintenzitás gyengülést ne érezzük, a gyengítõ faktorok le vannak tiltva, ekkor fatt = 1.0 A gyengítõ faktorok specifikációjánál figyelembe kell venni, hogy az ambient, diffuse és specular komponensek mindegyikét gyengíti a megadott faktor. Csak az emissziós (az objektumok saját színe) és globális ambient értékekre nincs hatással az gyengülés. Reflektorszerû fényforrások Alapértelmezésben egy létrehozott fényforrás minden irányban sugároz fényt, azonban lehetõség van reflektorszerû pozicionális fényforrások
specifikálására is. Ekkor a kibocsájtott fény kúp alakot vesz fel Ahhoz, hogy egy ilyen fényforrást létrehozzunk, meg kell adnunk ennek a kúpnak a szögét a GL SPOT CUTOFF paraméter beállításával. A kúpszög az ábrán látható módon van értelmezve: A kúpszög értelmezése Alapértelmezésben ez a kúpszög 180.0, ez pontosan azt jelenti, hogy a fényforrás minden irányban sugároz. A kúpszögön kívül meg kell határozni a reflektorfény irányát is (GL SPOT DIRECTION), amely a középvonalat határozza meg. A fénykúp intenzitásának eloszlását a reflektorfény exponensének (GL SPOT EXPONENT) beállításával specifikálhatjuk, amely alapértelmezésben 0.0 Az exponens segítségével megadhatjuk, hogy a reflektorfény a középvonalhoz közel koncentráltabb legyen, attól távolabb pedig egyre jobban enyhüljön az intenzitása. Az exponens növelésével egyre fókuszáltabb reflektorfényt kapunk. A világítási modell kiválasztása A
világítási modell paramétereit a következõ eljárással specifikálhatjuk: void glLightModel{if}(enum pname, T param); void glLightModel{if}v(enum pname, T *param); Az eljárás a világítási modell tulajdonságait állítja be. A tulajdonságot, amelyet be szeretnénk állítani, pname jelöli ki. param jelöli az értékeket, amelyre a pname tulajdonságot be akarjuk állítani. Ez egy vektor, amely értékekre mutat, vagy egy érték maga. Az eljárás nem vektoros verziója esetén a pname a következõ értékeket veheti fel: GL LIGHT MODEL LOCAL VIEWER: a param paraméter egy egész vagy lebegõpontos szám, amely azt adja meg, hogyan számítódjon ki a spekuláris fényvisszaverõdés szöge. Alapértelmezett értéke 00 GL LIGHT MODEL TWO SIDE: a param paraméter egy egész vagy lebegõpontos szám, amely megadja, hogy egy- vagy kétoldalas világítási számítások legyenek alkalmazva a sokszögeknél. Nincs hatással a pontok, szakaszok és bitmapek
megvilátítására. Ha params 0 (vagy 00), akkor egyoldalas világítás állítódik be, és csak az elülsõ anyag paraméterei kerülnek felhasználásra a világítási egyenleteknél. Máskülönben kétoldalas megvilágítás specifikálódik. Ebben az esetben a hátulsó sokszögek vertexei a hátulsó anyag paraméterei szerint világítódnak meg, és a normáljaik is módosulnak, mielõtt a világítási egyenlet kiértékelõdik. Alapértelmezett értéke 0.0 Az eljárás vektoros verziója esetén a pname értékei: GL LIGHT MODEL AMBIENT: a params paraméter egy vektor, amely négy egész vagy lebegõpontos értéket tárol. Ezek az értékek specifikálják a tér szórt háttérvilágításának RGBA intenzitását (globális fény). Az alapértelmezett érték: (0.2,02,02,10) GL LIGHT MODEL LOCAL VIEWER: mint az elõbb. GL LIGHT MODEL TWO SIDE: mint az elõbb. pl.: float ambientLight[] = { 0.4, 04, 04, 10 }; . . . glLightModelfv(GL LIGHT MODEL AMBIENT,
ambientLight); Anyagok tulajdonságai Nemcsak a fényforrások tulajdonságait, hanem az objektumok anyagjellemzõit is beállíthatjuk az OpenGL-ben. Azt már láttuk, hogyan kell egy objektum színét specifikálni. Egy objektum színe azt határozza meg, hogy a rá érkezõ fény mely komponensét milyen arányban nyeli el illetve veri vissza. Ha fényforrásokat is alkalmazunk, akkor ahelyett, hogy azt mondanánk, hogy egy sokszög piros, azt mondjuk, hogy a sokszög anyaga olyan, mely túlnyomórészt a vörös fényt veri vissza. Ekkor még mindig mondhatjuk azt, hogy a felület vörös, de ekkor specifikálnunk kell az anyag visszaverõdési tulajdonságait az ambient, diffúz és spekuláris fényforrások számára. Az anyagok egy másik tulajdonsága az emissziós érték, amely az anyagok saját fényét jelentik. Az anyag szín komponensei meghatározzák a visszavert fény hányadát, azaz azt, hogy az egyes komponensekbõl mennyi verõdik vissza. A glMaterial parancs Az
anyag jellemzõket a void glMaterial{if}(enum face, enum pname, T param); void glMaterial{if}v(enum face, enum pname, T params); eljárásokkal állíthatjuk be. face a GL FRONT, GL BACK, GL FRONT AND BACK szimbolikus konstansok valamelyike lehet, attól függõen, hogy az objektum elülsõ, hátulsó vagy mindkét oldalának anyag paramétereit specifikáljuk. pname a specifikálandó paraméter neve param vagy params az érték, vagy értékek, amelyekre a pname által jelzett paramétert be kell állítani. Paraméter név Jelentés GL AMBIENT GL DIFFUSE GL SPECULAR GL EMISSION GL SHININESS Alapértelmezett érték (0.2, 02, 0.2, 10) (0.8, 08, 0.8, 10) (0.0, 00, 0.0, 10) (0.0, 00, 0.0, 10) 0 GL AMBIENT AND DIFFUSE GL COLOR INDEXES (0, 1, 1) az anyag ambient RGBA tükrözõdése az anyag diffúz RGBA tükrözõdése az anyag spekuláris RGBA tükrözõdése az anyag emissziós fény intenzitása az anyag spekuláris exponense Az anyag ambient és diffúz színe együtt
Ambient, diffuse és specular szín indexek Részletesebben: Diffuse és ambient tükrözõdés A diffuse tükrözõdés játsza a legfontosabb szerepet abban, hogy egy objektumot milyen színûnek érzékelünk. Az érzékelt szín a bejövõ fény diffuse komponensének arányától és az objektum és a fényforrás szögétõl függ. A szempozíció nem játszik szerepet Az ambient tükrözõdésnek ott van szerepe, ahol az objektumot nem éri közvetlen fény. Az ambient tükrözõdésre sincs hatással a nézõpont helyzete. Mivel általában az objektumok diffuse és ambient tükrözõdése megegyezik, a kettõt általában egyszerre specifikáljuk. Specular tükrözõdés Az objektumok specular tükrözõdése fényes foltokat eredményez. A specular tükrözõdés a nézõponttól is függ: a tükrözõdés bizonyos pontokban élesebben jelentkezik. A specular tükrözõdési hatást a GL SPECULAR paraméterrel, a foltok (specular highlight) méretét és fényességét
pedig a GL SHININESS paraméterrel specifikálhatjuk (magasabb érték kisebb és fényesebb (jobban fókuszált) foltot eredményez). Emisszió A GL EMISSION paraméterrel specifikálhatjuk egy objektum saját fényét. Szín tracking: a glColorMaterial parancs Az anyag paramétereket úgy is specifikálhatjuk, hogy azok kövessék az objektumoknak azt a színét, amelyet a glColor parancsban megadtunk (color tracking). Ezt a void glColorMaterial(Glenum face, Glenum mode); paranccsal tehetjük meg. Face a GL FRONT, GL BACK, GL FRONT AND BACK szimbolikus konstansok valamelyike lehet, attól függõen, hogy az objektum elülsõ, hátulsó vagy mindkét oldala a glColor-ban megadott színt kövesse. Alapértelmezett értéke a GL FRONT AND BACK. Mode a GL EMISSION, GL AMBIENT, GL SPECULAR, GL DIFFUSE, GL AMBIENT AND DIFFUSE konstansok egyike, jelezve azt, hogy melyik anyag jellemzõt határozza meg az érvényes szín. Alapértelmezett érték a GL AMBIENT AND DIFFUSE. Felületek
normálisai Mint tudjuk, egy felület normálisa egy a felületre merõleges, egységnyi hosszúságú vektor. A normálisokat vertexekhez rendelhetjük hozzá Mivel a testeket a határoló felszínük határozza meg, és ezek a felszínek az OpenGL-ben sokszögekbõl (többnyire háromszögekbõl) épülnek fel, megkülönböztethetjük ezen sokszögek két oldalát, aszerint, hogy az adott oldal (face) a testbõl kifelé néz vagy nem. Az OpenGL-ben ha egy testet specifikálunk, akkor a határoló felületek normálisát úgy kell megadni, hogy azok a testbõl kifelé mutassanak. egy felület egy normálja(még nincs normalizálva) Az ábrán látható négyszög (1, 1, 0) koordinátájú vertexén átmenõ egyenes merõleges a négyszög síkjára. Ekkor, ha kiválasztjuk pl az (1, 10, 0) koordinátájú pontot az egyenesen, akkor az (1, 1, 0)-(1, 10, 0) szakasz egy normál vektor. A második pont egy irányt definiál, mégpedig egy felfelé mutató irányt. Ez jelzi, hogy melyik
a sokszög elülsõ oldala Ahelyett azonban, hogy két pontként specifikáljuk a normál vektort, kivonhatjuk a második pontból az elsõ pontot, így megkapunk egy koordináta hármast, amely a vertextõl való távolságot jelenti x, y és z irányban. A példánkban ez (0, 9, 0). Az OpenGL-ben azonban minden normált unit normállá kell konvertálni, ami egységnyi hosszúságú. Egy normálból úgy kaphatunk egység normált, hogy minden komponensét elosztjuk a hosszával. A példánkban tehát a (0, 9, 0) -ból (0, 1, 0) lesz, amely vektornak ugyanaz az iránya, csak a hossza egységnyi. pl.: glBegin(GL TRIANGLES); glNormal3f(0.0,00,10); glVertex3f(1.0,30,00); glVertex3f(2.0,30,00); glVertex3f(1.5,50,00); glEnd(); A példánkban a glNormal3f eljárással adtuk meg a háromszög normálvektorát. Itt mindhárom vertex normálisa ugyanaz A példában egyszerû a normálvektor meghatározása, hiszen a háromszög az xy síkon fekszik, így a normálisa egy olyan vektor mely
párhuzamos a z-tengellyel. De mi van akkor, ha olyan vertex vagy felület normálvektorát kell meghatározni, mely nem valamelyik fõ síkon fekszik? Legyen adott egy sokszög, mely tetszõleges három vertexe P1, P2 és P3. Legyen V1 a P1-bõl P2-be mutató vektor, V2 pedig a P1-bõl P3-ba mutató vektor. Két vektor egy síkot határoz meg a három-dimenziós térben, a sokszögünk ezen a síkon fekszik. Ekkor a V1 X V2 vektor merõleges a síkra Két vektor keresztszorzata Tehát egy normálvektor kiszámítása, ha adott három pont: Typedef struct { Glfloat x; Glfloat y; Glfloat z; } point3d; void normalizal(Glfloat *n) { float hossz; hossz = sqrt(n[0]*n[0] + n[1]n[1] + n[2]*n[2]); if(hossz == 0) hossz = 1; n[0] /= hossz; n[1] /= hossz; n[2] /= hossz; } void normalszamit(point3d p1, point3d p2, point3d p3, float *n) { point3d v1, v2; v1.x = p1x - p2x; v1.y = p1y - p2y; v1.z = p1z - p2z; v2.x = p2x - p3x; v2.y = p2y - p3y; v2.z = p2z - p3z; n[0] = v1.y * v2.z - v1z *
v2.y; n[1] = v1.z * v2.x - v1x * v2.z; n[2] = v1.x * v2.y - v1y * v2.x; normalizal(n); }