Grafická knihovna OpenGL (20): bodové světlo, směrové světlo a reflektor

18. 11. 2003
Doba čtení: 9 minut

Sdílet

Dnešní pokračování seriálu o grafické knihovně OpenGL došlo již ke svému jubilejnímu dvacátému pokračování. Místo nudných oslavných řečí si však povíme podrobnější informace o nastavení světelných zdrojů, které mohou být třech typů: bodové světlo, směrové světlo a reflektor.

Materiály, stínování – třetí část

Nastavení světelných zdrojů podrobněji

Jak jsme si již řekli v předminulém dílu, je nutné před použitím osvětlení ve scéně toto osvětlení globálně zapnout. To se provede příkazem void glEnable(GL_LIG­HTING). Opětovné vypnutí lze provést příkazem void glDisable(GL_LIG­HTING), dotaz na stav (tj. zapnutí či vypnutí osvětlení) lze provést zavoláním funkce GLboolean glIsEnabled(GL_LIG­HTING). Pokud je osvětlení globálně zapnuto, počítá se barva tělesa z charakteristiky světla a materiálu. Pokud je naopak osvětlení vypnuto, odpovídá barva povrchu tělesa hodnotám zadaným příkazy void glColor*() nebo barvě texelu z textury.

V každé implementaci OpenGL lze používat minimálně osm světel, i když v některých implementacích může být tento počet vyšší. Tato světla se ve všech funkcích označují symbolickými konstantami GL_LIGHT0 až GL_LIGHT7. Každé světlo lze nezávisle na ostatních povolit, zakázat a nastavit jeho vlastnosti.

Povolení některého světla lze provést příkazem void glEnable(GL_LIG­HT?), kde za otazník se doplní číslo světla. Opětovné zakázání světla se provede příkazem void glDisable(GL_LIG­HT?). Programový dotaz, zda je nějaké světlo povoleno, se provede zavoláním funkce GLboolean glIsEnabled(GL_LIG­HT?).

Při práci s více světly je důležité si uvědomit, že větší množství povolených (tj. zapnutých) světel může znatelně zpomalovat vykreslování celé scény. Je to způsobeno faktem, že především starší grafické akcelerátory nepodporovaly výpočty osvětlení, a tak se osvětlení počítalo programově s využitím výpočetního výkonu hlavního procesoru. Vzhledem k tomu, že výpočetní náročnost výpočtu osvětlení lineárně roste s počtem zapnutých světel, může větší počet světel zatížit hlavní procesor, který pak nestačí dodávat data grafickému akcelerátoru. Moderní grafické akcelerátory, které obsahují GPU, již podporují výpočet osvětlení přímo na grafické kartě, takže se výkon hlavního procesoru dá využít na důležitější věci (například odstranění neviditelných částí scén).

Vlastnosti jednotlivých světel se nastaví pomocí následujících funkcí:

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

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

Tyto funkce se liší pouze typem posledního parametru. Význam všech parametrů:

  1. Do parametru light se zadává symbolické jméno světla, jehož parametry chceme měnit. To znamená, že tento parametr může nabývat hodnotGL_LIGHT0 až GL_LIGHT7.
  2. Parametr pname udává vlastnost světla, kterou potřebujeme nastavit nebo změnit. Pro dvě funkce uvedené výše můžeme použít hodnoty: GL_SPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_AT­TENUATION, GL_LINEAR_ATTE­NUATION neboGL_QUADRA­TIC_ATTENUATI­ON. První dva parametry mají smysl pouze u reflektorového světla a budou popsány dále. Další tři parametry udávají faktory poklesu intenzity světla se vzdáleností od světelného zdroje.
  3. Do parametru param se zadá číselná hodnota vlastnosti světla, která byla vybrána předchozím parametrem.

Předchozí dvě funkce umožňovaly nastavit pouze jednu číselnou hodnotu. Světla však mají i další vlastnosti, u kterých se nastavuje více hodnot (tj. vektor) současně. Pro tyto případy jsou vytvořeny následující dvě funkce, které jako svůj třetí parametr akceptují pole:

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

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

Význam parametrů těchto funkcí:

  1. Do parametru light se, podobně jako u funkcí glLighti() a glLightf(), zadává symbolické jméno světla, jehož parametry chceme měnit. To znamená, že tento parametr může nabývat hodnot GL_LIGHT0 ažGL_LIGHT7.
  2. Parametr pname opět udává vlastnost světla, která se má nastavit: GL_AMBIENT (RGB hodnoty ambientní složky světla), GL_DIFFUSE (RGB hodnoty difúzní složky světla), GL_SPECULAR (RGB barva odlesků),GL_PO­SITION (pozice světelného zdroje nebo směr šíření směrového světla), GL_SPOT_DIRECTION (směr šíření reflektorového světla).

Konkrétní význam všech parametrů bude podrobněji vysvětlen v následujícím tex­tu.

Bodové světlo

Nejjednodušší (alespoň pro pochopení, programová implementace je poměrně složitá) je bodové světlo. Toto světlo si můžeme představit například jako žárovku v prostoru, která vyzařuje světlo na všechny strany se stejnou intenzitou. Pozice bodového světla se zadává příkazy:

glLightfv(GL_LIGHT?, GL_POSITION, position);
glLightiv(GL_LIGHT?, GL_POSITION, position);

kde parametr position je ukazatel na pole čtyř hodnot typu GLfloat nebo GLint. První tři hodnoty reprezentují pozici světla ve světových souřadnicích, tedy složky (x, y, z). Poslední hodnota v tomto poli musí vždy obsahovat jedničku, protože pomocí této hodnoty OpenGL rozliší, zda se jedná o bodové, nebo směrové světlo (podrobnější informace viz poznámka níže). Je potřeba si dát pozor na skutečnost, že implicitně je světlo nastavené na souřadnice (0, 0, 1, 0), takže jde o směrové světlo umístěné na z-ové souřadné ose. Jednoduché použití bodového světla je ukázáno v prvním demonstračním příkladu uvedeném na konci tohoto textu.

V době zadání pozice světla je tato pozice podrobena transformaci podle obsahu transformační matice MODELVIEW (viz první díly tohoto seriálu). Proto je zapotřebí tuto transformační matici před zadáním pozice světla korektně nastavit. Této vlastnosti se dá dobře využít v případě, kdy požadujeme, aby se světlo pohybovalo současně s objektem, tj. aby se pro transformaci pozice světla i objektu používala stejná transformační matice. Ukázky této techniky budou uvedeny v dalším dílu.

Směrové světlo

Směrové světlo nemá v pojetí OpenGL žádný pevně daný světelný zdroj. Světlo se šíří z jednoho směru (je zadáno vektorem) nezávisle na vzájemné poloze a vzdálenosti objektů v zobrazované scéně. Za směrové světlo lze považovat například slunce, neboť světelné paprsky, které dopadají na povrch těles ze slunce, jsou víceméně paralelní (vzhledem k poměru vzdáleností slunce a těles od sebe). Směr směrového světla se zadává stejným příkazem jako u světla bodového, tj. příkazy:

glLightfv(GL_LIGHT?, GL_POSITION, direction);
glLightiv(GL_LIGHT?, GL_POSITION, direction);

kde parametr direction je ukazatel na pole čtyř hodnot typu GLfloat nebo GLint. Narozdíl od bodového světla však v tomto poli není uložena pozice světla, ale vektor reprezentující směr šíření světla. Poslední složka v poli tedy musí v tomto případě obsahovat nulu (viz poznámka). V případě směrového světla se při výpočtu difúzního osvětlení a odlesků nebere v potaz pozice světla, ale přímo zde zadaný vektor, proto jsou výpočty se směrovým světlem jednodušší a tím pádem i rychlejší. Jednoduché použití směrového světla je ukázáno ve druhém demonstračním příkladu uvedeném na konci tohoto textu.

Plošné světlo

Plošné světlo, tj. světlo, které se šíří z plošného zdroje osvětlení, není v knihovně OpenGL podporováno. Jedná se totiž o výpočetně nejnáročnější zdroj světla, který není jednoduché simulovat. V počítačové grafice, která se zabývá fotorealistickým zobrazením scén, se pro plošné zdroje světla používá radiační metoda (radiosity), jež má však velkou výpočetní složitost, která prudce roste s počtem plošek v zobrazované scé­ně.

Zde se o plošném světle zmiňuji především proto, že toto světlo je v reálném světě velmi časté. Typickým příkladem je například zářivkové osvětlení, ale i takové okno do místnosti je v podstatě plošným zdrojem světla v této místnosti. Tam, kde je zapotřebí použít plošné světlo, se musí v OpenGL použít buď světelné mapy (lightmapy), nebo více zdrojů směrového resp. reflektorového světla.

Reflektorové světlo

Nejsložitějším světlem v OpenGL je reflektorové světlo. Tento typ světelného zdroje se chová podobně jako klasický reflektor, tj. světlo se šíří (stejně jako u bodového světla) z jednoho bodu, ale ne do všech směrů, nýbrž pouze v předem zadaném kuželu. U tohoto světla se také musí zadat nejvíce parametrů. Kromě pozice světla (tj. kde je umístěn reflektor) se musí zadat také hlavní směr šíření světelných paprsků (kam je reflektor namířen) a úhel síření světla (světelný kužel), ve kterém intenzita světla postupně klesá směrem k nule.

Reflektorovým světlem se budeme podrobněji zabývat v dalším pokračování.

„Záporné“ světlo

Při zadávání barvy jakékoliv světelné složky je možné zadat i záporné hodnoty. Toto nastavení má za následek odečtení hodnot barevných složek od barvy materiálu. V zobrazované scéně je potom vidět, že světlo se zápornými hodnotami osvětlení materiálu ve skutečnosti snižuje, místo aby ji zvyšovalo. Použití „záporného“ světla je ukázáno ve čtvrtém demonstračním příkladu na konci tohoto textu.

Poznámka o rozdílu mezi bodem a vektorem

Zejména v počítačové grafice, ale například i v geometrii, je zapotřebí nějakým způsobem rozlišit body a vektory. Řešení spočívá v použití takzvaných homogenních souřadnic, kdy místo třech souřadnic (x, y, z) používáme souřadnice čtyři (x, y, z, w). Čtvrtá souřadnice je nazývána váha – odtud plyne její označení symbolem w (weight). Pokud je váha nastavena na jedničku, tj. (x, y, z, 1), jedná se o zadání bodu, pokud je váha nastavena na nulu, tj. (x, y, z, 0), jedná se o vektor. Jiné (nenulové) hodnoty váhy w se eliminují tak, že se provede transformace z (x, y, z, w) na (x/w, y/w, z/w, 1).

Všechny operace nad vektory i body musí mít výsledek takový, aby váha byla buď jedničková, nebo nulová. Podívejme se, jak je tato podmínka splněna pro základní operace:

  • Sčítání bodů není korektní operace (je závislá na změně souřadného systému). Je to také vidět ze zápisu této operace: p1 +p2 = (x1 , y1 , z1 , 1)+ (x2 , y2 , z2 , 1)= (x1 +x2 , y1 +y2 , z1 +z2 , 2), takže výsledkem není ani bod ani vektor.
  • Sčítání vektorů je korektní operace, což lze poznat ze zápisu této operace: v1 +v2 = (x1 , y1 , z1 , 0)+ (x2 , y2 , z2 , 0)= (x1 +x2 , y1 +y2 , z1 +z2 , 0), takže výsledkem součtu vektorů je opět vektor.
  • Součet vektoru a bodu (tj. posun bodu ve směru daného vektoru) je korektní operace: p1 +v1 = (x1 , y1 , z1 , 1)+ (x2 , y2 , z2 , 0)= (x1 +x2 , y1 +y2 , z1 +z2 , 1), takže výsledkem součtu je bod na nových souřadnicích.
  • Odečítání bodů je korektní operace, neboť jejím provedením získáme vektor mezi těmito body: p1 -p2 = (x1 , y1 , z1 , 1)- (x2 , y2 , z2 , 1)= (x1 -x2 , y1 -y2 , z1 -z2 , 0).
  • Odečítání vektorů je operace totožná s sčítáním vektorů, protože poslední prvek čtveřice je vždy nastaven na nulu, která není znaménkem operace ovlivněna.

Pokračování

V následujícím dílu dokončíme část věnovanou osvětlení a materiálům. Budeme se podrobněji zabývat reflektorovým světelným zdrojem a změnám pozice světelného zdroje pomocí transformační matice ModelView.

Demonstrační příklady

První demonstrační příklad (HTML verze) předvádí použití bodového zdroje světla, kterým se osvětluje objekt ve vykreslované scéně. V našem případě jde o čajovou konvičku použitou i v předchozích demonstračních příkladech. Pomocí levého tlačítka myši se s objektem rotuje, pravé tlačítko myši slouží k přiblížení nebo oddálení objektu od pozorovatele.

Druhý demonstrační příklad (HTML verze) je podobný prvnímu příkladu s tím rozdílem, že pro osvětlení je použito směrové světlo namísto světla bodového. Další funkčnost programu zůstáva nezměněna. Všimněte si, že oproti minulému příkladu se osvětlení konvičky nemění s posunem dopředu/dozadu, protože u směrového světla není zadán jeho zdroj.

Ve třetím demonstračním příkladu (zde HTML verze) je zobrazena čajová konvička osvětlená více zdroji světla. Každý světelný zdroj osvětluje těleso jednou základní barvou. Tyto barvy se potom na povrchu tělesa mohou sčítat a vytvářet tak zajímavé světelné efekty. Použití více světelných zdrojů však s sebou nese větší výpočetní náročnost, což se může projevit zejména u rozsáhlejších scén.

ict ve školství 24

Poslední, čtvrtý demonstrační příklad (i s HTML verzí), ukazuje použití světelného zdroje, který má barevné složky nastavené na záporné hodnoty. Výsledek je ten, že se barva tohoto světla odečítá od barvy na povrchu tělesa a místo světelného zdroje tak používáme světelný „vysavač“.

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

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.