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.
