Ugye látott már mindenki olyan effektet, ahol a 3D-s objektum visszatükrözi a környezetét, persze teljesen torzítva. Ez az environment mapping. Az elv hasonló a phong shading-hez. (A kevésbé profiknak először azt ajánlanám.) Mivel a visszatükrözést, ahogy a fényvisszaverést is, normálvektorok segítségével tudjuk megadni. A beeső fénynek és a visszavert fénynek a sík normálvektorához viszonyított szöge (beesési és visszaverődési szög) egyenlő. Ezért, a view (nézet) vektort a normálvektorra tükrözve megkapjuk azt a vektort, ami a környezetből beeső fény irányát jelzi. Ennek a vektornak a segítségével meg tudjuk mondani, honnan jött a fény. Ha a környezet képét eltároljuk egy tömbben, ezzel a vektorral rátalálunk arra a pontra az eltárolt map-en, ahonnan jött a fény. Persze nem egyszerű az egész környezetet lerenderelni egy képre. Ha elvileg helyesek akarnánk lenni, az egész objektumot körülvevő környezetet el kéne tárolni. Mintha egy gömbben lennénk, és a gömb belső falán lévő képet kéne visszatükrözni. Egy ilyen képen pedig nem egyszerű megtalálni, amit keresünk.

Egy másik megoldás, hogy a környező teret 6 képen tároljuk el. Ami az objektumtól balra jobbra, lent, fent, előtte, mögötte van. Mintha egy kocka belsejében lennénk, és a kocka minden oldala egy-egy kép lenne, amit az objektum visszatükröz. Ezeket a képeket sem egyszerű előállítani, és keresgélni köztük sem túl egyszerű.

Ennyi félrevezető után nézzük hogyan működik ez a realtime számolás során.

Nem tároljuk el az egész környezetet, csak egyetlen 255X255-ös map-et kezelünk. Ez az environment map. Ez a kép tárolja a környezet alapvető mintáját. A csalás nem feltűnő, például egy alagút belsejében, ahol az alagút falain mindenhol ugyanaz a textúra van. Ezt az egy textúrát használva environment map-ként már célt érünk. 

A példaprogiban is egyetlen 255X255-ös textúrát használ a rutin, ami 8X8 (mint egy sakktábla) téglalapot ábrázol. A háttérkép csak egy egyszerű (elég rondára sikerült) kép, amit figyelembe se vesz, csak kirakja az objektum mögé. 

Innen a dolog teljesen ugyanaz, mint a phong shading-nél. A pixelhez tartozó normálvektor X és Y koordinátájával találjuk meg a keresett színt a map-en. Ezeket a normálvektorokat úgy kapjuk meg, hogy a sík szélein és belsején interpoláljuk a sarokpontokon ismert normálvektorokat. 

A sarokpontokhoz tartozó normálvektorokat pedig a környező síkok vektorainak kiátlagolásából kapjuk. Ezt az átlagolást egyszer kell elvégezni az objektum adatainak elkészítésekor. Forgatáskor a normálvektorokat ugyanúgy forgatjuk, mint a pontokat.

Ezt már mind leírtam a phong shading-nél, de aki esetleg elmulasztotta, egy pár ákom-bákom:

A Sarokpontokhoz tartozó normálvektorok (pirossal):

Tehát: 

Np1 = (N1 + N2+ N3 + N4) / 4 Np2 = (N2 + N1 + N5 + N6) / 4

És így tovább…

A képletben csak 3D-s vektorok szerepelnek, tehát a programban egy képletből 3 lesz. Külön X, Y, és Z összetevőkre.
Amit a phong shading-nél elmulasztottam, az a sík normálvektorainak kiszámolása. 

Ami biztos, hogy egy sík normálvektorának a meghatározásához elég 3 pont. Ha a síkot több pont határozza meg akkor is csak az első 3 a lényeg. (Mivel 3 pont már meghatároz egy síkot).

Magukra a képletekre különösebb magyarázatot nem tudnék mondani (így kell csinálni).

A három vektorösszetevőre egy sík normálvektora:

nx:=(p1.y - p2.y) * (p1.z - p3.z) - (p1.z - p2.z) * (p1.y - p3.y); ny:=(p1.z - p2.z) * (p1.x - p3.x) - (p1.x - p2.x) * (p1.z - p3.z); nz:=(p1.x - p2.x) * (p1.y - p3.y) - (p1.y - p2.y) * (p1.x - p3.x);

Ezt el kell játszani minden síkra, ezekből már ki lehet átlagolni a sarokpontok normálvektorait.

A poligonon belül a normálvetorok. 

Ez leképezés utáni állapot!

Minden egyes pixelre közelítőleg kiszámoljuk a normálvektort, interpoláljuk. Mivel csak az X és Y összetevőt fogjuk használni, elég azzal számolni.

Az environment mapping-et végző rutin szempontjából tulajdonképpen teljesen mindegy, hogy az átadott paraméterek valamikor normálvektorok voltak, a kirajzolásnál úgyis egyszerű textúrakoordináták lesznek az environment map-en. A paraméterek a rutinnak:

{Env. koords}

Points^[i].env.X := (Points^[i].newnormal.X  div  2) + 128 * 256; Points^[i].env.Y := (Points^[i].newnormal.Y  div  2) + 128 * 256;

Mivel a normálvektorokat praktikus okokból 65535-os egységhosszúságúra számoljuk, egy-egy összetevő -65535 tól +65535-ig vehet fel értéket. Ha osztjuk 2-vel, és hozzáadunk 128*256-ot, 0 és 65535 közé fog esni. Ez pedig egy 256-al felszorzott koordináta a 255X255-ös environment map-en.

Ha belegondolunk, ugyanezek a koordináták mutatnának a phong map-re is, ha lenne. Nagyon praktikusan lehet egyszerre phong shading-et is csinálni, mert a koordinátákat szinte ingyen kapjuk. 

A rutin majdnem ugyanaz, mint a háromszöges texture mapping-nél. Csak a kirajzolásnál van 2 árva sor különbség.

A Scanline Loop:

slc:
mov bx,si mov bl,dh mov al,fs:[bx] ;Environment Map add al,gs:[bx] ;Phong Map mov es:[di],al add dx,UStep add si,VStep inc di dec cx jnz slc
Kivesszük a színt az environment map-ből, és hozzá adjuk a Phong map odatartozó értékét.

Ha nem akarnánk felesleges összeadást végezni, ezt előre is megtehettük volna. A program indulásakor összeadhatjuk a két map-et, így egy összeadással kevesebb lesz pixelenként.

Összeadás helyett persze használhatnánk color .lookup table-t is, ahol minden színhez eltároljuk az árnyékolási színeit.

Így phong-shading-el együtt egész pofás lesz a dolog. 

A példaprogi Borland Pascalban sikerült.