Dithering i inny postprocessing

Uwaga! Informacje na tej stronie mają ponad 3 lata. Nadal je udostępniam, ale prawdopodobnie nie odzwierciedlają one mojej aktualnej wiedzy ani przekonań.

18:26
Thu
11
Jun 2009

Dithering i inny postprocessing

Dziś dalej bawiłem się w pisanie efektów postprocessingu. Szczególnie zainteresował mnie Dithering. Ta technika była stosowana do polepszania jakości obrazów w czasach, kiedy komputery dysponowały ograniczoną liczbą dostępnych kolorów. W szerszym kontekście Dithering oznacza celowe wprowadzanie szumów do sygnału celem zniwelowania nieprzyjemnego efektu powstającego w wyniku kwantyzacji do pewnej, małej liczby możliwych wartości (np. tylko kilka bitów na składowe RGB piksela czy próbkę dźwięku).

Dzisiejszy DirectX już nawet nie obsługuje palet, ale pomyślałem sobie, że napisanie takiego efektu renderowanego w czasie rzeczywistym za pomocą shaderów to będzie ciekawa część poznawania zagadnień związanych z tematem efektów pełnoekranowych i Non-Photorealistic Rendering.

Zasada działania takiego efektu jest stosunkowo prosta. Jeśli w kodzie shadera HLSL mamy dany kolor piksela Color.rgb, to możemy zmniejszyć precyzję każdego kanału do tylko 2, 3, 4 itd... możliwych wartości (g_DownsamplingFactor) za pomocą takiej operacji:

float Bias = 0.5;
Color.rgb = floor(Color.rgb * g_DownsamplingFactor + Bias) /
  g_DownsamplingFactor;

To 0.5 służy do zaokrąglenia części ułamkowej, zamiast jej obcięcia. Jeśli teraz to przesunięcie 0.5 zastąpimy przesunięciem losowym w zakresie 0..1 (bez wartości skrajnych), to otrzymamy Dithering. Oto efekt:

Skąd wziąć w Pixel Shaderze liczby losowe? Rozwiązaniem jest samplowanie tekstury szumu, adresowanej pozycją piksela w przestrzeni ekranu.

// in float2 Pos : VPOS
Bias = tex2D(DitheringSampler, Pos / g_DitheringTextureSize) * 0.99 + 0.005;
Color.rgb = floor(Color.rgb * g_DownsamplingFactor + Bias) /
  g_DownsamplingFactor;

Udostępniam przygotowane przeze mnie tekstury szumu: Download/.../Noise. Najwięcej napracowałem się nad szumem Perlina, ale zapewniłem, że (tak jak pozostałe) jest rozkładalny sąsiadująco. Uwaga! Tekstury są plikami DDS w formacie L8 i XnView nieprawidłowo je podgląda. DirectX Texture Tool oczywiście nie ma z nimi problemów :)

Napisałem też inny efekt, który używa tekstury wolumetrycznej. Jak taką teksturę zrobić? Wystarczy DirectX Texture Tool. On potrafi wgrywać zwykłe tekstury na kolejne płaszczyzny tekstury wolumetrycznej, sześciennej czy na poziomy mipmap. W moim efekcie adresowanie X,Y jest na podstawie pozycji piksela w przestrzeni ekranu, natomiast współrzędna Z jest brana z jasności piksela.

float Grayscale = dot(Color.rgb, float3(0.299, 0.587, 0.114));

float2 TexXY = Pos/g_VolumeLookupTextureSize.xy;
float TexZ = Grayscale;
float4 Sample = tex3D(VolumeLookupSampler, float3(TexXY.x, TexXY.y, TexZ));
Color.rgb = Sample.rgb;

Tworząc odpowiednio przemyślane płaszczyzny takiej tekstury można uzyskać ciekawe efekty, jak ręczne szkicowanie czy rysunek techniczny (w tym drugim dodatkowo włączyłem wykrywanie krawędzi). Użyte tekstury udostępniam tutaj: Download/.../Volume Lookup.

Wadą tego rozwiązania jest, że tekstury są nałożone w przestrzeni ekranu i pozostają w miejscu. Dużo ładniej byłoby, gdyby się przesuwały i obracały razem z obiektem. Jak to rozwiązać? Po porostu podmieniać teksturę obiektu na specjalną? A co z jej skalowaniem? Ciekawą technikę wynaleźli Praun et al. (czy jak to się tam pisze ;) w artykule Real-Time Hatching (PDF, bo oryginalny link z tej strony nie działa, a także HTML). Otóż wygenerowali oni tekstury przedstawiające poszczególne poziomy zakreskowania, z których każda ma na swoich mipmpach odpowiednio większe (a raczej tej samej wielkości) kreski tak, że przy oddalaniu obiektu kreski pozostają na ekranie tak samo duże.

Ja pomyślałem sobie, że gdybym w swoim Deferred Shadingu zapisywał do G-bufora oprócz normalnych także wektory styczne (przynajmniej Tangent), to być może wiedziałbym, w którym kierunku na powierzchni obiektu nakładać teksturę i wtedy mógłbym rysować taki efekt kreskowania w przestrzeni ekranu (a także uzyskać wiele innych ciekawych efektów). Może kiedyś spróbuję...

Przy okazji napotkałem (nie pierwszy raz z resztą) problem generowania tekstury szumu w GIMP-ie. Za pomocą standardowych poleceń nie udało mi się wygenerować szumu o rozkładzie równomiernym. Znalazłem za to wtyczkę do GIMP-a: Noise Generator. Jest fajna, ma różne rozkłady (równomierny, Gaussa, Poissona itd.), ale ma też dużą wadę - autor nie przewidział losowania wartości z tak "szerokiego" zakresu, jak pełne 0..255!

Comments (0) | Tags: graphics rendering tools | Author: Adam Sawicki | Share

Comments

(No comments)

Post comment

Nick *
Your name or nickname
E-mail
Your contact information (optional, will not be shown)
Text *
Content of your comment
Calculate *
(* - required field)
STAT NO AD [Stat] [Admin] [STAT NO AD] [pub] [Mirror] Copyright © 2004-2017 Adam Sawicki
Copyright © 2004-2017 Adam Sawicki