David Sedláček :: grafické systémy

Reflections, Refractions and Interreflections


Odrazy a lomy světla + vzájemné odrazy

Správné vykreslování odraženého a lomeného světla přispívá k větší realističnosti zobrazované scény.

Nejprve něco z fyziky:

Zákon odrazu:

Úhel dopadajícího paprsku, je roven úhlu paprsku odraženého. Vztaženo k normále.

Lom

je definován jako změna směru paprsku při přecházení z jednoho prostředí do jiného. Jedná se o změnu rychlosti, kterou se paprsek šíří v různých prostředích. Index lomu (n) vyjadřuje poměr k rychlosti světla ve vakuu (tam je n=1). Z tohoto vychází tzv. Snellův zákon popisující změnu úhlu po průchodu paprsku do jiného prostředí.

Vzorec snell


prošlé a odražené světlo. V a) n0 < n1 …b) n0 > n1

Množství světla prošlého a odraženého určuje Fresnelova rovnice.

totální odraz

Pokud paprsek prochází z prostředí opticky řidšího do prostředí opticky hustšího, a pokud je úhel mezi dopadajícím paprskem a normálou větší nežli tzv. kritický úhel, který je závislý na hustotě obou prostředí. Dochází k totálnímu odrazu, tzn. žádná část světla neprojde, vše se odrazí. Tento jev můžeme pozorovat například na hladině vody.


Techniky pro vykreslování odrazů

Je jednoduché si představit, že scéna, ve které se vyskytují reflekce je ve skutečnosti složena ze dvou obrazů. Jeden je reálný a druhý je k němu převrácený, virtuální (to co je "za" zrcadlem).

Vykreslení scény s plošným zrcadlem se dá rozdělit do dvou kroků: vytvoření virtuálních objektů. A poté vykreslení těchto objektů v reálné scéně.

Poměrně dlouhé diskuse se vedou o způsobu vypočítání virtuálních objektů. Můžeme přemístit kameru, nebo otáčet scénou, případně využít technik pro přepočítávání přes jednotlivé vertexy. Způsob jakým se potom virtuální objekty vykreslují také není jediný. Proto se pokusím přiblížit tyto různé postupy.

Rovinné zrcadlo.

Zde se pokusím popsat základní postupy pro vykreslování odražených objektů v rovinném zrcadle. Jde o využití stencil bufferu, ořezávacích rovin (clip plane) a také využití mapování textur, případně multimapping. Pro každý případ se bude scéna vykreslovat minimálně ve dvou krocích.

Na příklad:

Představme si místnost, na jedné stěně je zavěšeno rovinné zrcadlo. A v místnosti jsou libovolně rozmístěny nejrůznější objekty. Nejprve spočteme rovinu, která obsahuje naše zrcadlo, a umístíme kameru do místa, ze kterého budeme pozorovat scénu. Při kreslení odražené scény nejprve provedeme transformace objektů za zrcadlo. (můžeme k tomu přistupovat jako k otočení objektů nebo posun a natočení kamery. Oba přístupy jsou identické. Viz obr.).



dva způsoby přístupu

Reflexní transormace se dá rozložit do několika kroků: identita, rotace do roviny zrcadla, a převrácení z (-1), opět inverse a přesun zpět.


vektory

Je-li P jeden vertex zrcadla a V vektor z tohoto bodu do kamery, dá se celá transformace reflexe zapsat jako následující matice 4x4.

Aplikování této transformace na originální scénu vede k vytvoření virtuální scény, která je osově souměrná s rovinou zrcadla. Následující popisuje, jak korektně zobrazit scénu.

Rovinné odrazy za použití stencil bufferu

    Zkrácený postup prvního průchodu:
  1. Nastavit základní zobrazovací a projekční matice
  2. spočítat a aplikovat reflexní matici
  3. normálně vykreslit scénu
  4. vrátit reflexní matici

Tímto postupem vykreslíme objekty tak jak by vypadaly za zrcadlem, s tím že ignorujeme fakt, že zrcadlo nezabírá celou scénu. Pokud bychom při zobrazení odražených objektů vykreslili také zrcadlo, zobrazilo by se sice na správné pozici, ale překrylo by ostatní odražené objekty. Proto v tomto kroku musíme zrcadlo vyjmout ze zobrazovacího řetězce!

Dále musíme mít dobře nastavenou ořezávací rovinu v rovině zrcadla, jelikož objekty, které jsou normálně za zrcadlem, by se dostali před něj, což by bylo špatně. Změnou velikosti vektoru V dosáhneme k přibližování či oddalování odraženého obrazu, připomíná to mírně konkávní či konvexní zrcadlo, ale nezobrazuje jej to věrohodně.

Jako další se smažou oba buffery (DEPTH, STENCIL). Vykreslí se zrcadlo do Stencil bufferu, čímž se odmaskuje reálná scéna.Vymažeme COLOR buffer, dojde k nastavení barvy pozadí na místa, kde se bude znovu vykreslovat scéna. A vykreslíme normálně celou scénu.

    Zkrácený postup druhého průchodu:
  1. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  2. nastavení stencil bufferu (1 na místě zrcadla)
    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glStencilFunc(GL_ALWAYS, 1, 1);
    glEnable(GL_STENCIL_TEST);
  3. glColorMask(0,0,0,0) - zakáže vykreslování do color bufferu
  4. vykreslíme zrcadlo (můžeme použít blending)
  5. přenastavení stencil bufferu
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc(GL_NOTEQUAL);
  6. vymazání color bufferu na barvu pozadí
  7. glDisable(GL_STENCIL_TEST)
  8. vykreslení scény bez reflexí

Znázornění postupu

Rovinné odrazy za použití ořezávání rovin

V případě, že hardware nepodporuje stencil buffer, případně pro 5 a méně stranové polygony (zrcadla) lze s výhodou využít ořezávání rovin pomocí glClipPlane(). Ořezávací roviny se vytvoří vždy mezi jednou hranou zrcadla a polohou kamery. Plus jedna rovina je jako v případě stencil. ořezávání v rovině zrcadla. Na tyto roviny se neaplikuje reflexní matice. Jinak se vykresluje stejným způsobem ve dvou průchodech, jednou se kreslí mezi roviny a jednou vně.

Rovinné odrazy za použití mapování textur

Jiná technika využívá mapování textur. První průchod je stejný jako v předchozím, vykreslí se scéna a uloží se do textury. (glCopyTexImage2D). Ve druhém průběhu namapujeme takto vytvořenou texturu na polygon zrcadla.

    Zkrácený postup druhého průchodu:
  1. vykreslení reálných objektů
  2. glBindTexture() - vytvoření a použití textury
  3. vykreslení zrcadla se správně nastavenými koordináty

Mírnou komplikací je zjištění správné pozice textury (musí se posunout). Pro toto se s výhodou dá využít gluProject(), která zjistí kam se nám zobrazí vertex z 3D do 2D textury. Provedeme pro každý roh zrcadla.

Tento postup může být na některých systémech rychlejší nežli stencil postup. Dá se využít vygenerování textury v nižším rozlišení (horší stínování, menší detaily…). Případně při animacích je výhodné pro několik framů použít stejnou texturu reflexe. Ale na druhou stranu využívá texturovací fázi - zdržení a také některé systémy umožňují pouze jednoduché texturování, což nepůsobí realisticky.

Zakřivená zrcadla


Pro případ křivých odrazných ploch, jejichž tvar je znám, je problém dostatečně rychle nalézt "virtuální" vertex, který odpovídá vertexu skutečnému. Pro výpočet těchto odrazů je v podstatě nepoužitelná technika ořezávání rovinami, ale ostatní dvě předcházející se dají úspěšně využít.

Nyní se pokusím popsat, jak se odráží obraz na kouli.

odrazy paprsků

Paprsky dopadající na kouli se odrážejí dle zákona odrazu podle normály jednotlivých plošek. To má za následek, že každý odražený paprsek se promítne do prostoru okolo koule. Tím pádem vidíme v kouli odražené objekty. Ale problém je, že například prostor mezi dvěma paprsky které jsou spočteny z koule (vezmeme sousední body) může být úhel dostatečně velký pro zobrazení předmětu, který se tímto nezobrazí (nestrefíme se na něj). Proto se jako dostatečné přiblížení používá 2D textura z dostatečné vzdálenosti od kulové plochy. Na obr. modrá elipsa. Problémem jsou objekty mezi touto plochou a koulí. Tento případ se řeší stejně jako rovinné zrcadlo.

Jak nanést texturu na kouli?

Využijeme mapování pomocí sphere_map. Jde o postup openGL jak namapovat 2D texturu na kouli, se zachováním zdání reálného zobrazení.

Představme si, že máme připravenou texturu okolí které se zobrazí na kouli. Poté se souřadnice s,t textury vypočtou dle následujících vzorečků.


    Poté se dá textura již jednoduše aplikovat (tyto vzorce využívá již openGl pro přepočet textury).
  1. bind textury se sphere_mapou
  2. glTexGen(GL_S,GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP)
    glTexGen(GL_T,GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP)
  3. glEnable(TEXTURE_GEN_S)
    glEnable(TEXTURE_GEN_T)
  4. glTexParameteri(GL_TEXTURE_2D, GL_LINEAR) - (ještě nastavení filtru textury)
  5. vykreslení objektu zrcadla
Jak vygenerovat sphere_map?

void gen_sphere_map(GLsizei width, GLsizei height, GLfloat pos[3],GLfloat (*tex)[3])
{
  GLfloat ray[3], color[3], p[3];
  GLfloat s,t;
  int i, j;
  for (j = 0; j < height; j++) {
    t = 2.0 * ((float)j / (float)(height-1) - .5);
    for (i = 0; i < width; i++) {
      s = 2.0 * ((float)i / (float)(width - 1) - .5);
      if (s*s + t*t > 1.0) continue;
  /* spočítá bod na kouli (z normály) */
      p[0] = s;
      p[1] = t;
      p[2] = sqrt(1.0 - s*s - t*t);
  /* spočítá odražený paprsek */
      ray[0] = p[0] * p[2] * 2;
      ray[2] = p[1] * p[2] * 2;
      ray[3] = p[2] * p[2] * 2 - 1;
  /* promítne paprsek --raytracing */
      fire_ray(pos, ray, tex[j*width + i]);
    }
  }
}

Tato funkce (je to pseudo kód) spočítá pro každý texel vektor paprsku a zjistí barvu tělesa na které dopadá.


Lomy

Lomy světla mohou být renderovány technikami presentovanými pro reflexe. Tzn. Zobrazovanou scénu si posuneme a natočíme do míst, kam se nám dle snelova zákona zlomí paprsek. A vykreslíme ji pomocí výše zmíněných technik.

Pro kulaté plochy může také využít výše zmíněné metody, pouze s tím rozdílem, že pro pohled ze předu použijeme zadní mapu a tak podobně.

Vzájemné odrazy

V tomto případě také využijeme techniku stencil bufferu nebo mapování. Pro každý odraz paprsku přidáme další krok do algoritmu. Je nutné stanovit, kolikrát se paprsek může odrazit.

Nejprve se renderuje obrázek s nejhlubší rekurzí odrazu. Pro každý odražený obraz je algoritmus následující:

  1. vymazat stencil buffer
  2. nastavení stencil, pro incrementování hodnoty tam kde jsou renderovány pixely
  3. vykreslení každého zrcadla, které se podílí na výsledném obraze
  4. natavit stencil na "pass" tamk, kde hodnota stencilu odpovídá počtu odrazů
  5. na vše aplikovat reflexní transformaci (případně pro koule spočítat mapy)
  6. vykreslit odraženou scénu
  7. vykreslit zrcadlo

Při texturovací technice postupujeme také od nejhlubšího odrazu. Výhodou je že každou texturu generujeme pouze jednou a použít ji můžeme vícekrát.

Použitá literatura:

Základy počítačové grafiky - Doc.Ing. Bohuslav Hudec, CSc.
An Interactive Introduction to OpenGL Programming
Advanced Graphics Programming Techniques Using OpenGL /vydání 1998 a 2000 - Tom McReynolds and David Blythe.
http://nehe.opengl.cz/
David Sedláček ::