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

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

Co nowego w kodzie?

Na początek potrzebujemy procedury losującej kierunki z półsfery. Procedura $ sampleHemiSphere $ daje w wyniku losowy kierunek z półsfery. Nie musisz teraz rozumieć jak ona działa.

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);
}

Kolejną rzeczą jaką będziemy potrzebować jest procedura, która wygeneruje nam kierunek z półsfery zaczepionej w dowolnym punkcie sceny o dowolnie zorientowanym wektorze normalnym do powierzchni. Przyda się do tego procedura $ uChangeSpace $, której używalismy w mapowaniu wektorów normalnych.

1
2
3
4
5
6
7
8
inline vec3 uChangeSpace(vec3::i V, vec3::i Ex, vec3::i Ey, vec3::i Ez) {
  return vec3(
    V.x*Ex.x + V.y*Ey.x + V.z*Ez.x,
    V.x*Ex.y + V.y*Ey.y + V.z*Ez.y,
    V.x*Ex.z + V.y*Ey.z + V.z*Ez.z,
    1.0f
  );
}

Spójrzmy na poniższą ilutrację.

Czerwonym promieniem trafiamy w obiekt ($ ip $ - punkt na jego powierzchni). Z obiektu możemy odczytać wektor normalny $ N $ do powierzchni. Przy pomocy iloczynów wektorowych obliczamy wektory $ Ex $ oraz $ Ez $. Losujemy wektor na półsferze przy pomocy procedury $ sampleHemiSphere $, a następnie mapujemy go na przestrzeń rozpiętą na wektorach $ Ex $, $ N $, $ Ez $, otrzymując interesujący nas wektor kierunku odbicia z rozproszeniem (na ilustracji wektor koloru żółtego). Cały scenariusz realizuje poniższa procedura $ scatter $.

1
2
3
4
5
6
7
// odbicie z rozproszeniem
inline ray scatter(ray::i r, vec3::i p, vec3::i N) {
  vec3 Ex = uCrossProd(r.abackDir, N);
  vec3 Ez = uCrossProd(Ex, N);
  vec3 hsV = sampleHemiSphere();
  return ray(p, uChangeSpace(hsV, Ex, N, Ez));
}

W procedurze $ traceRay $ dokonujemy niewielkiej zmiany. Wcześniej obliczaliśmy trzy składowe - oświetlenie bezpośrednie z punktowego źródła światła, odbicie zwierciadlane i transmisję z refrakcją, a następnie sumowaliśmy ich wyniki. Teraz obsługujemy tylko emisję i odbicie rozproszone.

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;
}

Materiały do artykułu

Zaprogramowaliśmy już wszystkie poprawki w naszym kodzie, więc możesz teraz ściąganąć pełny kod napisanego programu.

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

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com