Mamy kompletny model materiału z Rozdziałów 5–7. Ale jeden promień na piksel z Rozdziału 3 wciąż daje poszczekane krawędzie. Czas to naprawić — nie zmieniając ani jednego wzoru na kolor, tylko to, ile razy go liczymy.
W Rozdziale 3 każdy piksel dostał jeden promień, wystrzelony przez jego środek. To traktuje piksel jako punkt — a piksel to w rzeczywistości mały kwadrat obrazu. Gdy krawędź obiektu przechodzi przez ten kwadrat, próbka ze środka daje odpowiedź binarną: promień trafił albo nie trafił, bez żadnego stanu pomiędzy.
Wzdłuż ukośnej krawędzi ta binarność układa się w charakterystyczne „schodki” — aliasing. To nie błąd w Twoim kodzie przecięć z Rozdziału 4; to fundamentalne ograniczenie próbkowania sceny w jednym punkcie na piksel.
Prosta naprawa: zamiast jednej próbki w środku piksela, weź N próbek rozłożonych po jego obszarze, wystrzel promień z każdej, uśrednij wyniki.
Najprostszy rozkład to regularna siatka — podziel piksel na N×N komórek i strzelaj przez środek każdej. To już naprawia krawędzie z poprzedniego diagramu. Ale regularna siatka ma swój własny problem: jeśli scena zawiera regularny wzór — cienkie linie płotu, siatkę okienną, teksturę w kratkę — regularne próbkowanie może z nim „rezonować”, produkując pulsujący wzór zwany moiré. Naprawiasz jeden rodzaj aliasingu i przypadkiem tworzysz inny.
Rozwiązanie: zamiast strzelać dokładnie przez środek każdej komórki siatki, dodaj do pozycji małe, losowe przesunięcie — wciąż jedna próbka na komórkę (więc rozkład jest wciąż równomierny, tzw. stratified sampling), ale już nie w idealnie regularnej pozycji.
Ludzkie oko jest dużo bardziej tolerancyjne dla przypadkowego szumu niż dla regularnego wzoru — dlatego zamiana strukturalnego aliasingu na losowy szum jest realną poprawą, nawet gdy liczba próbek się nie zmienia.
Wzór z tego rozdziału mówi „weź N próbek i uśrednij”. Produkcyjny renderer robi dwie rzeczy inaczej — i obie siedzą w suwakach, które kręcisz najczęściej.
Po pierwsze: N nie jest stałe. Gładkie niebo jest gotowe po czterech próbkach; krawędź gałęzi na tle tego nieba potrzebuje stu. Adaptive sampling wystrzeliwuje najpierw garść próbek, mierzy, jak bardzo się między sobą różnią, i dosypuje kolejne tylko tam, gdzie rozrzut przekracza próg. Ustawiasz więc nie jedną liczbę, a trzy: minimum próbek, maksimum próbek i próg, poniżej którego piksel uznajemy za gotowy.
To dlatego podniesienie „max samples” z 256 na 512 często prawie nie zmienia czasu renderu — jeśli próg jest osiągany wcześniej, ten limit nigdy się nie aktywuje. I odwrotnie: obniżenie progu potrafi wydłużyć render kilkukrotnie przy nietkniętym maksimum.
Po drugie: to nie jest zwykła średnia. Próbki nie należą wyłącznie do „swojego” piksela — renderer waży je funkcją rozciągającą się poza jego granice. Ta funkcja to filtr rekonstrukcji:
Szerokość filtra to osobny suwak od liczby próbek i miesza się z nią w diagnozie: obraz „miękki mimo wysokich sampli” to najczęściej za szeroki filtr, nie za mało próbek. Zwiększanie N takiego obrazu nie wyostrzy — usunie tylko szum.
Slider „Samples” albo „AA Samples” w Arnoldzie, V-Rayu, Cyclesie czy Unrealu to dosłownie N z tego rozdziału. Więcej próbek = gładsze krawędzie i mniej szumu z innych stochastycznych efektów (miękkie cienie z Rozdziału 6, GI w dalszych rozdziałach) — ale czas renderu rośnie liniowo z N.
Produkcyjne renderery rzadko używają czystego losowego jittera — z prostego powodu: czysta losowość potrafi przypadkiem skupić kilka próbek blisko siebie w jednym rogu piksela, a zostawić pustkę w drugim. Przy małym N widać to jako nierówny, „grudkowaty” szum, mimo że każda próbka jest tak samo losowa jak każda inna.
Sekwencje o niskiej dyskrepancji (najpopularniejsza w produkcji: Sobol) rozwiązują to inaczej niż stratified sampling z tego rozdziału — zamiast dzielić piksel na sztywne komórki, generują punkty tak, żeby każdy fragment przestrzeni próbek dostał sprawiedliwy udział, bez luk i bez skupisk, dla dowolnego N, nie tylko dla N będącego kwadratem liczby całkowitej (jak siatka komórek). W praktyce zbiegają się do poprawnego wyniku szybciej niż czysto losowe próbkowanie — mniej próbek daje ten sam poziom szumu.
Blue noise gra w zupełnie inną grę: nie chodzi o szybszą matematyczną zbieżność, tylko o to, gdzie ląduje błąd w domenie częstotliwości. Oko jest dużo bardziej tolerancyjne na szum wysokoczęstotliwościowy (drobne, przypadkowe ziarno) niż na szum niskoczęstotliwościowy (większe, widoczne plamy) — ten sam mechanizm percepcyjny, na którym opiera się dithering w kompresji obrazu. Wzorce blue noise są skonstruowane tak, żeby przy tej samej liczbie próbek przesunąć błąd w stronę wysokich częstotliwości — wynik wygląda czyściej, nawet gdy wariancja jest identyczna jak przy losowym próbkowaniu.
Z perspektywy artysty żadna z tych technik nie wymaga implementacji — to zwykle jedna pozycja w rozwijanym menu „Sampler” (Sobol, Correlated Multi-Jittered, Progressive…), obok samego slidera „Samples”. Zasada z tego rozdziału zostaje ta sama: wiele próbek, uśrednionych, byle nie w regularnym wzorze — różnica jest tylko w tym, jak sprytnie te próbki są rozłożone.
Adaptacja i filtr nazywają się w każdym silniku inaczej, ale sterują dokładnie tym, co opisuje sekcja wyżej. W Arnoldzie: AA Samples plus Adaptive Sampling z AA Samples Max i Adaptive Threshold, a filtr to Filter Type (domyślnie gaussian o szerokości 2,0). W RenderManie próg nazywa się Pixel Variance, a domyślnym filtrem jest blackman-harris. W Karmie (Houdini/Solaris) to Min/Max Pixel Samples plus Noise Level. W Cyclesie — Max Samples i Noise Threshold.
Praktyczny wniosek z tych dwóch mechanizmów razem: gdy render jest wolny, sprawdź najpierw próg, nie maksimum próbek — to on decyduje, ile z tego maksimum faktycznie zostanie zużyte. A gdy obraz jest miękki mimo wysokich sampli, sprawdź szerokość filtra, bo liczba próbek nie ma z tym nic wspólnego.
Krawędzie mamy już gładkie. W następnym rozdziale zajmiemy się tym, co dzieje się na powierzchni: tekstury, mapowanie UV i sposoby, w jakie piksel obrazu trafia na konkretny punkt na mapie koloru czy normalnych.