„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés

Rohamcsiga (vitalap | szerkesztései)
Rohamcsiga (vitalap | szerkesztései)
1 345. sor: 1 345. sor:


=== A fényt megtörő anyagok ===
=== A fényt megtörő anyagok ===
* A Fresnel egyenlet eddigi felhasználásakor azt feltételeztük, hogy a fénynek az a része, ami nem verődik vissza, az megpróbál továbbmenni, de pl. egy fém belsejében ezt nem tudja megtenni, ezért elnyelődik, energiává alakul. De nem minden anyag viselkedik így.
 
* Pl. az üveg esetében a fény, ha nem verődik vissza, akkor továbbmegy az üvegben, de egy picit el is térül. Az irányának a megváltozását a Snelius-Descart törvény írja le <code> sin(Alpha1) / sin(Alpha2) = n1 / n2 </code>.
A Fresnel egyenlet eddigi felhasználásakor azt feltételeztük, hogy a fénynek az a része, ami nem verődik vissza, az megpróbál továbbmenni, de pl. egy fém belsejében ezt nem tudja megtenni, ezért elnyelődik, energiává alakul. De nem minden anyag viselkedik így.
** Az irány kiszámoláshoz jobb lenne egy képlet, ami egyszerű vektorműveleteket használ.
 
** A kiszámolásnál figyelnünk kell a teljes visszaverődés esetére is. Ilyenkor gyakorlatilag a fény 100%-ka visszaverődik, még az a rész is, ami a Fresnel egyenlet szerint továbbmenne.
Például az üveg esetében a fény, ha nem verődik vissza, akkor továbbmegy az üvegben, de egy picit el is térül. Az irányának a megváltozását a Snelius-Descart törvény írja le <code> sin(Alpha1) / sin(Alpha2) = n1 / n2 </code>. Az irány kiszámoláshoz jobb lenne egy képlet, ami egyszerű vektorműveleteket használ. Továbbá a kiszámolásnál figyelnünk kell a teljes visszaverődés esetére is. Ilyenkor gyakorlatilag a fény 100%-ka visszaverődik, még az a rész is, ami a Fresnel egyenlet szerint továbbmenne. És persze a teljes visszaverődés esetén a spekuláris megcsillanás számolásakor se szabad megszorozni az eredményt a Fresneles taggal.
* Én az irány kiszámolásához az alábbi képletet fogom használni. A képlet másolása helyett inkább próbáld meg levezetni magadnak.
 
Én az irány kiszámolásához az alábbi képletet fogom használni. A képlet másolása helyett inkább próbáld meg levezetni magadnak.


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
1 362. sor: 1 363. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


* Fontos megjegyezni, hogy ebben a képletben az ''' 'n' a relatív törésmutató'''. Pl. ha a sugár levegőből üvegbe megy, akkor - mivel a levegő törésmutatója 1-nek tekinthető - így a relatív törésmutató az üveg törésmutatója: 1.5 / 1 = 1.5. Viszont amikor a sugár az üvegből távozik, akkor a relatív törésmutató 1 / 1.5 = 0.666.
Fontos megjegyezni, hogy ebben a képletben az ''' 'n' a relatív törésmutató'''. Pl. ha a sugár levegőből üvegbe megy, akkor - mivel a levegő törésmutatója 1-nek tekinthető - így a relatív törésmutató az üveg törésmutatója: 1.5 / 1 = 1.5. Viszont amikor a sugár az üvegből távozik, akkor a relatív törésmutató 1 / 1.5 = 0.666. Gyakori hiba ennek a reciprok képzésnek a lehagyása.
** Gyakori hiba ennek a reciprok képzésnek a lehagyása.
 
* Egy másik hibalehetőség ezzel a függvénnyel kapcsolatban, hogy ha normálvektor ellentettjét használjuk, akkor rossz eredményt ad. Erre fontos figyelni, pl. amikor a kocka belsejéből kifele jön a sugár, hiszen ilyenkor a befele mutató normállal kell számolni, nem a kifele mutatóval. Ugyanígy a Fresnel egyenlet is rossz eredményre vezet, ha a normál ellentettjével számolunk, ezért célszerű a függvény legelején megfordítani a normált, ha arra szükség van:
Egy másik hibalehetőség ezzel a függvénnyel kapcsolatban, hogy ha normálvektor ellentettjét használjuk, akkor rossz eredményt ad. Erre fontos figyelni, pl. amikor a kocka belsejéből kifele jön a sugár, hiszen ilyenkor a befele mutató normállal kell számolni, nem a kifele mutatóval. Ugyanígy a Fresnel egyenlet is rossz eredményre vezet, ha a normál ellentettjével számolunk, ezért célszerű a számolások legelején megfordítani a normált, ha arra szükség van:


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
1 372. sor: 1 373. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


* Fontos még, hogy a Fresnel egyenletben kifele menet is ugyan azt a törésmutatót kell használni. Az F0 kiszámolásakor az egyesek a levegő törésmutatója helyén állnak, általános esetben a képlet a <code>((n1 - n2)^2 + k*k) / ((n1 + n2)^2  + k*k)</code> alakot veszi fel. Ez a képlet viszont n1 és n2 szempontjából szimmetrikus, így '''nem kell két F0-t számolni''', egyet a befele, egyet meg a kifele menő sugarakhoz.
Fontos még, hogy a Fresnel egyenletben kifele menet is ugyan azt a törésmutatót kell használni. Az F0 kiszámolásakor az egyesek a levegő törésmutatója helyén állnak, általános esetben a képlet a <code>((n1 - n2)^2 + k*k) / ((n1 + n2)^2  + k*k)</code> alakot veszi fel. Ez a képlet viszont n1 és n2 szempontjából szimmetrikus, így '''nem kell két F0-t számolni''', egyet a befele, egyet meg a kifele menő sugarakhoz.
* Csak olyan törő anyagokkal foglalkozunk, amiknek a törésmutatója a hullámhossztól független. A nem így viselkedő anyagok, a prizmák nagyon látványos képeket tudnak eredményezni, de sokkal számításigényesebbek és bonyolultabbak, ezért most nem fogok velük foglalkozni.
 
* Ezt felhasználva:
Csak olyan törő anyagokkal foglalkozunk, amiknek a törésmutatója a hullámhossztól független. A nem így viselkedő anyagok, a prizmák nagyon látványos képeket tudnak eredményezni, de sokkal számításigényesebbek és bonyolultabbak, ezért most nem fogok velük foglalkozni.
 
Az én implementációm leegyszerűsítve:


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
1 380. sor: 1 383. sor:
   float n, n_rec;
   float n, n_rec;
   
   
   RefractiveMaterial(float n, Color k)
   RefractiveMaterial(float n, Color k, Color specular_color, float shininess)
     : ReflectiveMaterial(n, k), n(n), n_rec(1/n)
     : ReflectiveMaterial(n, k, specular_color, shininess), n(n), n_rec(1 / n)
   { }
   { }
   
   
1 401. sor: 1 404. sor:
       refracted.in_air = !inter.ray.in_air;
       refracted.in_air = !inter.ray.in_air;
   
   
       Color F_vec = F(dot(-inter.ray.direction, inter.normal));
       Color F_vec = F(dot(-inter.ray.direction, inter.normal));
       reflectedColor = F_vec * scene.shootRay(reflected, recursion_level+1);
       reflectedColor = F_vec * scene.shootRay(reflected, recursion_level+1)
                      + getSpecularHighlight(inter, lgts, lgt_num, reflected.travelled_dist, false);
       refractedColor = (1 - F_vec) * scene.shootRay(refracted, recursion_level+1);
       refractedColor = (1 - F_vec) * scene.shootRay(refracted, recursion_level+1);
     } else {
     } else {
       reflectedColor = scene.shootRay(reflected, recursion_level+1);
       reflectedColor = scene.shootRay(reflected, recursion_level+1)
                      + getSpecularHighlight(inter, lgts, lgt_num, reflected.travelled_dist, true);
     }
     }
   
   
1 413. sor: 1 418. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


* Példaprogram: [[Média:Grafpp_raytrace_uveg.cpp|Üvegkocka]]
Példaprogram: [[Média:Grafpp_raytrace_uveg.cpp|Üvegkocka]]
http://i.imgur.com/s6ZyCLT.png
 
* A kocka egészen hihetően néz ki, viszont '''a talaj megvilágítása teljesen rossz'''. Az árnyékszámító algoritmus azt feltételezte, hogy fény a nem megy át a - jelenleg átlátszó - kockán. De ha az árnyékokat elhagynánk, akkor is teljesen rossz képet kapnánk. Az üveg kocka megtöri a fényt, de néhol tükröz is, esetleg sok fénysugarat ugyanabba a pontba fókuszál... ezeknek a jelenségeknek a hatását az eddigi megvilágítási modellünk egyáltalán nem vette figyelembe.
http://i.imgur.com/SjKnAV0.jpg
* A klasszikus megvilágítási modell (ahol az anyagok színe az ambiens, diffúz és spekuláris tagok összege) azt feltételezte, hogy a fény, a fényforrásból a jelenet bármely pontjába csak egyenes úton juthat el. Ennek az a nagy előnye, hogy egy felületi pont színéhez nem kell tudnunk a többi pont színéről semmit. Az ezzel a tulajdonsággal rendelkező világításszámoló algoritmusokat lokális illuminációnak nevezzük. Ha a jelenetben van tükröző vagy törő anyag akkor ez értelemszerűen nem működik. Az ilyen jelenteknél másképp kell megvilágítást számolnunk. Ilyenkor globális illuminációra van szükségünk.
 
A kocka egészen hihetően néz ki, a két oldala nagyrészt átlátszó, míg a tetején teljes visszaverődést látunk. Viszont '''a talaj megvilágítása teljesen rossz'''. Az árnyékszámító algoritmus azt feltételezte, hogy fény a nem megy át a - jelenleg átlátszó - kockán. De ha az árnyékokat elhagynánk, akkor is teljesen rossz képet kapnánk. Az üveg kocka megtöri a fényt, de néhol tükröz is, esetleg sok fénysugarat ugyanabba a pontba fókuszál... ezeknek a jelenségeknek a hatását az eddigi megvilágítási modellünk egyáltalán nem vette figyelembe.
 
A klasszikus megvilágítási modell (ahol az anyagok színe az ambiens, diffúz és spekuláris tagok összege) azt feltételezte, hogy a fény, a fényforrásból a jelenet bármely pontjába csak egyenes úton juthat el. Ennek az a nagy előnye, hogy egy felületi pont színéhez nem kell tudnunk a többi pont színéről semmit. Az ezzel a tulajdonsággal rendelkező világításszámoló algoritmusokat lokális illuminációnak nevezzük. Ha a jelenetben van tükröző vagy törő anyag akkor ez értelemszerűen nem működik. Az ilyen jelenteknél másképp kell megvilágítást számolnunk. Ilyenkor globális illuminációra van szükségünk.


=== A globális illumináció ===
=== A globális illumináció ===