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

Alapértelmezett érték Jelentés

GL_AMBIENT | (0.0, 0.0, 0.0, 1.0) | A fény ambient RGBA intenzitása
GL_DIFFUSE | (1.0, 1.0, 1.0, 1.0) | A fény diffúz RGBA intenzitása
GL_SPECULAR | (1.0, 1.0, 1.0, 1.0) | A fény spekuláris RGBA intenzitása
GL_POSITION | (0.0, 0.0, 1.0, 0.0) | A fény (x, y, z, w) pozíciója
GL_SPOT_DIRECTION | (0.0, 0.0, -1.0) | A fény (x, y, z) iránya
GL_SPOT_EXPONENT | 0.0 | Reflektorfény exponens
GL_SPOT_CUTOFF | 180.0 | Reflektorfény sugárzásának kúpszöge
GL_CONSTANT_ATTENUATION | 1.0 | Konstans enyhítő faktor
GL_LINEAR_ATTENUATION | 0.0 | Lineáris enyhítő faktor
GL_QUADRATIC_ATTENUATION | 0.0 | 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, 0.0, 0.0, 1.0) 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, 0.0, 0.0, 1.0 };
float light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
float light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
float light_position[] = { 1.0, 1.0, 1.0, 0.0 };
.
.
.
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 0.0.
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 0.0), 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,0.2,0.2,1.0).
GL_LIGHT_MODEL_LOCAL_VIEWER: mint az előbb.
GL_LIGHT_MODEL_TWO_SIDE: mint az előbb.

pl.:

float ambientLight[] = { 0.4, 0.4, 0.4, 1.0 };
.
.
.
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

Alapértelmezett érték Jelentés

GL_AMBIENT | (0.2, 0.2, 0.2, 1.0) | az anyag ambient RGBA tükröződése
GL_DIFFUSE | (0.8, 0.8, 0.8, 1.0) | az anyag diffúz RGBA tükröződése
GL_SPECULAR | (0.0, 0.0, 0.0, 1.0) | az anyag spekuláris RGBA tükröződése
GL_EMISSION | (0.0, 0.0, 0.0, 1.0) | az anyag emissziós fény intenzitása
GL_SHININESS | 0 | az anyag spekuláris exponense
GL_AMBIENT_AND_DIFFUSE |  | Az anyag ambient és diffúz színe együtt
GL_COLOR_INDEXES | (0, 1, 1) | 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,0.0,1.0);
glVertex3f(1.0,3.0,0.0);
glVertex3f(2.0,3.0,0.0);
glVertex3f(1.5,5.0,0.0);
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(point

3d p1, point3d p2, point3d p3, float *n)

{

point3d v1, v2;

v1.x = p1.x - p2.x;

v1.y = p1.y - p2.y;

v1.z = p1.z - p2.z;

v2.x = p2.x - p3.x;

v2.y = p2.y - p3.y;

v2.z = p2.z - p3.z;

n[0] = v1.y * v2.z - v1.z * v2.y;

n[1] = v1.z * v2.x - v1.x * v2.z;

n[2] = v1.x * v2.y - v1.y * v2.x;

normalizal(n);

}

<-előző oldal
<--főoldal