Sfera była wygodnym punktem startu, ale prawie nic w produkcyjnej scenie nie jest sferą. Wracamy do obietnicy z Rozdziału 2: płaszczyzny i trójkąty — czyli dosłownie każda siatka, którą wyeksportujesz z Blendera, Mayi albo Houdiniego.
Płaszczyznę opisuje normalna N i dowolny punkt Q, który na niej leży. Punkt P należy do płaszczyzny, jeśli wektor od Q do P jest prostopadły do N — czyli ich iloczyn skalarny wynosi zero:
Podstawiamy nasz wzór promienia P(t) = O + t·D i rozwiązujemy — bez kwadratów, wprost:
Normalną trójkąta o wierzchołkach A, B, C liczymy tym samym iloczynem wektorowym, który poznałeś w Rozdziale 3:
Możemy teraz policzyć t tak jak dla płaszczyzny — ale to tylko połowa roboty. Trójkąt zajmuje wyłącznie fragment tej płaszczyzny, więc trzeba sprawdzić, czy trafiony punkt leży w jego granicach. Do tego służą współrzędne barycentryczne: każdy punkt trójkąta zapisujemy jako wagowaną kombinację jego trzech wierzchołków.
W praktyce nikt nie liczy najpierw normalnej, potem t, potem osobno u i v — to trzy niezależne kroki, które można połączyć w jeden szybki test. Algorytm Möllera–Trumbore (1997) robi to wszystko naraz, korzystając wyłącznie z iloczynów wektorowych i skalarnych, które już znasz:
↓ Dodatek K — skąd się bierze ten algorytmTo właśnie ta funkcja jest wołana najczęściej w całym programie: dla siatki z milionem trójkątów, dla każdego promienia z każdego piksela, z każdym odbiciem. Struktury akceleracyjne (BVH, o których będzie osobny rozdział) nie zmieniają tego testu — tylko drastycznie ograniczają liczbę trójkątów, które trzeba nim sprawdzić.
Wagi barycentryczne, które właśnie policzyłeś do testu przecięcia, to dokładnie ten sam mechanizm, który interpoluje UV, normalne i vertex color w każdym shaderze rasteryzującym trójkąt — GPU robi to sprzętowo przy każdym trójkącie. Ray tracer musi policzyć to samo ręcznie, dla każdego punktu trafienia.
Znak iloczynu skalarnego N·D mówi, z której strony trójkąta nadchodzi promień — ujemny oznacza trafienie w przednią (front-facing) stronę. Odrzucanie trafień w tylną stronę (backface culling) przyspiesza render i jest źródłem znanego artefaktu: „dziury” w jednostronnych powierzchniach, jak liście czy plane’y UI, widziane z drugiej strony.
Dlaczego w ogóle trójkąt, a nie na przykład quad, na którym zwykle modelujesz w Blenderze czy Mayi? Trzy punkty zawsze leżą na jednej płaszczyźnie — trójkąt z definicji jest płaski i wypukły. Cztery punkty takiej gwarancji nie mają: poruszony niezależnie jeden wierzchołek quada robi z niego powierzchnię niepłaską (non-planar quad), a wtedy pytanie „którą przekątną go przeciąć” ma dwie różne odpowiedzi, dające dwa różne kształty. Dlatego każdy silnik renderujący — Arnold, Cycles, V-Ray, sprzętowy ray tracing w Unrealu — triangularyzuje geometrię, zanim promień w ogóle ją zobaczy; nawet powierzchnie podziałowe kończą jako mikropoligony albo trójkąty w momencie testu przecięcia. Modelujesz w quadach dla wygody edycji — renderer i tak zawsze widzi wyłącznie trójkąty.
Umiemy już znaleźć punkt trafienia na prawdziwej siatce i wiemy, jaka jest tam normalna. W następnym rozdziale zamienimy to w kolor: modele oświetlenia Lamberta i Blinna–Phonga — pierwsze wzory, które faktycznie odpowiadają za to, jak „wygląda” materiał w Twoim renderze.