UV · (u,v) ∈ [0,1]²FILTER · BILINEARWRAP · REPEATMIP · AUTO
Rozdział 9 / Realizm i jakość obrazu

Tekstury i mapowanie UV

Każdy materiał od Rozdziału 5 miał jeden, płaski kolor kd. W tym rozdziale ten kolor zaczyna zależeć od tego, w które miejsce powierzchni trafił promień — a to właśnie robią tekstury.

TL;DR
Pass: UV

Druga, dwuwymiarowa współrzędna każdego wierzchołka

Oprócz pozycji (3D) i normalnej (3D), każdy wierzchołek siatki niesie zwykle też UV — parę liczb (u,v), zazwyczaj z zakresu [0,1]. To współrzędne, które ustawiłeś ręcznie w edytorze UV w Blenderze czy Mayi, „rozkładając” zakrzywioną powierzchnię 3D na płaski obraz.

W punkcie trafienia nie mamy UV od razu — musimy je zinterpolować z UV trzech wierzchołków, tymi samymi wagami barycentrycznymi, które policzył algorytm Möllera–Trumbore w Rozdziale 4. Uwaga na nazewnictwo: w Rozdziale 4 wagi barycentryczne nazwaliśmy u i v — to zwykłe skalary mówiące, jak blisko którego wierzchołka leży punkt trafienia (u ≥ 0, v ≥ 0, u+v ≤ 1). UV z akapitu wyżej to zupełnie inna para liczb: współrzędna na mapie tekstury, z zakresu [0,1] niezależnie od kształtu trójkąta. Nazwy się powtórzyły przypadkiem, więc poniżej piszemy wagi barycentryczne jako same u, v, a współrzędne tekstury zawsze z przedrostkiem — uvA, uvB, uvC, uv_hit:

uv_hit = (1−u−v)·uvA + u·uvB + v·uvCu, v — wagi barycentryczne z Rozdziału 4, nie współrzędne UV; uvA/uvB/uvC — UV trzech wierzchołków trójkąta; uv_hit — zinterpolowany wynik dla punktu trafienia
3D.to.UVsame weights
TRÓJKĄT W 3D TEN SAM TRÓJKĄT W UV A B C P (0,0) (1,1) uvA uvB uvC uv_P
Te same wagi (1−u−v, u, v) — inny kształt trójkąta po drugiej stronie. UV nie musi zachowywać proporcji 3D.
Pass: Teksel

Od UV do koloru piksela tekstury

uv_hit to para liczb z zakresu [0,1] — ale obraz tekstury to zwykła siatka pikseli (tu nazywanych tekselami, od „texture” + „pixel”) o konkretnych wymiarach Tw × Th, np. 2048×2048. Żeby wiedzieć, który dokładnie teksel odczytać, musimy przeskalować uv_hit z zakresu [0,1] do tej siatki:

x = u · Tw, y = (1 − v) · Th(1−v), bo v=0 zwykle oznacza dół tekstury, a rząd 0 obrazu to jego góra — ten sam problem odwrócenia osi co przy pikselach w Rozdziale 3

x, y rzadko wypadają na dokładny środek teksela — zwykle lądują gdzieś pomiędzy czterema najbliższymi. Filtrowanie najbliższego sąsiada po prostu zaokrągla (blokowe piksele z bliska). Filtrowanie biliniowe uśrednia cztery najbliższe teksele, ważąc je odległością — dokładnie tą samą logiką co interpolacja barycentryczna, tylko na kwadratowej siatce, nie trójkącie:

color = (1−fx)(1−fy)·C01 + fx(1−fy)·C11 + (1−fx)fy·C00 + fx·fy·C10fx, fy — część dziesiętna współrzędnej teksela; C00…C11 — cztery najbliższe teksele
Bilinear.weightsarea = weight
C01 C11 C00 C10 P fx fy
Waga każdego teksela to pole przekątnego prostokąta — im bliżej P, tym większa waga.

Osobny problem to wrapping: co zrobić, gdy u albo v wychodzi poza [0,1]? „Repeat” zawija wzór (u mod 1) — standard dla tekstur kaflowych. „Clamp” przycina do koloru z krawędzi. „Mirror” odbija naprzemiennie. To dokładnie te opcje, które widzisz jako Wrap Mode w Unity czy Unreal.

Wrap.modesrepeat / clamp / mirror
REPEAT CLAMP MIRROR wzór powtórzony identycznie poza [0,1]: rozmazany kolor krawędzi wzór odbity naprzemiennie [0,1] — bazowy tekst [0,1] — bazowy tekst [0,1] — bazowy tekst
Ten sam kafelek poza zakresem [0,1], trzy różne odpowiedzi: Repeat kopiuje wzór bez zmian, Clamp rozmazuje ostatni piksel krawędzi, Mirror odwraca wzór przy każdej granicy — dlatego szew w Mirror jest niewidoczny, a w Repeat czasem widać powtórzenie.
Pass: Proceduralne

Tekstury proceduralne: kolor bez obrazka

Alternatywa dla odczytu z obrazu: policz kolor prosto ze wzoru, na podstawie samego UV. Klasyczna szachownica:

checker(u,v) = (⌊u·N⌋ + ⌊v·N⌋) mod 2N — liczba pól szachownicy na jednostkę UV; wynik 0 albo 1 wybiera jeden z dwóch kolorów

Bardziej organiczne wzory — marmur, drewno, obłoki — buduje się z szumu Perlina: gładkiej, pseudolosowej funkcji, która wygląda naturalnie i nie ma szwów. To dosłownie budulec każdego node’a „Noise” w grafie materiału.

↓ Dodatek P — idea gradientowa
Procedural.compareformula vs noise
checker(u,v) — WZÓR SZUM PERLINA (feTurbulence)
Prawa strona to prawdziwy szum fraktalny (SVG feTurbulence) — nie ilustracja, tylko ten sam rodzaj funkcji, który liczy node „Noise”.

Kompromis jest praktyczny: tekstura proceduralna ma nieskończoną rozdzielczość i zero szwów, ale każde jej wywołanie to obliczenia — a skomplikowane, wielowarstwowe wzory (fBm, turbulencja) mogą być wolniejsze niż jeden odczyt z obrazu. Dlatego w produkcji proceduralne wzory często zapieka się (bake) do tekstury na koniec.

Z praktyki

Base Color, Roughness, Normal, Metallic — każdy slot mapy w grafie materiału robi dokładnie ten sam odczyt UV z tego rozdziału, tylko wynik trafia do innego parametru równania z Rozdziału 5, nie tylko do kd.

↓ Dodatek J — ile kosztuje policzenie tej sieci

Mipmapping — z góry wyliczone, mniejsze wersje tekstury — istnieje z tego samego powodu co antyaliasing z Rozdziału 8: gdy tekstura jest widziana z daleka albo pod kątem, jeden teksel odpowiada za wiele pikseli ekranu, i bez mipmap dostajesz migoczący moiré na przeskalowanej teksturze, nie tylko na geometrii.

↓ Dodatek G — Mipmapping
Słowniczek
UV / współrzędne tekstury
Dwuwymiarowe (u,v) przypisane do wierzchołka, mapujące go na obraz tekstury.
UV unwrapping
Rozkładanie zakrzywionej powierzchni 3D na płaski układ UV.
Teksel
Pojedynczy piksel obrazu tekstury.
Filtrowanie biliniowe
Ważone uśrednienie czterech najbliższych teksli.
Wrapping (Repeat / Clamp / Mirror)
Zachowanie UV poza zakresem [0,1].
Tekstura proceduralna
Kolor liczony wzorem z UV, bez obrazu.
Szum Perlina
Gładka, pseudolosowa funkcja bazowa dla organicznych wzorów.
Mipmapping
Wstępnie wyliczone, pomniejszone wersje tekstury, zapobiegające migotaniu z daleka.
Idź głębiej
Dodatek G — Mipmapping i filtrowanie tekstur Dodatek J — Złożoność sieci shaderów Dodatek P — Perlin cz.1 — idea gradientowa Dodatek AA — Triplanar mapping Dodatek AH — Displacement i subdivision w czasie renderu