2011-09-01

Kontrola oświetlenia sceny

Podczas renderowania sceny może nam wyjść, że jasność danego punktu (a konkretnie jednej ze składowych może przekroczyć dopuszczalny zakres). Mogą nam także wyjść liczby ujemne (w wyniku artefaktów po resamplowaniu, kiedy kernel filtru posiada składowe ujemne). Wartości ujemne powinniśmy zwyczajnie przyciąć do zera.

W tym momencie zakładam, że całe obliczenia dokonywane są na liczbach zmiennoprzecinkowych, gdzie 0 to zero, a 1 to biel.

Niezależnie od podanych niżej metod normalizacji jasności i tak warto podczas konwersji takiego obrazu do bitmapy sprawdzać zakres.

Pierwsze co możemy zrobić to przyciąć problematyczne składowe RGB. Kolory przestrzelonych jasności nie zostaną w tym wypadku zachowane.

Możemy więc cały kolor RGB przeskalować, kolor zostanie zachowany, a punkt zostanie przyciemniony. W tym wypadku zostanie zachwiana dynamika jasności. Jasność przejaśnionego obszaru będzie nieproporcjonalnie jasna w stosunku do otoczenia.

No to może po zrenderowaniu sceny skalujemy wszystkie punkty proporcjonalnie. Wadą jest nienaturalny wygląd sceny. Będzie ona zbyt ciemna, tylko po to by jakiś obszar nie był zbyt jasny.

Możemy zastosować prostą normalizację na całym wyrenderowanym obrazie: $C'=\frac{C}{C+1}$. Zdecydowanie metoda ta powinna dać najlepsze rezultaty.

Oprócz tego jest cała grupa metod do konwersji obrazów HDR. Tą konwersję nazywamy mapowaniem tonów. Algorytmy te są z reguły nieliniowe i starają się one oddać działanie oka i ludzkiego mózgu. Na ich zaimplementowanie przyjdzie czas...

Przykłady poszczególnych metod:

Przycięcie składowych RGB

foreach (var p in a_rect.EnumPixels())
{
    ColorFloat c = a_pixels.GetColor(p.X, p.Y);
    c = new ColorFloat(
        c.R > 1 ? 1 : c.R,
        c.G > 1 ? 1 : c.G,
        c.B > 1 ? 1 : c.B);
    a_pixels.SetColor(p.X, p.Y, c);
}



Skalowanie proporcjonalne składowych RGB

foreach (var p in a_rect.EnumPixels())
{
    ColorFloat c = a_pixels.GetColor(p.X, p.Y);
    float m = Math.Max(Math.Max(c.R, c.G), c.B);
    if (m > 1)
    {
        c = new ColorFloat(c.R / m, c.G / m, c.B / m);
        a_pixels.SetColor(p.X, p.Y, c);
    }
}



Skalowanie proporcjonalne wszystkich pikseli

float m = 0;

foreach (var p in new Rectangle(0, 0,
    a_pixels.Width, a_pixels.Height).EnumPixels())
{
    ColorFloat c = a_pixels.GetColor(p.X, p.Y);
    m = Math.Max(m, c.R);
    m = Math.Max(m, c.G);
    m = Math.Max(m, c.B);
}

if (m > 1)
{
    foreach (var p in new Rectangle(0, 0,
        a_pixels.Width, a_pixels.Height).EnumPixels())
    {
        ColorFloat c = a_pixels.GetColor(p.X, p.Y);
        c = new ColorFloat(c.R / m, c.G / m, c.B / m);
        a_pixels.SetColor(p.X, p.Y, c);
    }
}



Proste normalizacja

foreach (var p in new Rectangle(0, 0,
    a_pixels.Width, a_pixels.Height).EnumPixels())
{
    ColorFloat c = a_pixels.GetColor(p.X, p.Y);

    c = new ColorFloat(
        c.R / (c.R + 1),
        c.G / (c.G + 1),
        c.B / (c.B + 1));

    a_pixels.SetColor(p.X, p.Y, c );
}



Brak komentarzy:

Prześlij komentarz