8-bitowa tekstura w przestrzeni liniowej zaczyna się rozpadać na pasy, zanim jeszcze trafi do renderera — dlaczego niektóre kanały muszą być float, a inne nie.
Intuicja „sRGB daje cieniom więcej kroków, więc jest lepiej” jest tylko połową prawdy i nie tłumaczy, dlaczego banding w ogóle się pojawia. Prawdziwy mechanizm jest inny: krzywa dekodowania sRGB→linear (w przybliżeniu potęga ~2,2) jest wypukła — bierze szeroki zakres wejściowych wartości 8-bitowych blisko zera i ściska je w wąski zakres wynikowy, bliski zera. Jeśli wynik tej dekodyzacji zostanie z powrotem zapisany albo przetworzony przy niskim bit depth, wiele różnych wartości wejściowych ląduje w tym samym, jednym „kubełku” wyjściowym — informacja o gradiencie w cieniach znika, zanim jeszcze zacznie się shading. Przy gamma ≈2,2 wartość wejściowa 8-bit musi sięgnąć ~21/255, zanim dekodowany wynik w ogóle przestanie być zerem — wszystko poniżej tego progu koliduje w jeden punkt.
To dlatego problem jest najbardziej widoczny właśnie wtedy, gdy ktoś zapisuje pośredni wynik (np. zdekodowaną teksturę z powrotem jako plik 8-bitowy), a nie wtedy, gdy dekodyzacja dzieje się raz, w locie, przy samplingu tekstury we float. Dopóki cała ścieżka od pliku do shadingu trzyma wysoką precyzję (16 albo 32 bit), banding z tego mechanizmu się nie pojawia — pojawia się dokładnie tam, gdzie ktoś w środku łańcucha zaokrągla z powrotem do 8 bitów.
Albedo/base color zwykle bezpiecznie zostaje 8-bitowe — to zwykle ostatnie mnożenie w łańcuchu, oglądane po transformie wyświetlania (Rozdział 6), więc drobna utrata precyzji rzadko jest widoczna. Displacement, promień/MFP SSS (Rozdział 16) i roughness sterujący ostrym specularem to co innego — to wartości pośrednie, przez które przechodzi dalsza matematyka (przesunięcie geometrii, mnożenie przez scale, podnoszenie do potęgi w GGX), więc każdy błąd kwantyzacji na wejściu compounduje się w dalszych krokach. Praktyczna zasada: im więcej matematyki dzieje się po odczycie danej mapy, tym wyższy bit depth jest tej mapy wart.
Dithering to dodanie niewielkiego, losowego szumu do obrazu tuż przed zaokrągleniem do niższego bit depth — szum rozbija ostre granice między pasami na ziarnistą, znacznie mniej widoczną teksturę, kosztem odrobiny szumu zamiast wyraźnych krawędzi. Narzędzie maketx (OpenImageIO, pipeline Arnolda) ma udokumentowaną flagę --dither, włączającą losowy dithering przy każdej konwersji do 8 bitów lub mniej. Odpowiednik po stronie RenderMana, txmake, w dostępnej dokumentacji nie wystawia analogicznej flagi — jeśli dithering jest tam potrzebny, trzeba go zrobić wcześniej, na etapie przygotowania tekstury, a nie liczyć na sam txmake.
| Format | Typ | Kiedy używać |
|---|---|---|
zip / piz (Arnold, EXR) | bezstratny | domyślny wybór maketx; obowiązkowy dla map danych — normal, roughness, displacement |
dwaa / dwab (Arnold, EXR) | stratny, blokowy | kolor/albedo w wysokiej rozdzielczości — potrafi przepołowić rozmiar pliku bez widocznej różnicy przy domyślnej jakości |
none / lossless / lossy (RenderMan, .tex) | trzy tryby wprost | format Pixara domyślnie lossless, EXR domyślnie zip; lossy dla float'owych tekstur, gdzie liczy się rozmiar bardziej niż pełna precyzja |
Jeden konkretny, udokumentowany powód, żeby nie sięgać po dwaa dla map jednokanałowych: ma znany błąd na obrazach monochromatycznych, gdy rozmiar kafla jest mniejszy niż 16 albo nie jest potęgą dwójki — Arnold w takim wypadku po cichu spada z powrotem na zip. To dodatkowy, praktyczny argument za regułą, która i tak wynika z poprzedniej sekcji: mapy danych (jednokanałowe, precyzyjne) — bezstratnie; kolor w wysokiej rozdzielczości — stratna kompresja jest akceptowalnym kompromisem.
Tekstury są gotowe — poprawnie zdekodowane, w odpowiednim bit depth. Rozdział 13 otwiera Część V i pokazuje, gdzie te mapy trafiają: do warstw aiStandardSurface i PxrSurface, tego samego stosu GGX i Fresnela z poprzedniej książki pod postacią konkretnych parametrów.