Rendering wolumetryczny - cz. II

20.12.2010 - Adam Błaszkiewicz
TrudnośćTrudnośćTrudność
Krok 7 - Światło odbite

Trzy światła odbite - bez światła rozproszonego

Światło odbite to światło, którego kąt odbicia jest bliski kątowi padania (w przeciwieństwie do światła rozproszonego, które odbija się w każdym kierunku równomiernie). Im kąt odbicia jest bliższy kątowi padania, tym intensywniej światło się odbija. W prawdziwym świecie możemy takie światło porównać do np. nieba odbijającego się w kałuży. W przedstawionym programie będziemy śledzić światło „od końca”; tj. śledząc promień od kamery w stronę renderowanego piksela sprawdzimy, czy światło miało szansę odbić się od powierzchni obiektu tak, aby „oślepić” kamerę.

Aby otrzymać odbity promień od kamery do piksela (reflected), możemy np. rzutować -pixel.ray na pixel.normal. Rzutowanie wektora na inny wektor polega na przemnożeniu wektora, na który rzutujemy przez iloczyn skalarny obu wektorów. Otrzymany wektor dwukrotnie dodajemy do pixel.ray.

W prosty sposób intensywność światła odbitego możemy obliczyć jako cosinus kąta pomiędzy reflected i -l.direction (kąt ten jest wyróżniony na rysunku na niebiesko). Aby jednak refleksy świetlne były mniejsze, co daje znacznie lepszy efekt wizualny, przemnażamy kąt pomiędzy wektorami o pewien współczynnik typowy dla danego światła (l.specularSize):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
foreach (Light l in lights)
{
    for (int y = 1; y < sizeY + 1; ++y)
    {
        for (int x = 1; x < sizeX + 1; ++x)
        {
            Pixel pixel = pixels[x, y];
            if (pixel.depth > farClipping)
                continue;
 
            // Zauważmy, że:
            // pixel.normal * (-pixel.ray) == -(pixel.normal * pixel.ray)
            double dot = -(pixel.normal * pixel.ray);
            Vector reflected = 2 * dot * pixel.normal + pixel.ray;
 
            // Zauważmy, że:
            // reflected * (-l.direction) == -(reflected * l.direction)
            double intensity = -(reflected * l.direction);
            double angle = Math.Acos(intensity) * l.specularSize;
            if (angle >= Math.PI*0.5)
                continue;
 
            // Sprawdzamy, czy punkt nie jest w cieniu
            if (Collision(pixels[x, y].worldPoint, reflected, false))
                continue;
 
            intensity = Math.Cos(angle);
            intensity *= intensity;
            pixels[x, y].color += intensity * l.color;
        }
 
        // (...)
    }
}
5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com