Korzystanie z LUT w celu przyspieszenia modułu cieniującego Trig dla urządzeń mobilnych

10

Staram się, aby ten moduł cieniujący działał na naprawdę starym iDevice, a także na androidach.

Nawet gdy zredukuję kod do 2 funkcji sinusoidalnych na fragment, moduł cieniujący działa z prędkością około 20 klatek na sekundę.

Zastanawiałem się nad wyciągnięciem liścia z książki o starych technikach cieniowania i stworzeniem tablicy zawierającej garść predefiniowanych wartości wyzwalania i jakimś sposobem wykorzystania ich do przybliżenia modułu cieniującego.

W shaderze, który podłączyłem powyżej, już symuluję, że poprzez zaokrąglenie wartości wysyłanych do funkcji trig dalsza lewa myszka (gdy jest w dół) idzie w kierunku gorszej jakości shadera. To jest naprawdę całkiem fajne, ponieważ bardzo blisko lewej strony wygląda jak zupełnie inny i całkiem fajny shader.

W każdym razie mam dwa dylematy:

  1. Nie wiem, jaki jest najbardziej efektywny sposób, aby mieć w sobie tablicę wartości 360 w module cieniującym GLSL, stałym lub jednolitym?
  2. Nie mogę wymyślić, jak ustawić liczbę w zakresie, jak zwykle, gdybym chciał kąta od 0 do 360 (tak, wiem, że procesory graficzne używają radianów), zrobiłbym to w ten sposób.

    func range(float angle)
    {
       float temp = angle
       while (temp > 360) {temp -= 360;}
       while (temp < 0)   {temp += 360;}
       return temp;
    }
    

    Jednak GLSL nie zezwala na pętle while ani funkcje rekurencyjne.

J.Doe
źródło
Nie wiem, czy byłoby to praktyczne, ale pomogłoby to w nierównomiernym rozłożeniu wstępnie obliczonych wartości sinusoidy, z wartościami bardziej zgrupowanymi w miejscach, w których nachylenie krzywej sinusoidalnej jest najbardziej strome, a mniejszymi wartościami, w których się wyrównuje nie zmienia się tak bardzo? Czy pozwoliłoby to na większą dokładność tam, gdzie jest to potrzebne, bez konieczności przechowywania dużej liczby wartości?
trichoplax
5
Jeśli chodzi o # 2, wbudowana modfunkcja jest tym, czego chcesz. Można by napisać mod(angle, 360.0).
Nathan Reed,
1
@trichoplax genialny pomysł, ale nie wiem, jak byś wtedy mógł sprawdzić wartości na stole. Powiedzmy, że umieściliśmy je w szeregu, a niektóre z nich bardziej koncentrowały promieniowanie podczerwone. Jak znaleźć odpowiedni indeks?
J.Doe
6
Co powiesz na umieszczenie swoich wartości w 3-kanałowej fakturze 1D? W ten sposób możesz popełnić grzech, bo i opalać się za cenę pojedynczego wyszukiwania tekstur. Jeśli odwzorujesz kąt 0 - 2pi na 0 - 1 UV i użyjesz trybu powtarzania tekstury, nawet nie potrzebujesz wywołania mod, automatycznie „zawinie się”, a także możesz uzyskać przybliżone filtrowanie liniowe pomiędzy przechowywanymi wartościami, a nie przyciąganie do najbliższego.
Russ
3
Dużo czasu możesz wyeliminować funkcje trygonometryczne, gdy używasz ich w geometrii, nie używając kąta, ale rozpoczynając i kończąc parą sin / cos i używając tożsamości trygonowych dla pół kątów i tym podobnych.
maniak zapadkowy

Odpowiedzi:

8

[0,360)[0,2π)

  • [0,360][0,1]
  • Dodatkową zaletą jest to, że nie trzeba dostosowywać odstępów w pętli (chociaż i tak nie byłyby potrzebne pętle i można by po prostu użyć operacji modułu). Po prostu użyj GL_REPEATjako trybu zawijania dla tekstury, a zacznie się ona automatycznie od początku, gdy uzyskasz dostęp z argumentami> 1 (i podobnie w przypadku argumentów negatywnych).
  • I zyskujesz także korzyść z liniowej interpolacji między dwiema wartościami w tablicy, w zasadzie za darmo (lub powiedzmy prawie za darmo), używając GL_LINEARjako filtru tekstur, w ten sposób uzyskując wartości, których nawet nie zapisałeś. Oczywiście interpolacja liniowa nie jest w 100% dokładna dla funkcji trygonometrycznych, ale z pewnością jest lepsza niż brak interpolacji.
  • Możesz zapisać więcej niż jedną wartość w teksturze , używając tekstury RGBA (lub dowolnej liczby komponentów, których potrzebujesz). W ten sposób możesz uzyskać np. Sin i cos za pomocą pojedynczego wyszukiwania tekstury.
  • [1,1][0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;(lub nawet więcej składników dla drobniejszego ziarna). Pozwala to ponownie skorzystać z tekstur wieloskładnikowych.

Oczywiście należy jeszcze ocenić, czy jest to lepsze rozwiązanie, ponieważ dostęp do tekstur również nie jest całkowicie bezpłatny, a także jaka byłaby najlepsza kombinacja rozmiaru i formatu tekstury.

Jeśli chodzi o wypełnianie tekstury danymi i adresowanie jednego z twoich komentarzy, musisz wziąć pod uwagę, że filtrowanie tekstur zwraca dokładną wartość w środku teksla , tj. Współrzędną tekstury o połowę mniejszą niż rozmiar teksla. Więc tak, powinieneś generować wartości w .5tekstach , tj. Coś takiego w kodzie aplikacji:

float texels[256];
for(unsigned int i = 0; i < 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);

uniform float sinTable[361]glUniform1fv[0,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];
Chris mówi Przywróć Monikę
źródło
1
Oto interesujące rozszerzenie do przechowywania tabel wyszukiwania w teksturach. (Ab) używa N-liniowej interpolacji tekstur, aby uzyskać interpolację wyższego rzędu (czyli lepszą niż liniową) punktów danych, powierzchni, objętości i hiperwoluminów. blog.demofox.org/2016/02/22/…
Alan Wolfe