Jaka jest użyteczność kwadratowego promienia i odwrotnego kwadratowego promienia do obliczeń oświetlenia?


16

Na jednym ze slajdów z „DirectX 11 Rendering in Battlefield 3” PowerPoint zauważyłem następujący kod:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

Nie rozumiem, dlaczego mieliby przechowywać kwadratowy promień, a nawet odwrotny kwadrat (który moim zdaniem jest po prostu promieniem 1-kwadratowym) zamiast po prostu przechowywać promień? Jak wykorzystują te dane w swoich obliczeniach? A co z lampkami stożkowymi i liniowymi? Ta struktura musi być tylko dla świateł punktowych, nie widzę, aby działała dla innych typów - nie ma wystarczającej ilości danych. Nadal chciałbym wiedzieć, jak używają tego kwadratu i invSquare.

AKTUALIZACJA: Ok, w końcu to rozumiem.

Oto klasyczne równanie tłumienia światła, które można łatwo znaleźć w sieci:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

Jest to stosunkowo kosztowne, ponieważ length(lightVector)faktycznie robi to:

length(lightVector) = sqrt(dot(lightVector, lightVector);

ponadto operacja podziału (/lightRadius)jest również dość kosztowna.

Zamiast obliczać tłumienie światła w ten sposób, możesz obliczyć go w następujący sposób, co byłoby znacznie szybsze:

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

gdzie invRadiusSqr można wstępnie obliczyć na poziomie procesora i przekazać jako stałą modułu cieniującego.

Ponadto uzyskuje się w ten sposób kwadratowe tłumienie światła (zamiast liniowego w poprzednim przypadku), co jest jeszcze lepsze, ponieważ światło IRL wykazuje kwadratowy spadek światła.

Dziękuję wszystkim za pomoc!


13
Wyściółka. Shadery są wyrównane do 16 bajtów. A jeśli spojrzysz na sposób formowania kodu, zobaczysz, że zostanie on zapakowany w dwa float4. Dlaczego nie przechowywać czegoś pożytecznego, gdy otrzymasz go bezpłatnie z pamięci podręcznej?
Tordin

Odpowiedzi:


23

Jest to po prostu rodzaj optymalizacji, biorąc pod uwagę, że invSqrRadius = 1/SqrRadiuszamiast obliczać odwrotny kwadratowy promień dla każdego światła za każdym razem, gdy po prostu buforuje je, przyczyną jest to, że podział jest zwykle operacją „powolną”, przynajmniej w porównaniu do mnożenia.

Ta optymalizacja jest szczególnie istotna:

  • gdy operacja jest wykonywana wiele razy dla każdego światła, więc buforowanie wartości zwolni dodatkowe cykle procesora / GPU
  • Zakładając, że czas dostępu do pamięci do odczytu wartości jest w rzeczywistości szybszy niż ponowne obliczenie.

Jeśli chodzi o sposób jego wykorzystania, nie jestem pewien ich konkretnej implementacji, ale jeśli chodzi o 1/sqrRadiusto, jest on po prostu używany do tłumienia światła, wypadania i ubijania. Ma to również znaczenie dla kierunkowego i punktowego, jedyną różnicą w przypadku reflektora jest to, że należy obliczyć współczynnik reflektora po zastosowaniu tłumienia . Jeśli chodzi o światła kierunkowe, takie jak słońce, zwykle nie ma on żadnego tłumienia ani spadku, więc sądzę, że zostanie zignorowany.

[EDYCJA] Żeby rozwinąć więcej, nie są to nieistotne dane. Lekkie natężenie napromienienia można obliczyć za pomocą następującego równania:

E = Phi / 4 * pi * rSqr;

Gdzie

E jest gęstością pola strumienia.

Phi to strumień promieniowania.

4 * pi * rSqr to pole powierzchni kuli.

To równanie wyjaśnia, dlaczego ilość otrzymanej energii spada z kwadratową odległością.

Kolejnym punktem jest to, że musisz obliczyć odległość między wierzchołkiem a światłem, aby obliczyć udział światła w określonym wierzchołku (ta wartość nie jest buforowana), wierzchołek może znajdować się w zasięgu światła lub poza nim, co prowadzi nas do następnego punktu gdzie Radius Squarejest przydatny do uboju.

Jeśli potrzebujesz praktycznego przykładu do obliczania lekkiego spadku i wygaszania, jest to szczególnie przydatne w przypadku odroczonych rendererów opartych na kafelkach, oto jeden przykład .


Klątwy, pokonałeś mnie o 7 sekund! ;) (a także z bardziej wyczerpującą odpowiedzią!)
Trevor Powell,

Dzięki za szczegółowy komentarz, szczególnie za link! Z tego, co zrozumiałem z tego ostatniego linku, Battlefield 3 nie przechowuje promienia, ale rzeczywistą odległość między źródłem światła a odbiornikiem światła, prawda? To jest wartość „d”, której używają w artykule.
cubrman

@ kubrman trudno spekulować, nie widząc kodu. Domyślam się, że jest to promień odwrotny Sqr. A używane przez nich równania mogą znacznie różnić się od artykułu.
concept3d

Ale powiedz mi, jak możesz zastosować kwadratowy promień światła odwróconego do kwadratu w obliczeniach oświetlenia? Każde źródło znalezione w sieci mówi mi, że muszę znaleźć ODLEGŁOŚĆ między powierzchnią odbiorczą a źródłem światła, podzielić to ostatnie przez pierwotny promień światła, A NASTĘPNIE wyrównać wynik. Gdzie kiedykolwiek użyłbyś kwadratowego promienia lub invSqrRadius? Wydaje mi się to zupełnie nieistotnymi danymi.
cubrman

1
@cubrman zaktualizował odpowiedź.
concept3d

6

invSqrRadius nie jest 1 - sqrRadius; to 1 / sqrRadius.

Oznacza to, że możesz pomnożyć przez invSqrRadius, zamiast dzielić przez sqrRadius (ponieważ dzielenie jest zwykle znacznie droższe niż mnożenie)


6

Inne odpowiedzi tutaj dotyczyły odwrotnego promienia kwadratowego, ale zamiast tego przyjrzę się kwadratowemu promieniu (która koncepcja dotknęła, ale uważam, że zasługuje na dalszą dyskusję).

Kwadraty są przydatne do porównań odległości. Wiemy, że obliczanie odległości między dwoma punktami wymaga pierwiastka kwadratowego, a pierwiastki kwadratowe są drogie do obliczenia, ale jeśli wszystko, co chcemy zrobić, to porównać odległości (aby znaleźć, która jest mniejsza lub większa, i zrobić coś interesującego na podstawie wynik) możemy wyrzucić pierwiastek kwadratowy.

Jeśli sqrt (x)> sqrt (y), to również przypadek, że x> y.

W przypadku światła kwadratowy promień jest taki sam, jak odległość między środkiem światła i jego maksymalnym zasięgiem - oczywiście kwadrat.

W przypadku obliczeń oświetleniowych można to wykorzystać w przypadku wczesnego wyjścia. Jeśli odległość między oświetlanym punktem a środkiem (kwadratem) światła jest większa niż promień kwadratu, punkt nie odbiera światła i nie musisz uruchamiać reszty obliczeń. Jest to zatem tylko optymalizacja (dość powszechna) - możemy użyć kwadratu promienia, aby wykonać porównanie odległości bez drogich pierwiastków kwadratowych, a kosztem jest tylko odjęcie i kropka.

Oczywiście nie wiem, czy właśnie do tego używa go BF3, ale spodziewałbym się, że nie jestem zbyt daleko od celu.


Więc jeśli dobrze cię zrozumiałem, kod będzie następujący: jeśli (kropka ((lightPos - surfacePos), (lightPos - surfacePos))> lightRadiusSqr) nie robi oświetlenia, prawda?
cubrman
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.