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

Rohamcsiga (vitalap | szerkesztései)
Hozzáadtam a "valós tükröző anyagok" részt
Rohamcsiga (vitalap | szerkesztései)
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 tárgyból is általában ez szokott kelleni.
*** É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
 


-----
-----