Wiesz już, jak zapisać promień i jak sprawdzić, czy trafia w sferę. Zostało jedno pytanie: skąd bierze się D? Zamieniamy pozycję kamery, pole widzenia i współrzędne piksela w konkretny wektor kierunku.
TL;DR
Kamera potrzebuje trzech wzajemnie prostopadłych wektorów jednostkowych — forward, right i up — liczonych raz, na starcie renderu.
Pole widzenia (FOV) i proporcje obrazu wyznaczają rozmiar wirtualnego „okna” (viewportu), przez które kamera patrzy w scenę.
Każdy piksel to jeden punkt na tym oknie. Łączymy go z pozycją kamery — i to jest nasz promień D.
Pass: Układ kamery
Baza kamery: forward, right, up
Kamera w ray tracerze to coś więcej niż punkt w przestrzeni — to punkt plus orientacja. Potrzebujemy trzech wzajemnie prostopadłych wektorów jednostkowych, które razem tworzą lokalny układ współrzędnych kamery:
forward — kierunek, w który kamera patrzy (target − eye, znormalizowany).
Skąd wziąć right i up, mając tylko forward i globalny „up świata” (najczęściej (0, 1, 0))? Tu wchodzi nowa operacja: iloczyn wektorowy (cross product). W przeciwieństwie do iloczynu skalarnego z Rozdziału 2, który zwracał liczbę, A × B zwraca wektor — prostopadły jednocześnie do A i do B, zgodnie z regułą prawej ręki.
right = normalize(forward × up_world) up = right × forwardcross product: wektor prostopadły do obu wejściowych; drugie użycie gwarantuje, że up jest już idealnie prostopadły do forward
Camera.basisu, v, w
Trzy wzajemnie prostopadłe wektory jednostkowe — lokalny układ współrzędnych kamery.
Pass: FOV i proporcje
Viewport i pole widzenia
Pole widzenia (field of view, FOV) określa, jak „szeroko” kamera widzi scenę — ten sam parametr, który ustawiasz w atrybutach kamery w Mayi czy Blenderze. Umieszczamy wirtualny prostokąt (viewport) w pewnej odległości przed kamerą, wzdłuż forward — i to jego wymiary, nie parametry samej kamery, bezpośrednio wyznaczają kierunki promieni.
viewport_height = 2 · tan(FOV / 2) viewport_width = aspect · viewport_heightprzy odległości 1 wzdłuż forward; aspect = W / H (szerokość / wysokość obrazu w pikselach)
Większy FOV → większy viewport → promienie rozchodzą się szerzej — typowy „szeroki kąt”. Aspect ratio dba o to, żeby kwadratowe piksele nie rozciągały obrazu — inaczej kula wyjdzie jak jajko.
h = viewport_height, w = viewport_width. Ten sam kąt FOV z boku daje konkretny prostokąt z przodu — przesuń oba suwaki niezależnie.
Pass: Pixel → Ray
Od piksela do promienia
Mając bazę kamery i wymiary viewportu, każdy piksel (i, j) obrazu o rozdzielczości W×H zamieniamy na promień w czterech krokach. Najpierw normalizujemy współrzędne piksela do rozmiaru viewportu (czasem nazywane NDC — normalized device coordinates):
s = (2(i + 0.5)/W − 1) · viewport_width / 2 t = (1 − 2(j + 0.5)/H) · viewport_height / 2i, j — indeks piksela; +0.5 bierze środek piksela, nie jego róg; minus przy t, bo j rośnie w dół obrazu, a up w górę
Następnie łączymy to z bazą kamery, żeby dostać punkt na viewporcie w przestrzeni świata, i wyciągamy z niego kierunek:
P = eye + forward + s · right + t · up D = normalize(P − eye)P — punkt na viewporcie dla danego piksela; D — gotowy, znormalizowany kierunek promienia
Pixel.to.ray3 samples
Każdy piksel to inny punkt na tej samej płaszczyźnie viewportu — i inny promień D.
Z praktyki
Jeśli w Blenderze czy Mayi zmieniasz „focal length” i „sensor size” kamery, a nie FOV bezpośrednio — to i tak przelicza się na dokładnie ten sam kąt:
FOV = 2 · atan( sensor / (2 · focal) )
Dłuższy focal length = mniejszy FOV = bardziej „teleobiektywowy” kadr. To dlatego zbliżenia teleobiektywem spłaszczają perspektywę, a szerokie kąty ją mocno wyginają — to wciąż ten sam wzór z tego rozdziału, tylko schowany za innymi nazwami sliderów.
Słowniczek
Forward (look/view vector)
Kierunek, w który patrzy kamera.
Right, up
Wektory dopełniające bazę kamery, prostopadłe do forward i do siebie.
Iloczyn wektorowy (cross product)
Operacja A × B zwracająca wektor prostopadły do obu wejściowych.
Ortonormalna baza
Trzy wektory jednostkowe, wzajemnie prostopadłe — tu: forward, right, up.
Pole widzenia (FOV)
Kąt, jak szeroko kamera widzi scenę; wyznacza rozmiar viewportu.
Proporcje obrazu (aspect ratio)
W / H — zapobiega rozciąganiu obrazu przy niekwadratowych rozdzielczościach.
Viewport / NDC
Wirtualna płaszczyzna przed kamerą, na którą mapowane są piksele obrazu.
Co dalej
Mamy już wszystko, czego potrzeba, żeby wygenerować promień dla każdego piksela. W następnym rozdziale wracamy do kształtów, tak jak obiecane w Rozdziale 2: przecięcia z płaszczyznami i trójkątami — a więc z każdą siatką, którą wyeksportujesz z dowolnego DCC.