Rendering wolumetryczny - cz. II

20.12.2010 - Adam Błaszkiewicz
TrudnośćTrudnośćTrudność
Krok 6 - Światło rozproszone

Pojedyncze światło rozproszone

Na potrzeby tego artykułu, będziemy korzystać jedynie ze światła kierunkowego, tj. światła, które w każdym miejscu pada z tego samego kierunku. Możemy popatrzeć na to tak, że źródłem światła jest nieskończenie odległy punkt. W świecie rzeczywistym takie światło przypomina słońce oświetlające pewne niewielkie obiekty na powierzchni ziemi. Będziemy symulować rozproszenie i odbicie takiego światła.

Światło rozproszone jest podstawowym i najważniejszym rodzajem światła, jakie będziemy symulować. Właściwie, wystarczyłoby ono, aby wyrenderowany obraz wyglądał już dość dobrze. Zakładamy, że promień takiego światła po zetknięciu z powierzchnią odbija się w każdym możliwym kierunku z jednakową intensywnością - w tym w stronę kamery. Jednak intensywność ta zależy od kąta pomiędzy kierunkiem światła a normalną płaszczyzny stycznej do powierzchni w rozpatrywanym punkcie: najmocniej oświetlone będą powierzchnie, na które światło pada prostopadle. Symulację należy przeprowadzić dla wszystkich pikseli, badając intensywność światła odbitego od punktu widocznego w danym pikselu.

Zauważmy, że na rysunku nie ma zaznaczonej kamery - pozycja obserwatora nie ma znaczenia, ponieważ założyliśmy, że światło jest odbite z jednakową intensywnością w każdym kierunku.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
foreach (Light l in lights)
{
    for (int y = 1; y < sizeY+1; ++y)
    {
        for (int x = 1; x < sizeX+1; ++x)
        {
            if (pixels[x, y].depth > farClipping)
                continue;
 
            // Sprawdzamy, czy punkt nie jest w cieniu
            if (Collision(pixels[x, y].worldPoint, -l.direction, false))
                continue;
 
            double intensity = pixels[x, y].normal * -l.direction;
            if (intensity < 0)
                intensity = 0;
 
            pixels[x, y].color += intensity * l.color;
        }
 
        // (...)
    }
}

W powyższym fragmencie programu najpierw sprawdzamy, czy przetwarzany punkt jest w cieniu, śledząc promień od tego punktu w stronę światła. Jeśli napotkamy przeszkodę, punkt jest w cieniu - nie dodajemy żadnego światła do piksela. W przeciwnym wypadku, obliczamy intensywność światła za pomocą iloczynu skalarnego normalnej i odwróconego wektora światła (wektory te zaznaczone są na rysunku). Oba te wektory są znormalizowane, więc w efekcie otrzymujemy cosinus kąta pomiędzy nimi (kąt także został zaznaczony na rysunku). To, dlaczego używamy akurat funkcji cosinus, można łatwo sobie wyobrazić - możemy rozważyć (w dwóch wymiarach) strumień światła pewnej niewielkiej szerokości i zaobserwować, jak zmienia się długość oświetlonego odcinka prostej, w zależności od kąta, pod jakim światło pada na prostą. Jeśli odcinek jest dłuższy, to światło traci intensywność, ponieważ oświetla większy obszar.

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com