„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
Hozzáadtam a "valós tükröző anyagok" részt |
Hozzáadtam a "fényt megtörő anyagok részt" |
||
| 1 107. sor: | 1 107. sor: | ||
} | } | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
* Az egyetlen kényelmetlenséget az okozhatja, hogy ha az eddig használt adatstruktúránk nem tárolta, hogy milyen irányból érkezett a sugár, mert az nyilván kell ahhoz, hogy tudjuk, hogy melyik irányba verődik vissza. | * Az egyetlen kényelmetlenséget az okozhatja, hogy ha az eddig használt adatstruktúránk nem tárolta, hogy milyen irányból érkezett a sugár, mert az nyilván kell ahhoz, hogy tudjuk, hogy melyik irányba verődik vissza. | ||
* Pl ha padló anyagát lecserélem egy tükörre, akkor az eredmény így néz ki: | * Pl ha padló anyagát lecserélem egy tükörre, akkor az eredmény így néz ki: | ||
| 1 112. sor: | 1 113. sor: | ||
* Ez valóban egy tükörnek néz ki, de egy apró probléma még akad vele... Mi van, ha két tükröt rakunk egymással szembe? A sugár a végtelenségig fog pattogni a kettő között? Nem egészen. Ugyanis ez egy rekurzív algoritmus, ahol a függvényhívásoknak a stackbe is lesz nyoma, ahol viszont a hely előbb utóbb elfogy, és ilyenkor a programunk megáll. | * Ez valóban egy tükörnek néz ki, de egy apró probléma még akad vele... Mi van, ha két tükröt rakunk egymással szembe? A sugár a végtelenségig fog pattogni a kettő között? Nem egészen. Ugyanis ez egy rekurzív algoritmus, ahol a függvényhívásoknak a stackbe is lesz nyoma, ahol viszont a hely előbb utóbb elfogy, és ilyenkor a programunk megáll. | ||
** A sugárkövető függvényünkbe követnünk kell, hogy ez hanyadik függvényhívás volt, és ha ez a szám, meghalad valamilyen értéket, pl. 8-at, akkor a sugarat már ne lőjük tovább. | ** A sugárkövető függvényünkbe követnünk kell, hogy ez hanyadik függvényhívás volt, és ha ez a szám, meghalad valamilyen értéket, pl. 8-at, akkor a sugarat már ne lőjük tovább. | ||
** Pl: | |||
<br/> <syntaxhighlight lang="c"> | |||
Color shootRay(Ray r, int recursion_level = 0) const { | |||
if(recursion_level >= 8) { | |||
return env_color; | |||
} | |||
//... | |||
} | |||
Color getColor(Intersection inter, const Light* lgts, size_t lgt_num, int recursion_level) { | |||
// ... | |||
return scene.shootRay(reflected_ray, recursion_level + 1); | |||
} | |||
</syntaxhighlight> <br/> | |||
* [http://pastebin.com/28U44wt6 Két szemben lévő tükör] hatása: | * [http://pastebin.com/28U44wt6 Két szemben lévő tükör] hatása: | ||
http://i.imgur.com/5EcYwj6.png | http://i.imgur.com/5EcYwj6.png | ||
| 1 120. sor: | 1 136. sor: | ||
** Elég nagy problémát okoz, hogy ebben a képletben a bemenet és a kimenet is hullámhossz függő. Egy lehetséges egyszerűsítés, hogy mi csak három kitüntetett színre (a pirosra a zöldre és a kékre) számoljuk ki a képlet eredményét, és ezt ezzel megszorozzuk az RGB színskálán leírt színünket. | ** Elég nagy problémát okoz, hogy ebben a képletben a bemenet és a kimenet is hullámhossz függő. Egy lehetséges egyszerűsítés, hogy mi csak három kitüntetett színre (a pirosra a zöldre és a kékre) számoljuk ki a képlet eredményét, és ezt ezzel megszorozzuk az RGB színskálán leírt színünket. | ||
** Ez közvetlenül a Maxwell-egyenletekből levezethető, bár az eredmény, a [http://hu.wikipedia.org/wiki/Fresnel-egyenletek Fresnel-egyenletek] jóval bonyolultabb, mint amit mi használni szeretnénk. | ** Ez közvetlenül a Maxwell-egyenletekből levezethető, bár az eredmény, a [http://hu.wikipedia.org/wiki/Fresnel-egyenletek Fresnel-egyenletek] jóval bonyolultabb, mint amit mi használni szeretnénk. | ||
*** Én csak a képletnek egy közelítését írom itt le, a | *** Én csak a képletnek egy közelítését írom itt le, ami eltekint a polaricázótól ([http://en.wikipedia.org/wiki/Schlick%27s_approximation Schlick's approximation]), mert a grafikában általában ezt szokták használni. | ||
**** n - törésmutató (RGB vektor) | **** n - törésmutató (RGB vektor) | ||
**** k - kioltási tényező (RGB vektor) | **** k - kioltási tényező (RGB vektor) | ||
| 1 129. sor: | 1 145. sor: | ||
***** F(theta) = F0 + (1-F0) * pow(1-cos(theta), 5) | ***** F(theta) = F0 + (1-F0) * pow(1-cos(theta), 5) | ||
<br/> | <br/> | ||
* Pl: | |||
<br/> <syntaxhighlight lang="c"> | |||
struct ReflectiveMaterial : public Material { | |||
const Color F0; | |||
ReflectiveMaterial(Color n, Color k) | |||
: F0(((n-1)*(n-1) + k*k) / | |||
((n+1)*(n+1) + k*k)) | |||
{ } | |||
Color F(float cosTheta) { | |||
return F0 + (Color(1)-F0) * pow(1-cosTheta, 5); | |||
} | |||
Color getColor(Intersection inter, const Light* lgts, size_t lgt_num, int recursion_level) { | |||
Ray reflected_ray; | |||
reflected_ray.direction = reflect(inter.ray.direction, inter.normal); | |||
reflected_ray.origin = inter.pos + 1e-3*reflected_ray.direction; | |||
return F(dot(-inter.ray.direction, inter.normal)) * scene.shootRay(reflected_ray, recursion_level+1); | |||
} | |||
}; | |||
</syntaxhighlight> <br/> | |||
* [http://pastebin.com/nSTreTpN Ezüst esetén] (n = (0.14, 0.16, 0.13), k = (4.1, 2.3, 3.1)): | * [http://pastebin.com/nSTreTpN Ezüst esetén] (n = (0.14, 0.16, 0.13), k = (4.1, 2.3, 3.1)): | ||
http://i.imgur.com/dvA9XWq.png | http://i.imgur.com/dvA9XWq.png | ||
=== 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>. | |||
** 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. | |||
* É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"> | |||
inline Vector refract(Vector I, Vector N, double n) { | |||
double k = 1.0 - n * n * (1.0 - dot(N, I) * dot(N, I)); | |||
if (k < 0.0) { | |||
return Vector(); | |||
} else { | |||
return n * I - (n * dot(N, I) + sqrt(k)) * N; | |||
} | |||
} | |||
</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. | |||
* 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. | |||
* Ezt felhasználva: | |||
<br/> <syntaxhighlight lang="c"> | |||
struct RefractiveMaterial : public ReflectiveMaterial { | |||
float n, n_rec; | |||
RefractiveMaterial(Color n, Color k) | |||
: ReflectiveMaterial(n, k), n((n.r + n.g + n.b) / 3), n_rec(1 / this->n) | |||
{ } | |||
Color getColor(Intersection inter, const Light* lgts, size_t lgt_num, int recursion_level) { | |||
Ray reflected; | |||
reflected.direction = reflect(inter.ray.direction, inter.normal); | |||
reflected.origin = inter.pos + 1e-3*reflected.direction; | |||
Color reflectedColor, refractedColor; | |||
Ray refracted; | |||
refracted.direction = refract(inter.ray.direction, inter.normal, inter.ray.in_air ? n : n_rec); | |||
if(!refracted.direction.isNull()) { | |||
refracted.origin = inter.pos + 1e-3 * refracted.direction; | |||
refracted.in_air = !inter.ray.in_air; | |||
Color F_vec = F(dot(-inter.ray.direction, inter.normal)); | |||
reflectedColor = F_vec * scene.shootRay(reflected, recursion_level+1); | |||
refractedColor = (1 - F_vec) * scene.shootRay(refracted, recursion_level+1); | |||
} else { | |||
reflectedColor = scene.shootRay(reflected, recursion_level+1); | |||
} | |||
return reflectedColor + refractedColor; | |||
} | |||
}; | |||
</syntaxhighlight> <br/> | |||
* Az eredménye: [http://pastebin.com/be4vE87t Üveg kocka] | |||
http://i.imgur.com/s6ZyCLT.png | |||
----- | ----- | ||