Kontury renderowania, chyba że renderujesz tylko kilkanaście znaków, pozostaje „nie do przejścia” ze względu na liczbę wierzchołków potrzebnych na znak do przybliżenia krzywizny. Chociaż zamiast tego pojawiły się metody oceny krzywych Beziera w module cieniującym piksele, cierpią one na to, że nie można ich łatwo wygładzić, co jest trywialne przy użyciu quada z teksturą mapy odległości, a ocena krzywych w module cieniującym jest nadal obliczeniowo znacznie droższa niż to konieczne.
Najlepszym kompromisem między „szybkim” i „jakością” są nadal teksturowane quady z podpisaną teksturą pola odległości. Jest bardzo nieznacznie wolniejszy niż zwykły quad o zwykłej fakturze, ale nie tak bardzo. Z drugiej strony jakość jest zupełnie inna. Rezultaty są naprawdę oszałamiające, są tak szybkie, jak to tylko możliwe, a efekty takie jak blask są również banalnie łatwe do dodania. W razie potrzeby technikę można również obniżyć do starszego sprzętu.
Zobacz słynną pracę Valve na temat tej techniki.
Technika ta jest koncepcyjnie podobna do tego, jak działają powierzchnie niejawne (metabelki i tym podobne), chociaż nie generuje wielokątów. Działa całkowicie w module cieniującym piksele i przyjmuje odległość próbkowaną od tekstury jako funkcję odległości. Wszystko powyżej wybranego progu (zwykle 0,5) jest „na wejściu”, wszystko inne jest „na zewnątrz”. W najprostszym przypadku, na 10-letnim sprzęcie nieobsługującym modułu cieniującego, ustawienie progu testu alfa na 0,5 zrobi to dokładnie (choć bez efektów specjalnych i antyaliasingu).
Jeśli ktoś chce dodać nieco więcej ciężaru do czcionki (faux pogrubienie), nieco mniejszy próg załatwi sprawę bez modyfikowania jednego wiersza kodu (wystarczy zmienić mundur „font_weight”). Aby uzyskać efekt blasku, po prostu traktuje się wszystko powyżej jednego progu jako „wejściowe”, a wszystko powyżej drugiego (mniejszego) progu jako „wyjściowe, ale w blasku”, a LERP między nimi. Antialiasing działa podobnie.
Dzięki zastosowaniu 8-bitowej wartości odległości ze znakiem zamiast pojedynczego bitu ta technika zwiększa efektywną rozdzielczość mapy tekstury 16-krotnie w każdym wymiarze (zamiast czerni i bieli używane są wszystkie możliwe odcienie, dlatego mamy 256 razy więcej informacje przy użyciu tego samego magazynu). Ale nawet jeśli powiększysz znacznie powyżej 16x, wynik nadal wygląda całkiem akceptowalnie. Długie proste linie w końcu staną się nieco kręte, ale nie będzie typowych „blokowych” artefaktów próbkowania.
Możesz użyć modułu cieniującego geometrię do generowania quadów poza punktami (zmniejsz przepustowość magistrali), ale szczerze mówiąc, zyski są raczej marginalne. To samo dotyczy instancji renderowania znaków, jak opisano w GPG8. Narzut instancji jest amortyzowany tylko wtedy, gdy masz dużo tekstu do narysowania. Zyski nie są moim zdaniem związane ze zwiększoną złożonością i brakiem możliwości obniżenia. Dodatkowo albo jesteś ograniczony liczbą stałych rejestrów, albo musisz czytać z obiektu bufora tekstur, który nie jest optymalny dla spójności pamięci podręcznej (a celem była optymalizacja na początek!).
Prosty, prosty stary bufor wierzchołków jest równie szybki (być może szybszy), jeśli zaplanujesz wysyłanie nieco wcześniej i będzie działał na każdym sprzęcie zbudowanym w ciągu ostatnich 15 lat. I nie ogranicza się do określonej liczby znaków w czcionce ani do określonej liczby znaków do renderowania.
Jeśli masz pewność, że czcionka nie zawiera więcej niż 256 znaków, tablice tekstur mogą być warte rozważenia w celu usunięcia przepustowości magistrali w podobny sposób, jak generowanie quadów z punktów w module cieniującym geometrię. Podczas korzystania z tekstury tablic współrzędne tekstur wszystkich quadów mają identyczne, stałe s
i t
współrzędne i różnią się tylko r
współrzędnymi, które są równe indeksowi znaków do renderowania.
Ale podobnie jak w przypadku innych technik, oczekiwane korzyści są marginalne kosztem niezgodności ze sprzętem poprzedniej generacji.
Istnieje przydatne narzędzie Jonathana Dummera do generowania tekstur odległości: strona opisu
Aktualizacja:
jak ostatnio podkreślono w Programowalnym ciągnięciu wierzchołków (D. Rákos, „OpenGL Insights”, s. 239), nie ma znaczącego dodatkowego opóźnienia ani obciążenia związanego z programowym wyciąganiem danych wierzchołków z modułu cieniującego w najnowszych generacjach GPU, w porównaniu do robienia tego samego przy użyciu standardowej stałej funkcji.
Ponadto najnowsze generacje procesorów graficznych mają coraz bardziej rozsądne rozmiary pamięci podręcznych L2 ogólnego przeznaczenia (np. 1536kiB na nvidii Kepler), więc można się spodziewać niespójnego problemu z dostępem podczas wyciągania losowych przesunięć dla kwadratowych narożników z faktu, że tekstura bufora jest mniej problem.
To sprawia, że pomysł pobierania stałych danych (takich jak rozmiary quadów) z tekstury bufora jest bardziej atrakcyjny. Hipotetyczna implementacja mogłaby zatem ograniczyć do minimum transfery PCIe i pamięci, a także pamięci GPU przy takim podejściu:
- Prześlij tylko indeks znaków (jeden na znak do wyświetlenia) jako jedyny sygnał wejściowy do modułu cieniującego wierzchołków, który przekazuje ten indeks
gl_VertexID
, i wzmocnij go do 4 punktów w module cieniującym geometrię, wciąż mając indeks znaków i identyfikator wierzchołka (to będzie „gl_primitiveID udostępniony w module cieniującym wierzchołki”) jako jedyne atrybuty i przechwyci to poprzez sprzężenie zwrotne transformacji.
- Będzie to szybkie, ponieważ istnieją tylko dwa atrybuty wyjściowe (główne wąskie gardło w GS), a poza tym na obu etapach jest ono zbliżone do „braku operacji”.
- Powiąż teksturę bufora, która zawiera, dla każdego znaku w czcionce, pozycje wierzchołka teksturowanego kwadratu względem punktu bazowego (są to w zasadzie „metryki czcionki”). Dane te można skompresować do 4 liczb na kwadraty, przechowując tylko przesunięcie lewego dolnego wierzchołka i kodując szerokość i wysokość wyrównanego do osi pola (przy założeniu, że półpłynne, będzie to 8 bajtów stałego bufora na znak - typowa 256-znakowa czcionka może całkowicie zmieścić się w 2 kB pamięci podręcznej L1).
- Ustaw jednolitą linię bazową
- Powiąż teksturę bufora z poziomymi przesunięciami. Te mogłyby prawdopodobnie nawet być obliczane na GPU, ale jest o wiele łatwiejsze i bardziej wydajne, aby tego rodzaju rzeczy na CPU, ponieważ jest ściśle sekwencyjne działanie i wcale trywialne (myślę o kerningu). Potrzebny byłby także kolejny sygnał zwrotny, który byłby kolejnym punktem synchronizacji.
- Renderuj wcześniej wygenerowane dane z bufora sprzężenia zwrotnego, moduł cieniujący wierzchołki wyciąga poziome przesunięcie punktu bazowego i przesunięcia wierzchołków narożnych z obiektów buforowych (używając pierwotnego identyfikatora i indeksu znaków). Oryginalny identyfikator wierzchołka przesłanych wierzchołków jest teraz naszym „prymitywnym identyfikatorem” (pamiętaj, że GS zamienił wierzchołki w kwadraty).
W ten sposób można idealnie zmniejszyć wymaganą szerokość pasma wierzchołków o 75% (amortyzowaną), chociaż byłby w stanie renderować tylko jedną linię. Jeśli ktoś chciałby być w stanie renderować kilka linii w jednym wywołaniu losowania, musiałby dodać linię bazową do tekstury bufora, zamiast używać jednolitego (zmniejszając przepustowość).
Jednak nawet przy założeniu zmniejszenia o 75% - ponieważ dane wierzchołków wyświetlające „rozsądne” ilości tekstu wynoszą tylko około 50-100kiB (czyli praktycznie zerodo procesora graficznego lub magistrali PCIe) - wciąż wątpię, aby dodatkowa złożoność i utrata kompatybilności wstecznej naprawdę były warte kłopotu. Zmniejszenie zera o 75% to wciąż tylko zero. Wprawdzie nie wypróbowałem powyższego podejścia i potrzeba więcej badań, aby uzyskać prawdziwie kwalifikowane oświadczenie. Ale jeśli ktoś nie jest w stanie wykazać naprawdę oszałamiającej różnicy w wydajności (używając „normalnych” ilości tekstu, a nie miliardów znaków!), Mój punkt widzenia pozostaje taki, że w przypadku danych wierzchołków prosty, zwykły stary bufor wierzchołków jest wystarczająco dobry być uważane za część „najnowocześniejszego rozwiązania”. To proste i jednoznaczne, działa i działa dobrze.
Odnosząc się już do „ OpenGL Insights ” powyżej, warto również zwrócić uwagę na rozdział „Rendering kształtu 2D według pól odległości” autorstwa Stefana Gustavsona, który wyjaśnia szczegółowo renderowanie pola odległości.
Aktualizacja 2016:
Tymczasem istnieje kilka dodatkowych technik, które mają na celu usunięcie artefaktów zaokrąglania narożników, które stają się niepokojące przy ekstremalnych powiększeniach.
Jedno podejście po prostu używa pól pseudo-odległości zamiast pól odległości (różnica polega na tym, że odległość jest najkrótszą odległością nie do rzeczywistego obrysu, ale do obrysu lub wyimaginowanej linii wystającej ponad krawędź). Jest to nieco lepsze i działa z tą samą prędkością (identyczny moduł cieniujący), wykorzystując tę samą ilość pamięci tekstur.
Inne podejście wykorzystuje medianę z trzech w trzykanałowych szczegółach tekstury i implementacji dostępnych w github . Ma to na celu poprawę w stosunku do i-lub hacków używanych wcześniej do rozwiązania tego problemu. Dobra jakość, nieco, prawie nie zauważalnie, wolniej, ale zużywa trzy razy więcej pamięci tekstur. Ponadto trudniej jest uzyskać dodatkowe efekty (np. Blask).
Wreszcie, przechowywanie rzeczywistych krzywych Beziera tworzących postacie i ocena ich w shaderze fragmentów stało się praktyczne , z nieco gorszą wydajnością (ale nie tak bardzo, że to problem) i oszałamiającymi wynikami nawet przy największych powiększeniach.
Demo WebGL renderujące duży plik PDF z tą techniką w czasie rzeczywistym dostępny tutaj .