Własny silnik graficzny. Część IV: krwawienie kolorów i miękkie cienie.

30.11.2010 - Robert Kraus
TrudnośćTrudnośćTrudnośćTrudność

Całkowanie w naszym kodzie

Procedura $ sampleHemiSphere $ daje w wyniku losowy kierunek z półsfery losując kierunki z rozkładem cosinusa. Poniżej ilustracja pokazująca różnicę pomiędzy rozkładem jednostajnym i cosinusa. W rozkładzie jednostajnym kierunki podczas losowania rozkładają się równomiernie po półsferze, natomiast w rozkładzie cosinusa kierunki bedą występować gęściej wokół wektora normalnego do powierzchni, na której leży punkt, w którym obliczamy oświetlenie.

We wzorze na gęstość w rozkładzie cosinusa $ \theta $ to kąt pomiędzy kierunkiem na półsferze a wektorem normalnym do powierzchni obiektu.

1
2
3
4
5
6
7
8
// losowy wekotr na półsferze o promieniu 1
// rozkład cosinusa
inline vec3 sampleHemiSphere() {
  float Rd = uRand();
  float Ad = uRand();
  float c = sqrt(1.0 - Rd);
  return vec3(c * cos(PiMul2*Ad), sqrt(Rd), c * sin(PiMul2*Ad), 1.0f);
}

Wróćmy teraz do procedury $ traceRay $.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// r - śledzony promień
// s - scena, w której śledzony jest promień
// d - głębokość rekursji
color traceRay(ray::i r, scene& s, nat d) {
  if (d == 0) return black; // koniec rekursji
  hit h = nearestHit(r, s); // informacje o najbliższym przecięciu
  if (h.t == undefined) return black; // brak przecięcia
 
  material& m = *h.mat; // materiał trafionego obiektu
  color texMapped = notNull(m.drMap) ? (*m.drMap)(h.uv.u, h.uv.v) : white;
  if (m.hasNormalMap) // perturbacja wektora normalnego zgodnie z teksturą
    h.N = uChangeSpace((*m.normalMap)(h.uv.u, h.uv.v), h.Ex, h.N, h.Ez);
 
  if (m.isDiffuse) // odbicie rozproszone
    return m.dr * texMapped * traceRay(scatter(r, h.ip, h.N), s, d-1);
  else // emisja
    return m.e;
}

Tak wygląda całka w przypadku naszego zagadnienia oświetlenia:

$$ \int_{\omega \in \Omega} \frac{k_d}{\pi} \cdot cos(\theta) \cdot L(\omega) \hspace{0.1cm} d \omega .$$
Gdzie:
1) $ \Omega $ to zbiór wszystkich kierunków na półsferze
2) $ \omega $ dowolny kierunek z półsfery
3) $ k_d $ jest to kolor powierzchni (współczynnik odbicia rozproszonego materiału)
4) $ \theta $ to kąt pomiędzy kierunkiem na półsferze a wektorem normalnym do powierzchni
5) $ L(\omega) $ ilość światła docierająca do trafionego punktu z kierunku $ \omega $
Nasza całkowana funkcja ma postać:
$$ f(\omega) = \frac{k_d}{\pi} \cdot cos(\theta) \cdot L(\omega). $$
Rozkład cosinusa, z którym losujemy ma funkcję gęstości postaci:
$$ p(\omega) = \frac{cos(\theta)}{\pi}. $$
Estymator więc ma postać:
$$ X_f(\omega) = \frac{f(\omega)}{p(\omega)} = k_d \cdot L(\omega),$$
Gdzie $ k_d $ w naszym kodzie to $ m.dr * texMapped $,
a $ L(\omega) $ to $ traceRay(scatter(r, h.ip, h.N), s, d-1) $.

Pamiętamy jednak, że prosty estymator $ X_f(\omega) $ był mało zadowalający. Radziliśmy sobie używając wielu prób prostego estymatora, obliczając średnią arytmetyczną uzyskanych wyników. Ten lepszy estymator realizowany jest przez funkcję $ splicer $, która wcześniej służyła jedynie do realizacji antyaliasingu.

Podsumowanie

Nauczyliśmy się jak przy pomocy śledzenia promieni generować zdjęcia wirtualnej sceny. Poznaliśmy zjawiska takie jak odbicia zwierciadlane, odbicia rozproszone, transmisja z refrakcją, miękke cienie, krwawienie kolorów. Dowiedzieliśmy się jak teksturować powierzchnie obiektów. Niedługo pojawią się kolejne części tego cyklu artykułów, w których scalimy wszystkie dotychczas poznane zjawiska tworząc program obsługujący ścieżki typów E (D|S|T)* L. Poznamy zjawisko kaustyk. Dodamy do kamery symulację systemu optycznego, który pozwoli uzyskać efekt głębi ostrości. Dowiemy się trochę więcej o symulacji lokalnych zjawisk związanych z odbijaniem światła. Na sam koniec wprowadzimy obsługę siatek trójkątów i bardziej skomplikowanej geometrii, żeby zobaczyć coś ciekawszego od kilku kulek :)

Część I    Część II    Część III    Część IV   

4
Twoja ocena: Brak Ocena: 4 (4 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com