„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
| 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. | |||
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. | |||
<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. 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 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. | |||
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]] | |||
http://i.imgur.com/ | |||
http://i.imgur.com/SjKnAV0.jpg | |||
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ó === | ||