Hlavní navigace

Grafická knihovna OpenGL (21): nastavení světelných zdrojů

25. 11. 2003
Doba čtení: 8 minut

Sdílet

Tímto dílem seriálu o grafické knihovně OpenGL ukončíme část věnovanou nastavení světelných zdrojů a materiálů zobrazovaných těles. Dnes si povíme detailní informace o nastavení reflektorového zdroje světla a změny pozice a orientace světelného zdroje pomocí transformační matice ModelView. V ilustračních příkladech si ukážeme použití reflektorového světla i použití transformačních matic při změně pozice světelných zdrojů.

Materiály, stínování – čtvrtá část

Reflektorové světlo

V minulém dílu jsme si uvedli tři typy světelných zdrojů, které lze v OpenGL použít. Pro připomenutí – jednalo se o bodový světelný zdroj, směrové světlo a reflektorové světlo. První dva typy světelných zdrojů jsme si už popsali, dnes nám zbývá vysvětlit pouze nejsložitější typ světelného zdroje, tj. reflektorové světlo. Reflektorové světlo napodobuje svými vlastnostmi klasický reflektor. Ukázka jednoduché scény, která sestává ze dvou těles (čajové konvičky a podložky) a je osvětlena reflektorovým světlem, je na prvním obrázku.

Ukázka scény nasvícené reflektorovým světlem
Obrázek 1: Ukázka scény nasvícené reflektorovým světlem

Reflektorové světlo je vlastně takovým hybridem mezi bodovým světlem a směrovým světlem, přičemž mu jsou dodány ještě další zajímavé vlastnosti. Podobně jako u bodového světla musíme u reflektoru zadávat jeho pozici v prostoru. Bodové světlo však do všech směrů září se stejnou intenzitou. U reflektoru ovšem můžeme (resp. dokonce musíme) zadat hlavní směr šíření světelných paprsků, v tomto se tedy reflektorové světlo zase podobá světlu směrovému. Navíc můžeme u reflektorového světla zadávat i takzvaný světelný kužel, ve kterém se mění intenzita světla od plně vysvícené (v hlavním směru šíření světelných paprsků) do úplného útlumu.

Vlastnosti reflektorového světla jsou ukázány na druhém obrázku; následuje vysvětlení jednotlivých popisek u tohoto obrázku:

  • GL_POSITION značí umístění reflektoru ve scéně. Význam je podobný jako u bodového světla, které jsme popisovali v minulé části.
  • GL_SPOT_DIRECTION je vektor značící orientaci a směr hlavního světelného paprsku. Hlavním světelným paprskem máme na mysli paprsek s největší světelnou intenzitou.
  • GL_SPOT_CUTOFF udává úhel světelného kužele, za nímž již reflektorový světelný zdroj nesvítí, jelikož intenzita světla klesla k nule. V oblasti mezi hlavním světelným paprskem a povrchem světelného kužele postupně klesá intenzita světelného zdroje z plné (uvnitř kužele) až k nulové (na povrchu kužele).

Parametry reflektorového světla
Obrázek 2: Parametry reflektorového světla

Pro zadání vlastností reflektorového zdroje světla v OpenGL můžeme použít několik funkcí:

void glLightf(
    GLenum light,
    GLenum pname,
    GLfloat para
);

void glLighti(
    GLenum light,
    GLenum pname,
    GLint param
);

void glLightfv(
  GLenum light,
  GLenum pname,
  const GLfloat *params
);

void glLightiv(
  GLenum light,
  GLenum pname,
  const GLint *params
);

Význam jednotlivých parametrů zůstává stejný jako u bodového a směrového světla, tj. v parametru light je číslo světelného zdroje, v parametru pname jméno vlastnosti a v parametru param resp. params hodnota resp. hodnoty měněné vlastnosti.

U reflektorového světla však lze zadávat mnohem více přidaných vlastností než u bodového a směrového světla:

  1. Pomocí vlastnosti GL_POSITION lze zadat pozici reflektorového světelného zdroje ve scéně. Hodnotou této vlastnosti je vektor, který je transformován pomocí aktuálně nastavené transformační matice ModelView. Na předchozím obrázku je pozice reflektoru naznačena bodem GL_POSITION. Implicitní hodnotou vlastnosti GL_POSITION je vektor (0.0, 0.0, 1.0, 0.0).
  2. Vlastností GL_SPOT_DIRECTION se mění směr hlavního světelného paprsku. Také tento směr je transformován pomocí transformační matice ModelView, takže je možné reflektorem otáčet. Směr hlavního světelného paprsku je zadán vektorem, který je ihned po zavolání funkce glLight*() normalizován. Implicitní hodnota této vlastnosti je vektor (0.0, 0.0, –1.0), reflektor tedy svítí proti směru osy z.
  3. Vlastnost GL_SPOT_EXPONENT nastavuje míru koncentrovanosti světla ve směru hlavního světelného paprsku. Čím větší je hodnota této vlastnosti, tím více je světelný tok soustředěn okolo své osy. Implicitní hodnota této vlastnosti je nastavena na nulu. Při výpočtu osvětlení se spočte vektor od světelného zdroje k povrchu zobrazovaného tělesa a tento vektor se normalizuje. Potom se vypočítá skalární součin mezi tímto vektorem a vektorem představujícím hlavní světelný paprsek. Výsledek tohoto skalárního součinu je umocněn na zde nastavenou hodnotu.
  4. Vlastnost GL_SPOT_CUTOFF určuje úhel, ve kterém se reflektorové světlo šíří. Tato hodnota může být nastavena v rozmezí od 0 stupňů do 90 stupňů, existuje ještě speciální hodnota 180 stupňů. Nulová hodnota značí úzký světelný paprsek, hodnota 90 stupňů naopak reflektor svítící do celého poloprostoru. Implicitní hodnota této vlastnosti je nastavena na speciální hodnotu 180.0, což znamená, že je reflektorové světlo vypnuto a jedná se o všesměrové světlo.
  5. Vlastností GL_CONSTANT_AT­TENUATION se nastavuje hodnota proměnné kc, která je použita při výpočtu útlumu světla (viz další text). Počáteční hodnota této proměnné je jedna.
  6. Vlastností GL_LINEAR_ATTE­NUATION se nastavuje hodnota proměnnékl, která bude taktéž popsána v dalším textu. Počáteční hodnota této proměnné je nula.
  7. Pomocí vlastnosti GL_QUADRATIC_AT­TENUATION se nastavuje hodnota proměnné kq, která bude opět popsána dále. Počáteční hodnota této proměnné je taktéž nulová.

Útlum světla

Pomocí funkcí OpenGL lze také simulovat další vlastnost reálných světel. Tou je útlum, tj. snižování intenzity světla se vzrůstající vzdáleností od světelného zdroje. Útlum je způsoben rozbíháním světelných paprsků, odrazy, lomy a pohlcováním světla vlivem pevných prachových částeček a páry, které se ve vzduchu nacházejí.

Problém útlumu a s ním související světelné efekty není uspokojivě vyřešen v žádné vykreslovací (renderovací) metodě včetně raytracingu a radiositní metody. V OpenGL lze pouze simulovat základní útlum světla. Pro výpočet útlumu je nejdříve nutné spočítat faktor útlumu (attenuation factor) z následujícího vztahu:

faktor_útlumu=1/(kc +kl d+kq d2)

V tomto vztahu vystupuje několik konstant a jedna proměnná. Konstanty kc, kl a kq se nastavují pomocí funkce glLight*() s vlastnostmi GL_CONSTANT_AT­TENUATION, GL_LINEAR_ATTE­NUATION aGL_QUADRATIC_AT­TENUATION. Nezávislá proměnná je ve vztahu pouze jedna, a to d (distance), která představuje vzdálenost vykreslované plošky nebo vrcholu od zdroje světla.

Jak již bylo řečeno výše, pouze konstanta kc je nastavena na jedničku, ostatní dvě konstanty jsou nulové. To mimo jiné znamená, že celý vztah se při implicitních parametrech zkrátí na kostantní výraz s hodnotou jedna a žádný útlum světla nenastává. Útlum tedy nastavíme pomocí konstant kl a/nebo kq. První zmíněnou konstantou se specifikuje míra útlumu nepřímo úměrná rostoucí vzdálenosti, druhou konstantou míra útlumu nepřímo úměrná čtverci vzdálenosti.

Nastavení světelného útlumu nemá vliv na směrové světlo popsané v předchozím dílu, protože směrové světlo je umístěno v nekonečné vzdálenosti od zobrazované scény. Z tohoto důvodu nelze vyjádřit vzdálenost d a výraz pro výpočet útlumu tak ztrácí smysl.

Změna pozice světelného zdroje pomocí transformační matice ModelView

OpenGL zachází s pozicemi popř. směrem světel ve scéně jako s běžným vrcholem (vertexem). Na zadanou pozici světla je tedy aplikována transformace specifikovaná v matici ModelView. Na tuto skutečnost je potřeba pamatovat a pozici zadávat v tom místě programu, kde je vždy jasné, jak je transformační matice ModelView nastavena. Důležité je, že na pozici resp. směr světla se (narozdíl od vertexů) neprovádí další transformace specifikovaná transformační maticí Projection.

Pomocí vhodné kombinace funkcí pro změnu transformačních matic a nastavení pozice světla lze vytvořit tři nejčastěji používané světelné zdroje:

  1. Světlo, které zůstává stále na stejném místě.
  2. Světlo, které se pohybuje současně s pozorovatelem.
  3. Světlo, které se pohybuje okolo stojícího objektu.

První typ světla, tj. světlo, které zůstává stále na stejném místě, se vytvoří jednoduše tak, že se pozice světla specifikuje ihned při inicializaci transformačních matic, ideálně v případě, kdy je transformační matice ModelView nastavena na identitu. Tak je to naznačeno i v ukázkové části kódu, který se provádí při inicializaci pohledu na scénu:

// nastavení projekční matice
glViewport();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho();

// nastavení modelové a pohledové matice
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// ihned po nastavení modelové a pohledové
// matice specifikujeme pozici světla
glLightfv(GL_LIGHT0, GL_POSITION, position);

Nastavení světelného zdroje tak, aby se světlo pohybovalo současně se stojícím objektem, se docílí nastavením pozice světla uvnitř funkce pro vykreslení. Světlo se tak nastavuje se stejnou transformační maticí jako objekty ve scéně, což je ukázáno na ilustračním kódu:

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

// nastavení modelové a pohledové matice
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
nastavPohled();
glLightfv(GL_LIGHT0, GL_POSITION, position);

vykresliScenu();
glFlush();

Nastavení světelného zdroje tak, aby se světlo pohybovalo okolo stojícího objektu, je poněkud složitější, protože musíme změnu provádět relativně vůči nastavení pozorovatele. Řešení tohoto problému spočívá v použití zásobníku matic, do kterého uložíme právě nastavenou modelovou matici, matici změníme, nastavíme pozici světla a ze zásobníku obnovíme původní nastavení transformační matice. Možné použití je vidět na přiloženém kódu, který se volá při každém požadavku na překreslení scény:

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

// nastavení modelové a pohledové matice
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
nastavPohled();

// uložení matice na zásobník
glPushMatrix();
    glRotated(rotace, 1.0, 0.0, 0.0);
// souřadný systém je orotován
    glLightfv(GL_LIGHT0, GL_POSITION, position);

// obnovení matice ze zásobníku a vykreslení scény
glPopMatrix();

vykresliScenu();
glFlush();

Pokračování

V následujících dílech tohoto seriálu se budeme věnovat další rozsáhlé a důležité součásti knihovny OpenGL – texturování, tj. pokrytí povrchu těles rastrovými obrázky.

Demonstrační příklady

prvním demonstračním příkladu (můžete si opět stáhnout i HTML verzi) je ukázáno použití reflektorového světla. Při spuštění programu se zobrazí klasická čajová konvička osvětlená reflektorovým světlem. Konvičkou lze otáčet pomocí levého tlačítka myši, pravé tlačítko myši slouží k přemístění objektu dozadu a dopředu.

Screenshot z prvního příkladu
Screenshot z prvního příkladu

Druhý demonstrační příklad (zde je k dispozici HTML verze) je v podstatě kopií demonstračních příkladů z předchozích dílů tohoto seriálu. Je zde uveden pouze pro demonstraci světelného zdroje, jehož pozice ve scéně je neměnná. Je to zařízeno tak, že pozice světelného zdroje se nastaví jednou při inicializaci aplikace ve chvíli, kdy je maticeModelView nastavena na identity. Poté se pozice světelného zdroje nemění, což má za následek, že světelný zdroj „visí“ v prostoru stále na stejném místě, nezávisle na rotaci tělesa.

Screenshot z druhého příkladu
Screenshot z druhého příkladu

Třetí demonstrační příklad (i s HTML verzí) ukazuje, jakým způsobem lze řešit pohyb světla současně s kamerou. V příkladu je použito jedno světlo, které je transformováno stejnou transformační maticí jako objekty ve scéně, což má za následek pohyb světla současně s těmito objekty – objekt je tedy nasvětlen stále do stejného místa.

Screenshot ze třetího příkladu
Screenshot ze třetího příkladu

Další, čtvrtý demonstrační příklad (zde je k dispozici HTML verze) kombinuje oba přístupy, které byly ukázány ve druhém a třetím demonstračním příkladu. Zobrazovaná scéna je osvětlena dvěma zdroji světla, přičemž jeden světelný zdroj je nehybně umístěn na jednom místě ve světových souřadnicích, kdežto druhý světelný zdroj se pohybuje současně s kamerou.

Screenshot ze čtvrtého příkladu
Screenshot ze čtvrtého příkladu

Poslední, pátý demonstrační příklad, (HTML verze) ukazuje animaci světelného zdroje okolo objektu s použitím zásobníku matic. Objektem lze samozřejmě stále pohybovat a rotovat pomocí levého i pravého tlačítka myši.

Screenshot z pátého příkladu
Screenshot z pátého příkladu

Pro majitele pomalejšího připojení k internetu je zde k dispozici celý článek i s přílohami zabalený do jednoho zip souboru.

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.