Jak wdrażane jest wygładzanie krawędzi w Ray Tracing?


13

Po przeczytaniu kilku artykułów w Internecie mogę śmiało powiedzieć, że nie mam pojęcia, jak działa wygładzanie przy użyciu Ray Tracing .

Rozumiem tylko, że pojedynczy piksel / promień jest podzielony na 4 subpiksele i 4 promienie zamiast 1 .

Czy ktoś mógłby wyjaśnić, jak to się robi (najlepiej za pomocą kodu)?


2
Czy mogę tylko zasugerować, aby spojrzeć na „supersampling” en.wikipedia.org/wiki/Supersampling i być może także en.wikipedia.org/wiki/Distribution_ray_tracing ?
Simon F

2
Mogę również polecić przeczytanie tego rozdziału PBRT pbrt.org/chapters/pbrt_chapter7.pdf i przeczytanie tego dokumentu lgdv.cs.fau.de/get/785 (który wyjaśnia inną technikę niż ta zastosowana w pbrt).
Tom van Bussel,

1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
maniak zapadkowy

Odpowiedzi:


12

Myślę, że można bezpiecznie powiedzieć, że istnieją dwa różne sposoby wykonywania AA w raytracingu:

1: jeśli masz obraz końcowy i obraz głębi, możesz zastosować prawie wszystkie istniejące techniki, które są używane w grach (FXAA itp.) Działają one bezpośrednio na obrazie końcowym i nie są powiązane z raytracingiem

2: druga metoda polega na uwzględnieniu wielu promieni dla każdego piksela, a następnie uśrednieniu wyniku. W przypadku bardzo prostej wersji pomyśl o tym w ten sposób:

  • renderujesz najpierw obraz o rozmiarze 1024x1024, jeden promień na każdy piksel (na przykład)
  • po renderowaniu przeskalujesz obraz do 512 x 512 (każde 4 piksele są powiększone do jednego) i możesz zauważyć, że krawędzie są gładsze. W ten sposób skutecznie wykorzystałeś 4 promienie dla każdego piksela na końcowym obrazie o wielkości 512x512.

Istnieją inne warianty tej metody. Na przykład możesz dostosować liczbę próbek dla pikseli znajdujących się bezpośrednio na krawędzi geometrii, co oznacza, że ​​dla niektórych pikseli będziesz mieć tylko 4 próbki, a dla innych 16.

Sprawdź linki w komentarzach powyżej.


Zasadniczo renderuję obraz do dużego rozmiaru, a kiedy zapisuję go do obrazu, zmniejszam go do mniejszego rozmiaru? To wydaje się dość proste :)! Czy to metoda superpróbkowania?
Arjan Singh,

2
@Arjan Singh tak, to jest en.wikipedia.org/wiki/Supersampling , ale jest to najwolniejszy z nich wszystkich, raytracing pozwala z łatwością wykonać adaptacyjne supersamplowanie, które może działać o wiele lepiej
Raxvan

14

Raxvan ma całkowitą rację, że „tradycyjne” techniki antyaliasingu będą działać w raytracingu, w tym wykorzystujące takie informacje, jak głębokość do antyaliasingu. Na przykład możesz wykonać tymczasowe wygładzanie krawędzi w śledzeniu promieni.

Julien rozwinął drugą pozycję Raxvana, która była wyjaśnieniem supersamplingu, i pokazał, jak to zrobić, wspominając również, że możesz losowo rozmieścić próbki w pikselu, ale następnie wchodzisz do kraju przetwarzania sygnału, który jest bardzo duży głębiej i zdecydowanie tak jest!

Jak powiedział Julien, jeśli chcesz wykonać próbek na piksel, możesz podzielić piksel na równomiernie rozmieszczonych punktów próbki (zasadniczo na siatce) i uśrednić te próbki.NNN

Jeśli to zrobisz, nadal możesz uzyskać aliasing. Jest to lepsze niż NIE robienie tego, ponieważ zwiększasz częstotliwość próbkowania, więc będziesz w stanie obsługiwać dane o wyższej częstotliwości (czyli mniejsze szczegóły), ale nadal może powodować aliasing.

Jeśli zamiast tego pobierzesz losowych próbek w pikselu, skutecznie zamieniasz aliasing na szum. Hałas jest łatwiejszy dla oczu i wygląda bardziej naturalnie niż aliasing, więc zwykle jest to preferowany wynik. Uważam, że jest to nawet idealna sytuacja z większą liczbą próbek, ale nie mam na ten temat więcej informacji):N

Gdy używasz po prostu „zwykłych” liczb losowych, takich jak otrzymywanych z rand () lub std :: uniform_int_distribution, jest to nazywane „białym szumem”, ponieważ zawiera wszystkie częstotliwości, na przykład sposób, w jaki światło białe składa się ze wszystkich innych kolorów (częstotliwości) ) światła.

Użycie białego szumu do losowego losowania próbek w pikselach ma problem polegający na tym, że czasami próbki zlepiają się razem. Na przykład, jeśli uśredniasz 100 próbek w pikselu, ale WSZYSTKIE kończą się w lewym górnym rogu piksela, nie dostaniesz ŻADNEJ informacji o innych częściach piksela, więc ostateczny wynikowy kolor piksela będzie brakowało informacji o tym, jaki powinien być kolor.

Lepszym podejściem jest zastosowanie czegoś zwanego niebieskim szumem, który zawiera tylko komponenty wysokiej częstotliwości (np. Jak światło niebieskie jest światłem wysokiej częstotliwości).

Zaletą niebieskiego szumu jest to, że uzyskuje się równomierne pokrycie piksela, tak jak w przypadku jednolitej siatki próbkowania, ale nadal występuje pewna przypadkowość, która zamienia aliasing w szum i daje lepiej wyglądający obraz.

Niestety, niebieski szum może być bardzo kosztowny do obliczenia, a najlepsze metody wydają się być opatentowane (co do cholery ?!), ale jeden sposób na zrobienie tego, wymyślony przez pixara (i opatentowany też, ale nie w 100% pewny) polega na utworzeniu równej siatki punktów próbki, a następnie losowym przesunięciu każdego punktu próbki niewielką ilość - jak losowa ilość pomiędzy plus lub minus połową szerokości i wysokości siatki próbkowania. W ten sposób otrzymujesz rodzaj próbkowania szumu niebieskiego za całkiem tanią.

Zauważ, że jest to forma próbkowania warstwowego, a także próbkowanie dysku Poissona, które jest również sposobem generowania niebieskiego szumu: https://www.jasondavies.com/poisson-disc/

Jeśli chcesz wejść głębiej, prawdopodobnie również zechcesz sprawdzić to pytanie i odpowiedzieć!

Jakie jest podstawowe uzasadnienie antyaliasingu przy użyciu wielu losowych próbek w pikselu?

Wreszcie, te rzeczy zaczynają błądzić w sferze śledzenia ścieżki Monte Carlo, która jest powszechną metodą wykonywania fotorealistycznego śledzenia promieni. jeśli chcesz dowiedzieć się więcej na ten temat, przeczytaj to!

http://blog.demofox.org/2016/09/21/path-tracing-getting-started-with-diffuse-and-emissive/


Gdy tylko zobaczyłem twoje imię, wiedziałem, że umieścisz coś na niebieskim szumie :)
Hubble

Ponieważ pojawiło się to ponownie jako aktywne: Aby wyjaśnić, dlaczego tak jest - wzorce próbkowania szumu niebieskiego przodują w integracji funkcji z większością energii skoncentrowanej na niskich częstotliwościach. Zatem stosując niebieski wzór szumu skutecznie zakładasz, że twój sygnał jest w większości płynny / zdominowany przez niskie częstotliwości. W przypadku sygnałów o wysokiej częstotliwości może to faktycznie przynieść efekt przeciwny do zamierzonego. Na szczęście naturalne obrazy koncentrują swoją energię na niskich częstotliwościach. Aby uzyskać więcej informacji, zobacz: sampling.mpi-inf.mpg.de/2019-singh-fourier.html
lightxbulb

7

Załóżmy, że dość typowa główna pętla raytracing:

struct Ray
{
    vec3 origin;
    vec3 direction;
};

RGBColor* image = CreateImageBuffer(width, height);

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        float x = 2.0 * (float)i / (float)max(width, height) - 1.0;
        float y = 2.0 * (float)j / (float)max(width, height) - 1.0;

        vec3 dir = normalize(vec3(x, y, -tanHalfFov));
        Ray r = { cameraPosition, dir };

        image[width * j + i] = ComputeColor(r);
    }
}

Jedną z możliwych modyfikacji, aby wykonać 4 próbki SSAA byłoby:

float jitterMatrix[4 * 2] = {
    -1.0/4.0,  3.0/4.0,
     3.0/4.0,  1.0/3.0,
    -3.0/4.0, -1.0/4.0,
     1.0/4.0, -3.0/4.0,
};

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        // Init the pixel to 100% black (no light).
        image[width * j + i] = RGBColor(0.0);

        // Accumulate light for N samples.
        for (int sample = 0; sample < 4; ++sample)
        {
            float x = 2.0 * (i + jitterMatrix[2*sample]) / (float)max(width, height) - 1.0;
            float y = 2.0 * (i + jitterMatrix[2*sample+1]) / (float)max(width, height) - 1.0;

            vec3 dir = normalize(vec3(x, y, -tanHalfFov) + jitter);
            Ray r = { cameraPosition, dir };

            image[width * j + i] += ComputeColor(r);
        }

        // Get the average.
        image[width * j + i] /= 4.0;
    }
}

Inną możliwością jest wykonanie losowego jittera (zamiast powyższego opartego na macierzy), ale wkrótce wkraczasz w sferę przetwarzania sygnału i potrzeba dużo czytania, aby wiedzieć, jak wybrać dobrą funkcję szumu.

Pomysł pozostaje ten sam: rozważ piksel jako niewielki kwadratowy obszar i zamiast strzelać tylko jednym promieniem przechodzącym przez środek piksela, strzelaj wieloma promieniami pokrywającymi cały obszar piksela. Im bardziej gęsty jest rozkład promienia, tym lepszy jest sygnał.

PS: Napisałem powyższy kod w locie, więc spodziewam się kilku błędów. Ma on jedynie na celu pokazanie podstawowego pomysłu.


Świetna odpowiedź! Jakie byłyby korzyści z zastosowania tej metody w porównaniu z metodą @Raxvan? Czy uzyskam te same wyniki, renderując do dużego rozmiaru, a następnie zmniejszając go do mniejszego rozmiaru?
Arjan Singh

Zasadniczo dzięki ray tracingowi nie trzeba renderować większego obrazu, a następnie zmniejszać go. Oznacza to, że masz dużo większą elastyczność: możesz mieć wiele próbek, możesz zmieniać liczbę próbek w zależności od regionu i po prostu nie musisz dodawać kroku przeskalowania.
Julien Guertault,

2
Jeśli chodzi o drżenie, okazuje się, że jest to dość złożony temat. Oto świetny artykuł analizujący najnowszą grafikę
.pixar.com/library/MultiJitteredSampling/paper.pdf

W powyższym przykładzie kodu użyto 4 próbek MSAA. Gdybym chciał zrobić 8x MSAA, jak wyglądałaby wtedy matryca? Co powinienem zmienić w pokazanej powyżej macierzy jittera?
Arjan Singh

1
@ SimonF Dziękujemy za wskazanie go. Naprawiony.
Julien Guertault

1

Aby dodać do powyższych odpowiedzi:

Rozproszone śledzenie promieni (Cook, Porter i Carpenter). Umożliwia jednoczesne wykonywanie przestrzennego AA, czasowego AA (tj. Rozmycie w ruchu) oraz ostrości / głębi ostrości. Najlepiej przeczytać artykuł, ale zasadniczo promieniom N, które strzelasz na piksel, można także przypisać pseudolosowe czasy (dla rozmycia w ruchu) i pozycje na obiektywie (aby uzyskać efekty ostrości).

Adaptacyjne superpróbkowanie: Na początku wystrzeliwujesz określoną liczbę promieni na piksel. Jeśli jednak promienie w lokalnej okolicy zwracają znacząco różne wyniki, możesz lokalnie zwiększyć częstotliwość próbkowania (powiedzmy 2x), aby poprawić wyniki. Możesz zdecydować się powtórzyć proces. Nie jest idealny, ponieważ obiekty wciąż mogą „spaść między próbkami”, ale jest prawdopodobnie tańszy niż jednolite próbkowanie z większą częstotliwością.

Śledzenie promieni za pomocą stożków (Amanatydy): Zamiast używać nieskończenie cienkiego promienia, każdy promień jest przybliżany przez stożek, powiedzmy o średnicy pokrywającej cały piksel (i trochę sąsiadów), co pozwala ocenić częściowe pokrycie . Ma również zalety w zakresie próbkowania / antyaliasingu tekstur, miękkich cieni i potencjalnie LOD modeli. Wykonałem implementację wiele lat temu - z pewnością jest to trudniejsze, ale unika wielu dodatkowych promieni. IIRC istniał również schemat zwany śledzeniem ołówka (ale moje pierwsze wyszukiwanie nie znalazło linku do artykułu)

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.