Tone Mapping + Occlusion Query

27.10.2008 18:16 in OpenGL, grafika 3D, programowanie

Aby scena trójwymiarowa wyglądała bardziej efektownie możemy zastosować technikę HDR. Ponieważ jednak jest ona dosyć kosztowna obliczeniowo, to często stosowana jest jej ,,uproszczona'' wersja, nie operująca na teksturach zmiennoprzecinkowych. Dodatkowym elementem każdej w tych odmian HDR jest Tone mapping. Polega on na zasymulowaniu procesu ,,przyzwyczajania się'' oka do stałego natężenia światła. Dodatkowo należy zwrócić uwagę, że w przypadku ,,udawanego'' HDR zastosowanie TM jest nieodzowne -- w przeciwnym wypadku przy jasnych scenach wszystko zostanie wybielone i rozmazane.

Pytanie brzmi: jak można policzyć jasność sceny? Możemy na przykład wyliczyć średnią jasność piksela. Aby to osiągnąć, zwykle stosuje się następującą metodę:

  • skopiuj obraz do bufora o niskiej rozdzielczości
  • skopiuj bufor do pamięci operacyjnej
  • policz na CPU średnią jasność sceny

Algorytm sprawdza się, posiada jednak pewną -- istotną, według mnie -- wadę. Zmusza on bowiem kartę graficzną do pracy w ,,nietypowy'' dla niej sposób. Zazwyczaj dane nie są bowiem z buforów graficznych odczytywane i takie operacje są niestety bardzo mało wydajne. Wymyśliłem więc inną metodę, która korzysta z Occlusion Query. Dla tych, którzy o tym narzędziu słyszą pierwszy raz, krótkie streszczenie: jest to, w przypadku OpenGL, rozszerzenie, które udostępnia interfejs pozwalający zapytać kartę graficzną ile pikseli narysowała w trakcie pomiaru. Cóż, to również jest komunikacja od karty graficznej do CPU, ale zrealizowana w sposób bardziej zoptymalizowany. OQ najczęściej używa się do wyznaczania obszarów przesłoniętych (jak sugeruje jego nazwa), ale możemy zastosować je w prosty sposób:

Na początku bufor (teksturę, rendertarget) kopiujemy do nowego bufora o rozmiarze np. 128x128.

Zdjęcie naszej sceny

Następnie ustalamy obszary jasne. Przyjmijmy, że obszar o jasności piksela > 70% uznajemy za jasny. Usuńmy obszary ciemne.

Usunięcie ciemnych obszarów

Wyrenderujmy teraz nasz bufor tylko dla pikseli jasnych (oznaczonych tu kolorem białym).

Czarno-biały model jasności

Schemat przejścia wygląda więc tak:

Zadanie to możemy zrealizować przepuszczając obraz (1) przez następujący Pixel Shader:

void PS(float4 color_in : COLOR, out float4 color_out : COLOR)
{
  float f = (color_in.x + color_in.y + color_in.z) / 3;
  if (f > 0.7)
  {
    color_out = 1;
  }
  else
  {
    discard(true);
  }
}

Następnie wynik uzyskany z Occlusion Query podzielony przez kwadrat rozdzielczości będzie jasnością sceny (np. 5000 jasnych pikseli przy rozdzieloczości 128x128 współczynnik to ok. 0.30517). Mając tą wartość możemy w efektowny a zarazem kontrastowy sposób uwypuklić jasne obszary sceny.

Filtrowanie anizotropowe

27.10.2008 00:06 in grafika 3D, programowanie, OpenGL

Przy obecnym sprzęcie filtrowanie anizotropowe tekstur stało się już niemalże standardem. O tym, że jest ono potrzebne, nie trzeba raczej nikogo przekonywać. Zobaczmy sami:

Po lewej - filtrowanie włączone (4x), po prawej - wyłączone. Na ściankach pod dużym kątem widać doskonale różnicę w filtrowaniu.

Pytanie, jak tego efektu użyć w naszym programie OpenGL? Podobnie jak większość ,,nowoczesnych'' opcji, ustalamy je za pomocą rozszerzenia. W tym przypadku zwie się ono GL_EXT_texture_filter_anisotropic. Najłatwiej jest skorzystać z niego (jak również z wszystkich innych) za pomocą GLEW. Aby włączyć filtrowanie anizotropowe dla danej tekstury, musimy ją uaktywnić i ustawić dla niej następującą opcję:

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, v);

Gdzie v to nasza wartość AF (4 albo 8 powinno być w sam raz, na sprzęcie z górnej półki nie bójmy się ustawić 16). Maksymalną wartość można odczytać za pomocą zapytania:

float max;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max);

Więc do roboty i włączcie filtrowanie anizotropowe we wszystkich swoich projektach! :)

Visual Studio + CG

24.10.2008 18:23 in grafika 3D, programowanie, visual studio, cg

Pisania aplikacji 3D bez shaderów sobie nie wyobrażamy, prawda (jeżeli sobie wyobrażamy to polecam zakup GTX 280 -- leczy z tego bardzo szybko)? A owych shaderów nie będziemy przecież klepać w assemblerze jak jaskiniowcy. Do wyboru mamy trzy, a w zasadzie dwie możliwości: GLSL albo HLSL odpowiednio dla OGL/DX, albo CG, który jest uniwersalny (a przy okazji ma najfajniejszą nazwę).

To tyle tytułem zupełnie niepotrzebnego wstępu. Chciałbym bowiem ułatwić życie tym z Was, którzy dotychczas toczyli nierówną walkę z shaderami. Dlaczego nierówną? Bo domyślnie VS nie wspiera shaderów .cg (nie wiem jak jest z HLSL) i praca wygląda tak:

  • poprawiam kod shadera
  • uruchamiam program
  • czytam MessageBoxa o błędach kompilacji shadera długi na pół ekranu (optymistyczny przypadek) albo podziwiam natychmiastowy powrót do IDE (dla pesymistów)

A Visual Studio da się zmusić do ludzkiego traktowania shaderów CG!

  1. Po pierwsze, można kazać mu kolorować składnię. Wykorzystujemy tu fakt zgodności CG z C i plik z dodatkowymi słowami kluczowymi. W instalce Cg Toolkit są gotowe do ,,odkliknięcia'' pliki włączające kolorowanie. Jedyny mankament jest taki, że od teraz wszystkie użycia tex2D czy lerp będą kolorowe. Dla funkcji min i max jest to trochę wkurzające. Być może istnieje magiczna opcja ustalająca słowa kluczowe tylko dla plików .cg (ale aż tak głęboko nie szukałem).
  2. Co więcej, można podpiąc kompilator shaderów CG bezpośrednio pod VS. Dzięki temu poprawność składni będzie kontrolowana przez kompilator zupełnie jak w przypadku ,,zwyczajnych'' źródełek. Jak to zrobić?
    • do projektu dodajemy folder typu Shader Files (jeżeli jeszcze go nie mamy)
    • PPM na projekcie, Custom Build Rules...
    • New Rule File (Display Name, File Name i Directory z grubsza dowolne), Add Build Rule...
    • uzupełniamy okienko mniej więcej w taki sposób:

I już! Pliki .cg będą przepuszczane przez kompilator cgc i generować błędy podobne do tych z plików .cpp:

Cudo, prawda? (bez skojarzeń z CUDA, chociaż...)