Cieniowanie Toon / Cel o zmiennej szerokości linii?

15

Widzę kilka ogólnych podejść do robienia cieniowania cel:

  1. Powielanie i powiększanie modelu za pomocą odwróconych normalnych (dla mnie nie jest to opcja)
  2. Filtr Sobela / moduł cieniujący fragmenty zbliża się do wykrywania krawędzi
  3. Bufor matryc zbliża się do wykrywania krawędzi
  4. Metody cieniowania geometrii (lub wierzchołków) obliczające normalne ściany i krawędzie

Czy mam rację, zakładając, że podejście zorientowane na geometrię daje największą kontrolę nad oświetleniem i grubością linii, a także np. dla terenu, gdzie możesz zobaczyć linię sylwetki wzgórza łączącą się stopniowo w równinę?

Co jeśli nie potrzebuję oświetlenia pikselowego na moich powierzchniach? (I prawdopodobnie nie będę, ponieważ planuję używać opartego na komórkach oświetlenia / cieniowania opartego na wierzchołkach lub mapach tekstur.) Czy lepiej byłbym wtedy trzymać się podejścia opartego na geometrii, czy zamiast tego przejść na podejście do przestrzeni / fragmentów ekranu uprościć sprawę? Jeśli tak, to w jaki sposób mógłbym uzyskać „odbarwienie” wzgórz w obrębie sylwetki siatki, a nie tylko kontur całej siatki (bez szczegółów „atramentu” w tym zarysie? ( Sugestywne kontury , zagniecenia ).

Wreszcie, czy można tanio emulować podejście z odwróconą normą, używając modułu cieniującego geometrię? Martwię się tym, że z pewnością mógłbym powielić każdy wierzchołek i odpowiednio go skalować, ale jak podejść do odwracania normalnych i wyraźnego kolorowania w module cieniującym fragmenty?

To, czego chcę - różna grubość linii z natrętnymi liniami wewnątrz sylwetki ...

wprowadź opis zdjęcia tutaj

Czego nie chcę ...

wprowadź opis zdjęcia tutaj


EDYCJA: Dalsze badania wykazały, że ...

Ponieważ mam ogromną liczbę wierzchołków w terenie, nawet biorąc pod uwagę LoD oparte na odległości, ani odwrócone normalne, ani podejście oparte na shaderze geometrii (nawet z ubijaniem frustum) nie byłoby rozsądną opcją ze względu na złożoność obliczeniową związaną z duplikacją i skalowaniem wszystkich przesłane wierzchołki.

Biorąc pod uwagę, że nie potrzebuję oświetlenia na piksel w postaci cieniowania tonów stałych na powierzchniach terenu, rozważniejsze jest podejście oparte na normalnej twarzy - w przeciwnym razie wymóg prawidłowego oświetlenia powierzchni - są naturalnie dość drogie do obliczenia. Prawdą jest jednak, że dają one najwyższy stopień kontroli; na przykład możliwość cieniowania krawędzi za pomocą „artystycznych” pociągnięć: Piękna, ale nie bardzo przydatna w przypadku bardzo złożonego środowiska gry.

Bufory szablonów są czymś, czego wolałbym unikać, ponieważ wolałbym wykonywać całą pracę w shaderach. (Powyższy przykład z czerwonym konturem wykonano za pomocą bufora szablonów - stara szkoła).

Pozostawia to podejście cieniowania fragmentów obrazu. Złożoność obliczeniowa jest zredukowana do liczby fragmentów, a nie do liczby wierzchołków (w moim przypadku jest to 10–100 razy mniej operacji niż w przypadku modułu cieniującego geometrię). Wymaga to więcej niż jednego przejścia renderowania w celu wygenerowania bufora g (składającego się również z bufora normalnego i opcjonalnie również bufora głębokości), do którego możemy zastosować filtry nieciągłości (np. Operator Sobela). Nieciągłość głębokości umożliwia sugestywne kontury i fałdy. Moim jedynym sporem z tym podejściem jest niemożność zapewnienia dokładniejszej kontroli nad atramentowymi szerokościami krawędzi, chociaż przy odpowiednim algorytmie w module cieniującym fragmenty jestem pewien, że byłoby to możliwe.

Pytanie staje się teraz bardziej szczegółowe: jak dokładnie miałbym uzyskać otrzymywanie zmiennych szerokości krawędzi, szczególnie na zewnętrznej sylwetce, w shaderze fragmentów?

Inżynier
źródło
Zmienna grubość linii jest cechą podejścia geometrycznego. Nie można tego skutecznie uzyskać w żaden inny sposób. Ponieważ tego chcesz, nie widzę cieniowania pikseli jako „bardziej wydajnej metody”: liczba pikseli do przeszukania = (grubość linii * 2 + 1) ^ 2 - 1. To w zasadzie oznaczałoby, że twój moduł cieniujący będzie dużo (szacunek: 10x) wolniejszy, jeśli max. szerokość linii wynosi 2. Po prostu upuść wstępne ocenianie i wypróbuj metodę cieniowania geometrii.
snake5
@ snake5 Szczerze sugerujesz, że przejdę przez 150 milionów wierzchołków na aktualizację renderowania (około 25 milionów z utratą widoku) w GS? Tak naprawdę nie kupuję tego, ale dziękuję za wkład. Wróć do tego, co powiedziałem powyżej o liczbie wierzchołków i złożoności. Nawet przy twojej 10-krotnej figurze moduł cieniujący fragmenty przynajmniej by rysował, i prawdopodobnie zrobiłby o wiele lepszy.
Inżynier
25 milionów wierzchołków (liniowe odczyty / zapisy) vs. 23 miliony próbek tekstur (raczej słabo buforowana pamięć odczytuje + dekompresja danych docelowych renderowania) @ 1280x720. Nawiasem mówiąc, ograniczenie / zmniejszenie liczby wierzchołków jest znacznie łatwiejsze. Zwłaszcza w dzisiejszych czasach, gdy duże monitory (1920x1080 +) i konfiguracje z wieloma monitorami stają się bardzo popularne.
snake5
co powiesz na użycie tego samego algorytmu, który generuje czerwone kontury, a następnie przerzedzanie / powiększanie linii na podstawie ich głębokości?
Ali1S232,
@Gajoo, jak mówiłem w pytaniu, nie chodzi tylko o sylwetkę. Zwróć uwagę na natrętne linie za ramieniem dziewczyny na pierwszym zdjęciu. Są one funkcją podejścia odwróconego do normalnego i działają jak sugestywne kontury / zmarszczki. Potrzebuję tego samego.
Inżynier

Odpowiedzi:

4

Zdecydowałem się zastosować metodę cieniowania fragmentów poprzez filtrowanie nieciągłości bufora głębokości. Przyczyny tego są:

  1. Liczba wierzchołków świata jest bardzo, bardzo wysoka ze względu na ogromne odległości widzenia, nawet z siatką LoD;
  2. Wykonuję szereg innych operacji cieniowania fragmentów, takich jak rozmycie DoF, które mogą korzystać z tych samych struktur (próbkowanie / filtrowanie Gaussa) w tym samym przebiegu.

Po przetestowaniu powiedziałbym, że ze względu na złożoność w przyszłych projektach wybrałbym podejście oparte na geometrii. Powodem jest to, że (jak sugerują inni w komentarzach) podejście modułu cieniującego fragmenty do wykrywania krawędzi może być intensywne obliczeniowo, szczególnie w przypadku implementacji DoF, w których okrąg promienia pomieszania, a tym samym liczba próbek na fragment, może być dość wysoka. Na szczęście jest to mniej ważne dla shaderów konturu.

Inżynier
źródło
Myślę, że pójście z nieciągłością normalną dałoby lepsze wyniki w przypadku uzyskania płaskich normalnych. Możesz zmienić szerokość linii, pobierając próbki dalej.
Grieverheart,
@Grieverheart To jest zaakceptowana odpowiedź, ponieważ metoda, której używam, działa dobrze. Dzięki.
Inżynier
0

W rzeczywistości istnieje bardzo łatwe ogólne rozwiązanie, jeśli można zastosować efekt końcowy. Świetną rzeczą jest to, że nie ma znaczenia, jak wysoka jest twoja liczba. Renderuj mapę głębokości jako skalę szarości, a następnie utwórz kropkę w żądanym kolorze linii, gdy kontrast między dwoma sąsiadującymi pikselami jest większy niż wartość progowa. Możesz powiększyć kropkę zgodnie z kontrastem i / lub poziomem światła renderowanego obrazu.

Wynalazłem algorytm toonShader w 2000/2001, jeszcze przed tym francuskim kolesiem, który stworzył to rozwiązanie. Mój był oparty na faktycznej geometrii materiałów. Istnieją w zasadzie dwa sposoby na zrobienie tego: 1. spójrz na normalne, jeśli normalne dwie płaszczyzny połączone z linią są odwrócone i skierowane w stronę kamery, renderuj tę linię, a następnie możesz użyć głębokości, oświetlenia itp. Jako wskazówek dla głębokości linii. 2. Spójrz na renderowaną geomerty (więc po transformacji perspektywy) Weź każdy odcinek linii, jeśli wierzchołki płaszczyzn łączących znajdują się po tej samej stronie tego odcinka linii, renderujesz linię. Możesz wtedy zrobić to samo dla grubości linii jak w metoda 1. 3. Mógłbyś tworzyć kombinacje tych trzech technik, ale wspomniałem o pierwszej, ponieważ wskazałeś ogromną liczbę.

SnoepGames
źródło