Czy rosyjska ruletka jest naprawdę odpowiedzią?


21

Widziałem, że w niektórych implementacjach Path Tracing, podejście zwane Russian Roulette jest używane do eliminacji niektórych ścieżek i dzielenia się ich udziałem wśród innych ścieżek.

Rozumiem, że zamiast podążać ścieżką, dopóki nie spadnie ona poniżej określonej wartości progowej wkładu, a następnie porzucając ją, stosuje się inny próg, a ścieżki, których wkład jest poniżej tego progu, są kończone z niewielkim prawdopodobieństwem. Pozostałe ścieżki mają swój wkład zwiększony o kwotę odpowiadającą dzieleniu utraconej energii z zakończonej ścieżki. Nie jest dla mnie jasne, czy ma to na celu skorygowanie błędu systematycznego wprowadzonego przez technikę, czy też cała technika jest konieczna, aby uniknąć błędu systematycznego.

  • Czy Russian Roulette daje obiektywny wynik?
  • Czy rosyjska ruletka jest konieczna do uzyskania obiektywnego wyniku?

To znaczy, czy użycie małego progu i po prostu zakończenie ścieżki w momencie, gdy spadnie poniżej tego progu, da bardziej stronniczy lub mniej stronniczy wynik?

Biorąc pod uwagę dowolnie dużą liczbę próbek, czy oba podejścia byłyby zbieżne na bezstronnym obrazie wynikowym?

Chcę zrozumieć podstawowy powód korzystania z rosyjskiej ruletki. Czy istnieje znacząca różnica w szybkości lub jakości?


Rozumiem, że energia jest rozdzielana między inne promienie w celu zachowania całkowitej energii. Czy jednak ta redystrybucja nie byłaby jeszcze możliwa, gdyby promień został zakończony po zejściu poniżej ustalonego progu, a nie o losowo określonej długości życia po osiągnięciu tego progu?

I odwrotnie, jeśli energia, która zostałaby utracona przez zakończenie promienia bez redystrybucji jej energii, i tak zostanie ostatecznie utracona (ponieważ promienie, do których jest ona redystrybuowana, również zostają ostatecznie zakończone), jak to poprawia sytuację?

Odpowiedzi:


26

Aby zrozumieć rosyjską ruletkę, spójrzmy na bardzo podstawowy znacznik ścieżki wstecznej:

void RenderPixel(uint x, uint y, UniformSampler *sampler) {
    Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);

    float3 color(0.0f);
    float3 throughput(1.0f);

    // Bounce the ray around the scene
    for (uint bounces = 0; bounces < 10; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
            color += throughput * float3(0.846f, 0.933f, 0.949f);
            break;
        }

        // We hit an object

        // Fetch the material
        Material *material = m_scene->GetMaterial(ray.geomID);
        // The object might be emissive. If so, it will have a corresponding light
        // Otherwise, GetLight will return nullptr
        Light *light = m_scene->GetLight(ray.geomID);

        // If we hit a light, add the emmisive light
        if (light != nullptr) {
            color += throughput * light->Le();
        }

        float3 normal = normalize(ray.Ng);
        float3 wo = normalize(-ray.dir);
        float3 surfacePos = ray.org + ray.dir * ray.tfar;

        // Get the new ray direction
        // Choose the direction based on the material
        float3 wi = material->Sample(wo, normal, sampler);
        float pdf = material->Pdf(wi, normal);

        // Accumulate the brdf attenuation
        throughput = throughput * material->Eval(wi, wo, normal) / pdf;


        // Shoot a new ray

        // Set the origin at the intersection point
        ray.org = surfacePos;

        // Reset the other ray properties
        ray.dir = wi;
        ray.tnear = 0.001f;
        ray.tfar = embree::inf;
        ray.geomID = RTC_INVALID_GEOMETRY_ID;
        ray.primID = RTC_INVALID_GEOMETRY_ID;
        ray.instID = RTC_INVALID_GEOMETRY_ID;
        ray.mask = 0xFFFFFFFF;
        ray.time = 0.0f;
    }

    m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}

TO ZNACZY. odbijamy się wokół sceny, gromadząc w miarę upływu czasu kolor i tłumienie światła. Aby być całkowicie matematycznie bezstronnym, odbicia powinny przechodzić w nieskończoność. Jest to jednak nierealne i, jak zauważyłeś, nie jest konieczne wizualnie; w większości scen, po pewnej liczbie odrzuceń, powiedzmy 10, wkład w końcowy kolor jest bardzo bardzo minimalny.

Tak więc, aby zaoszczędzić zasoby komputerowe, wiele znaczników ścieżki ma twarde ograniczenie liczby odrzuceń. To dodaje stronniczości.

To powiedziawszy, trudno wybrać, jaki powinien być ten twardy limit. Niektóre sceny wyglądają świetnie po 2 odbiciach; inne (powiedzmy z transmisją lub SSS) mogą zająć do 10 lub 20. 2 Odrzuceń od Big Hero Disneya 6 9 Odrzuceń od Big Hero Disneya 6

Jeśli wybierzemy zbyt nisko, obraz będzie wyraźnie stronniczy. Ale jeśli wybierzemy zbyt wysoką wartość, marnujemy energię obliczeniową i czas.

Jak zauważyłeś, jednym ze sposobów rozwiązania tego problemu jest zakończenie ścieżki po osiągnięciu pewnego progu tłumienia. To także dodaje stronniczości.

Zaciskanie po progu zadziała , ale znowu, jak wybrać próg? Jeśli wybierzemy zbyt duży, obraz będzie wyraźnie stronniczy, zbyt mały i marnujemy zasoby.

Rosyjska ruletka próbuje rozwiązać te problemy w sposób bezstronny. Po pierwsze, oto kod:

void RenderPixel(uint x, uint y, UniformSampler *sampler) {
    Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);

    float3 color(0.0f);
    float3 throughput(1.0f);

    // Bounce the ray around the scene
    for (uint bounces = 0; bounces < 10; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
            color += throughput * float3(0.846f, 0.933f, 0.949f);
            break;
        }

        // We hit an object

        // Fetch the material
        Material *material = m_scene->GetMaterial(ray.geomID);
        // The object might be emissive. If so, it will have a corresponding light
        // Otherwise, GetLight will return nullptr
        Light *light = m_scene->GetLight(ray.geomID);

        // If we hit a light, add the emmisive light
        if (light != nullptr) {
            color += throughput * light->Le();
        }

        float3 normal = normalize(ray.Ng);
        float3 wo = normalize(-ray.dir);
        float3 surfacePos = ray.org + ray.dir * ray.tfar;

        // Get the new ray direction
        // Choose the direction based on the material
        float3 wi = material->Sample(wo, normal, sampler);
        float pdf = material->Pdf(wi, normal);

        // Accumulate the brdf attenuation
        throughput = throughput * material->Eval(wi, wo, normal) / pdf;


        // Russian Roulette
        // Randomly terminate a path with a probability inversely equal to the throughput
        float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
        if (sampler->NextFloat() > p) {
            break;
        }

        // Add the energy we 'lose' by randomly terminating paths
        throughput *= 1 / p;


        // Shoot a new ray

        // Set the origin at the intersection point
        ray.org = surfacePos;

        // Reset the other ray properties
        ray.dir = wi;
        ray.tnear = 0.001f;
        ray.tfar = embree::inf;
        ray.geomID = RTC_INVALID_GEOMETRY_ID;
        ray.primID = RTC_INVALID_GEOMETRY_ID;
        ray.instID = RTC_INVALID_GEOMETRY_ID;
        ray.mask = 0xFFFFFFFF;
        ray.time = 0.0f;
    }

    m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}

Rosyjska ruletka losowo kończy ścieżkę z prawdopodobieństwem odwrotnie równym przepustowości. Dlatego ścieżki o niskiej przepustowości, które nie przyczynią się znacząco do sceny, są bardziej prawdopodobne, że zostaną zakończone.

Jeśli się tam zatrzymamy, nadal będziemy stronniczy. „Tracimy” energię ścieżki, którą losowo kończymy. Aby uczynić to bezstronnym, zwiększamy energię nieterminicznych ścieżek o prawdopodobieństwo ich zakończenia. To, oprócz losowości, sprawia, że ​​rosyjska ruletka jest bezstronna.

Aby odpowiedzieć na ostatnie pytania:

  1. Czy Russian Roulette daje obiektywny wynik?
    • tak
  2. Czy rosyjska ruletka jest konieczna do uzyskania obiektywnego wyniku?
    • Zależy od tego, co rozumiesz przez obiektywne. Jeśli masz na myśli matematykę, to tak. Jeśli jednak masz na myśli wizualnie, to nie. Musisz bardzo ostrożnie wybrać maksymalną głębokość ścieżki i próg odcięcia. Może to być bardzo uciążliwe, ponieważ może zmieniać się w zależności od sceny.
  3. Czy możesz użyć ustalonego prawdopodobieństwa (odcięcia), a następnie rozdzielić „utraconą” energię. Czy to jest bezstronne?
    • Jeśli używasz stałego prawdopodobieństwa, dodajesz uprzedzenie. Poprzez redystrybucję „utraconej” energii zmniejszasz stronniczość, ale nadal jest ona matematycznie tendencyjna. Aby być całkowicie bezstronnym, musi być losowy.
  4. Jeżeli energia, która zostałaby utracona przez zakończenie promienia bez redystrybucji jej energii, jest ostatecznie i tak tracona (ponieważ promienie, na które jest ona redystrybuowana, również są ostatecznie przerywane), jak to poprawia sytuację?
    • Rosyjska ruletka tylko zatrzymuje podskakiwanie. Nie usuwa całkowicie próbki. Również „utracona” energia jest uwzględniana w odbiciach aż do zakończenia. Tak więc jedynym sposobem na „utratę energii” będzie całkowicie czarny pokój.

Ostatecznie Rosyjska Ruletka jest bardzo prostym algorytmem, który wykorzystuje bardzo niewielką ilość dodatkowych zasobów obliczeniowych. W zamian może zaoszczędzić dużą ilość zasobów obliczeniowych. Dlatego tak naprawdę nie widzę powodu, aby go nie używać.


szczerze mówiąc, nie jestem do końca pewien to be completely unbiased it must be random. Myślę, że nadal możesz uzyskać wyniki matematyczne, stosując częściowe ważenie próbek, zamiast binarnego podania / upuszczenia narzuconego przez rosyjską ruletkę, po prostu ruletka zbiegnie się szybciej, ponieważ działa z próbkowaniem o doskonałej ważności.
v.oddou

9

Sama rosyjska technika ruletki jest sposobem na zakończenie ścieżek bez wprowadzania systemowego uprzedzenia. Zasada jest dość prosta: jeśli na danym wierzchołku masz 10% szansy na arbitralne zastąpienie energii 0, a jeśli zrobisz to nieskończoną liczbę razy, zobaczysz 10% mniej energii. Zwiększenie energii po prostu to kompensuje. Jeśli nie zrekompensujesz energii utraconej z powodu zakończenia ścieżki, rosyjska ruletka byłaby stronnicza, ale cała technika jest przydatną metodą unikania stronniczości.

Gdybym był przeciwnikiem, który chciałby udowodnić, że technika „kończenia ścieżek, których wkład jest mniejszy niż jakaś niewielka stała wartość”, jest tendencyjna, stworzyłbym scenę ze światłami tak przyciemnionymi, że ścieżki przyczyniające się są zawsze mniejsze niż ta wartość. Być może symuluję aparat przy słabym świetle.

Ale oczywiście zawsze możesz ujawnić użytkownikowi stałą wartość jako parametr, który można modyfikować, aby mógł upuścić ją jeszcze bardziej, jeśli ich scena jest słabo oświetlona. Pomińmy więc ten przykład przez minutę.

Co się stanie, jeśli rozważę obiekt oświetlony wieloma ścieżkami o bardzo niskiej energii, które są zbierane przez reflektor paraboliczny ? Ścieżki o niskiej energii niekoniecznie odbijają się bez rozróżnienia w sposób, który można całkowicie zaniedbać. Podobnie rozumowanie dotyczy np. Odcinania ścieżek po ustalonej liczbie odrzuceń: możesz zbudować scenę ze ścieżką, która odbija się od serii 20 luster przed uderzeniem w obiekt.

Inny sposób patrzenia na to: jeśli ustawisz wkład ścieżki na 0 po tym, jak spadnie ona poniżej ustalonego epsilon, jak skorygujesz tę utratę energii? Nie zmniejszasz po prostu całkowitej energii o ułamek. Nie wiesz nic o tym, ile energii zaniedbujesz, ponieważ odcinasz się przy pewnym progu wkładu, zanim poznasz inny czynnik: energię zdarzenia.


8

Aby rozwinąć niektóre inne odpowiedzi, bardzo prosty jest dowód, że rosyjska ruletka nie daje błędnego wyniku.

fa

fa=fa1++faN.

Zamień każdy termin na:

faja={1pjafajaz prawdopodobieństwem pja0Inaczej

Następnie:

mi[faja]=pja×1pjami[faja]+(1-pja)×0=mi[faja]

pjafa

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.