SPACE · linear vs sRGBDECODE · ^(1/2.2)HDR → LDR · tone mapGAP · not yet covered
Dodatek E / Głębiej: Kolor

Przestrzenie kolorów, gamma i tone mapping

Żaden z poprzednich 22 rozdziałów nie powiedział wprost: liczby, na których liczysz Lamberta, Fresnela i GGX, muszą być liniowe — a liczby w plikach tekstur i na ekranie liniowe nie są. To najczęstszy praktyczny błąd w całym renderingu.

TL;DR
Pass: Gamma

Linear light kontra gamma: 0,5 nie znaczy „w połowie jasne”

Oko ludzkie jest dużo czulsze na różnice w ciemnych partiach obrazu niż w jasnych — z tego powodu formaty obrazów od dekad kodują jasność krzywą gamma (~potęga 1/2,2), przydzielając więcej „poziomów” tam, gdzie oko i tak widzi więcej szczegółu. To kodowanie percepcyjne, nie fizyczne.

V_sRGB ≈ V_linear^(1/2,2)uproszczona aproksymacja; prawdziwe sRGB ma dodatkowy liniowy odcinek blisko czerni, ale kształt krzywej jest ten sam
Linear.vs.gammasame 0–1 range
0 1 (wartość liniowa) liniowe (fizyka) gamma / sRGB (plik, ekran)
Ta sama para liczb — gamma podnosi ciemne wartości wyżej, żeby zmieściło się w nich więcej odcieni.
Pass: Tekstury

Dlaczego trzeba dekodować teksturę, zanim wejdzie do wzoru

Tekstura zapisana jako PNG czy JPG (Rozdział 9) jest prawie zawsze w sRGB — bo to, co widzisz w przeglądarce plików, ma wyglądać dobrze na ekranie. Zanim jej kolor wejdzie jako kd do wzoru Lamberta z Rozdziału 5, trzeba go zdekodować z powrotem do liniowej przestrzeni (odwrotność gamma). Zapomnienie o tym kroku to jeden z najczęstszych błędów: render wygląda spłowiały albo zbyt kontrastowy, mimo że wzory są matematycznie poprawne.

Uwaga: to dotyczy tylko tekstur kolorowych (Base Color, Albedo). Mapy „danych” — normal maps, roughness, metallic, maski — nie reprezentują jasności światła, tylko surowe liczby, więc nie przechodzą dekodowania gamma. To dokładnie ta sama para: przełącznik „sRGB” / „Non-Color” przy każdym slocie tekstury w Blenderze, Unrealu i Unity.

Pass: Tone mapping

Z liniowego HDR do wyświetlacza 0–1

Wynik z path tracingu (Rozdział 15) jest liniowym HDR — jasne piksele (słońce, żarówka widziana z bliska) mogą mieć wartości znacznie większe niż 1, bez górnego limitu. Ekran przyjmuje tylko 0–1. Zwykłe obcięcie (clip) każdej wartości >1 do 1 daje brutalnie „wypalone”, płaskie highlighty.

Reinhard: color' = color / (1 + color)gładka kompresja w stronę 1, bez twardego progu — prosty, klasyczny operator z 2002 roku
Tone.mapping.curvesclip vs Reinhard vs ACES
0 wejście HDR (3,0) 1,0 obcięcie (clip) Reinhard ACES (filmic)
Obcięcie ucina wszystko nagle przy 1,0. Reinhard i ACES ściskają płynnie — ACES zachowuje więcej kontrastu w środkowych tonach.
Z praktyki

„Color Management” w Blenderze (Standard vs Filmic vs AgX), „OCIO” w Nuke, domyślny ACES w Unrealu i Arnoldzie — to wszystko wybór operatora tone mapowania z tego rozdziału. Klasyczny symptom „mój render wygląda płasko/wyblakło” w 90% przypadków to niedopasowanie color management, nie problem z oświetleniem.

Słowniczek
Linear light
Wartości proporcjonalne do fizycznej ilości światła.
Gamma encoding / sRGB
Percepcyjne kodowanie jasności do zapisu i wyświetlania.
Kolor kontra dane
Tekstury Base Color dekoduje się z sRGB; mapy danych (normal, roughness) nie.
Tone mapping
Ściskanie liniowego HDR do wyświetlanego zakresu 0–1.
Operator Reinharda
color/(1+color) — prosty, gładki operator tone mapowania.
ACES
Standard filmic tone mapowania używany domyślnie w większości silników.
Idź głębiej
Dodatek L — ACES cz.1 — problem interchange