Na szóval a shading gondolom egyértelmű. De mi az a Gouraud. Nos ő az a fickó, aki kitalálta a gouraud shadinget. Ha a dolog nevét már értjük, nézzük mi is ez.

A Gouraud shading a folytonos árnyalást, lineáris interpolációs módszerrel valósította meg. (Ez mán döfi!) 

Ismerjük a poligon sarkain a fényerősséget, a poligon összes többi pontján csak közelítjük azt. A közelítés lehetne többféle is, ennél az árnyékolási módszernél lineárisan közelítünk. Az interpoláció módján nagyon sok múlik, a phong shading csak annyiban különbözik a gouraud-tól, hogy ott nem lineáris a közelítés.

Tehát, ha árnyalni akarunk, nem ártana fényről gondoskodni. Azt, hogy a fény hova mutat, legegyszerűbb egy nullvektorral megmondani. Az pedig, hogy egy síkon mekkora a fényerősség, nem függ mástól, mint a fény vektora, és a sík vektora által bezárt szögtől. Egész pontosan egyenesen arányos a bezárt szög koszinuszával. 

Ahol C a fényintenzitás, K az anyagtól függő visszaverődési tényező, apedig a fény vektora, és a sík vektora által bezárt szög koszinusza.

Ha segítségül hívjuk a skaláris szorzatot, arra jutunk hogy:

Ahol L a fény vektora, N a sík normálvektora, a pedig a két sík által bezárt szög.

Ezt a K állandóval összeszorozva megkapjuk a fényerősséget a síkra. 

Több probléma is felmerül. Először is honnan tudom meg a sík normálvektorát, aztán a vektor hosszának kiszámolásához gyököt kellene vonni. 

A gyökvonást könnyű kiküszöbölni, ha a vektorokat egységnyi hosszúságúra állítjuk. Ehhez egyszer kell gyököt vonni a program elején (a hosszúság kiszámolásához), és mind a három vektorösszetevőt elosztani a hosszúsággal. Ezzel a hossz egységnyi lesz. 

A sík normálvektorának 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);

Ezzel ki lehet számolni az összes sík normálvektorát. Minden összetevőt leosztva a hosszúsággal egységnyi hosszú vektorokat kapunk.

Ezzel még nem végeztünk, hiszen az árnyékolás lényege, hogy a sokszög sarokpontjainak fényerejéből közelítjük a belső pontokat.

A sarokpont fényereje meghatározható a sarokponthoz tartozó normálvektor segítségével. Ezt a normálvektort egyszerűen a környező síkok normálvektorainak kiátlagolásával kapjuk. Matematikailag persze egy pontnak nem lehet normálvektora, viszont esetünkben egy képernyőpont a 3D-s modellben egy kis síkot takar, aminek már lehet.

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.

A sarokpont fényereje meghatározható az eddigiek alapján, az egységnyi hosszú vektorokkal:

A képlet igen egyszerű. Osztani nem kell, és gyököt vonni sem.

Összetevőkre, ahogy a programban is szerepelni fog:

C=K*(Np.x*L.x+Np.y*L.y+Np.z*L.z)

Ezzel megkapjuk a sarokpont színét. 

A poligon belső pontjait közelíteni fogjuk.

Először a poligon széleire számoljuk ki a fényintenzitást, majd a scanline-on belül a többire. Scanline= egy, a poligont felépítő vízszintes vonal.

Az interpoláció két lépésben történik. Először Y, majd X irányban.


Az Y irányúnál a képlet:                                   X irányban:
a=a0+(y-y0)*(a1-a0)/(y1-y0)                              a=a0+(x-x0)*(a1-a0)/(x1-x0)

Ahol az "a" betűk a fényintenzitást jelentik.

Vegyük észre, hogy két egymást követő "a" között mindig ugyanannyi a különbség, mind Y, mind X irányban. Ezeket lépésközként használva, csak egy összeadás a következő pont színének kiszámolása. (Ettől lineáris az interpoláció)

A lépésközök:

Y irányban:                         X irányban:

StepY=(a1-a0) /(y1-y0)        StepX=(a1-a0)/(x1-x0)

Felmerülhet a kérdés, hogy honnan tudjuk, hol a poligon széle. A legegyszerűbb megoldás, ha egy tömbben eltároljuk minden scanline-ra az elejét, végét, a kezdő és végszínt. Ezt az Y irányú interpoláció során megtehetjük. 320X200-as felbontásnál egy 200 elemű tömb megfelel. A tömb minden n.-dik eleme az n.-dik sort reprezentálja a képernyőn. Ha ez a tömb megfelel?en fel van töltve, az X irányú interpoláció során már csak ki kell szedegetni az elemeket, és megrajzolni a scanline-t.

Eddig volt elméletileg.

A gyakorlatban egy pár dologra figyelni kell:

    Ne számoljunk lebegőpontosan. Ezt úgy érhetjük el, hogy az értékeket felszorozzuk kettő valamely hatványával, mely művelet egy balraléptetéssel (shl) megtehető. Mikor az eredményre szükségünk van, egyszerűen visszaléptetjük (sar / shr csak pozitív számokra). Így járunk el az interpoláció során, és a vektoroknál.

    Ennek értelmében a vektorok egységnyi hosszúsága nem egyet, hanem mondjuk 65536-ot jelent. 16 bittel balraléptetve.

    Figyeljünk, a képernyőn kívülre ne akarjunk írni. Az egyik legbiztosabb módja, hogy lefagyjon a program. (Clipping- elni kell)

    A sokszögek leírásánál a láthatóaknak legyen ugyanaz a körüljárásuk, a nem látatóaknak pedig fordított. Ezzel meg tudjuk mondani, a test melyik oldala látható, melyik nem. Az interpoláció során is fel fogjuk használni a tömb feltöltésénél.

    Meg ami még a programból kiderül.

A progi pascalban van, igyekeztem kevés assembly-vel. 
forrkod.zip