Dodatek Y nauczył Cię odpytywać panoramę przy każdym odbiciu ścieżki — poprawnie, ale za wolno na 60 klatek na sekundę. Silniki czasu rzeczywistego liczą tę samą całkę raz, zanim jeszcze zacznie się render, i potem tylko odczytują gotową odpowiedź.
Dodatek Y odpytuje panoramę na nowo przy każdym odbiciu ścieżki, dla każdego piksela, w każdej klatce renderu offline — który i tak liczy się minuty albo godziny, więc kilkaset dodatkowych odczytów tekstury nie robi różnicy. Silnik czasu rzeczywistego ma na całą klatkę, ze wszystkim, budżet rzędu milisekund. Nie może sobie pozwolić na importance sampling po luminancji panoramy dla każdego piksela na ekranie, w każdej klatce.
Rozwiązanie jest tym samym pomysłem, co w Dodatku AE: policz raz, użyj szybko. Zanim gracz w ogóle zobaczy scenę, silnik piecze (bake) odpowiedź na całkę oświetleniową dla danej panoramy — raz, offline albo przy starcie poziomu — i w czasie gry tylko odczytuje gotowy wynik, dwoma-trzema odczytami tekstury na piksel.
Dla światła rozproszonego (Lambert) potrzebujesz tylko jednej liczby na kierunek normalnej — napromieniowania E(N), czyli sumy światła dochodzącego z całej hemisfery, ważonej cosinusem, dokładnie jak AO w Dodatku AE, tylko bez binarnej widoczności — tu liczy się prawdziwa wartość radiancji z panoramy:
Ponieważ E(N) jest funkcją bardzo gładką (cosinus filtruje wszystkie wysokie częstotliwości panoramy — nawet ostre słońce rozmywa się w łagodny gradient), zwykle nie zapisuje się jej jako obrazu wcale. Zamiast tego kompresuje się ją do garstki liczb — sferycznych harmonik (Ramamoorthi i Hanrahan, 2001): 9 współczynników na kanał koloru wystarcza, żeby odtworzyć E(N) dla dowolnego N z błędem niezauważalnym gołym okiem. To dosłownie tysiące razy mniej danych niż pełna panorama.
Rozdział 11 pokazał, że rozkład GGX D(H) rozszerza się z roughness — gładki materiał ma wąski lob, chropowaty szeroki. Prefiltrowanie specular IBL robi dokładnie to samo, tylko z panoramą zamiast pojedynczego światła: konwoluuje ją wokół kierunku odbicia R (przy standardowym w real-time uproszczeniu N=V=R) rozkładem GGX o zadanym roughness, i zapisuje wynik jako kolejny poziom mipmapy tej samej tekstury — mip 0 to ostra, oryginalna panorama (roughness≈0), najwyższy mip to najbardziej rozmyta wersja (roughness≈1):
Prefiltrowana panorama z poprzedniej sekcji zależy tylko od R i roughness — ale prawdziwa specular IBL to pełna całka ∫f(l,v)·L_i(l)·(N·l)dl, która zależy też od F0 materiału i kąta patrzenia (N·V) osobno. Upieczenie tego wszystkiego w jedną teksturę per environment map byłoby ogromne i musiałoby się powtarzać dla każdej kombinacji materiału. Karis (2013, „Real Shading in Unreal Engine 4”) zauważył, że całkę da się rozbić w przybliżeniu na iloczyn dwóch niezależnych, dużo tańszych całek:
„Reflection Captures” (Unreal), „Reflection Probes” (Unity), „Irradiance Volumes” i cubemapy odbić w Blender Eevee — wszystkie to dokładnie ten bake: irradiance (sferyczne harmoniki albo mała cubemapa) plus prefiltrowany łańcuch mipów. Dlatego przesunięcie dużego mebla w edytorze wymaga „rebake lighting” — geometria się zmieniła, więc precalkulowana odpowiedź na całkę jest już nieaktualna. To też dlaczego takie probe'y łapią tylko statyczną scenę — ruchome obiekty potrzebują dodatkowo SSR albo ray-traced reflections, żeby w ogóle się odbijały.
Razem z Dodatkiem Y i Dodatkiem AE masz teraz pełny obraz: dlaczego IBL w ogóle działa (Y), jak jest wystarczająco szybkie na 60 klatek na sekundę (ten dodatek), i jak ten sam pomysł „policz raz, użyj szybko” wygląda zastosowany do samej geometrii zamiast światła (AE).