•  

    Hej mirki i mirabelki,

    Wczoraj na GitHub wypuściłem nową wersję mojega silnika do gier CLUSEK. Chciałem się z wami podzielić moją historią, która jest z tym związana. Ostrzegam z góry, że będzie to długi wpis w którym omówię implementację niektórych mechanizmów, które udało mi się wprowadzić w ostatnim czasie oraz moje perypetie z jedną firmą. Tym jednak zajmę się później, ponieważ na początek pełna lista zmian:
    - Dodano teren z teselacją;
    - Parametry teselacji mogą być zmieniane z pliku konfiguracyjnego oraz w czasie rzeczywistym w specjalnym oknie;
    - Wstępne generowanie siatki terenu (VB, IB) może być generowane równolegle;
    - Dodano cienie kaskadowe;
    - Dodano przesunięcie NearZ oraz usunięto starą metodę stałej głębokości dla wyliczenia cieni;
    - Bias może być zmieniony niezależnie dla każdej z kaskad;
    - Okno podglądu umożliwia zmianę kaskad;
    - Dodano nową bibliotekę do projektu - stb_image;
    - Dodano wyliczanie płaszczyzn bryły ściętej (frustum) dla kamery;
    - Naprawiono błąd z niepoprawnie działającym trybem siatki (wireframe mode) oraz wyboru ukrywania ścianek (culling mode);
    - Przeprowadzono niewielką optymalizację systemu renderowania;
    - Dodano dokumentację dla mapy wysokości (heightmap).

    Mimo to, że zmian jest sporo to skupię się na dwóch główynch zmianach, które pochłoneły większą część czasu dewelopmentu tej wersji silnika. Te dwie główne zmiany to bardziej zaawansowane cienie oraz teren. Zacznę od cieni, a następnie przejdę do terenu, ponieważ teren to niekończąca się opowieść.

    W tej wersji udało mi się zaimplementować stabilizowane kaskadowe cienie. Od teraz cienie nie drgają, ani trochę podczas obracania kamerą, co zdecydowanie polepsza doświadczenia płynące z korzystania z silnika. Dokonałem tego, dzięki przestudiowaniu świetnej prezentacji z GDC, którą znajdziecie tutaj oraz dokumentacji Microsoftu. Druga sprawa to kaskadowość cienii. Otóż cienie nie są renderowane raz, ale wielokrotnie. Dzięki tej technice można renderować cienie dla obiektów w różnej odległości od kamery, co daje nam ostre cienie blisko dla dużej ilości obiektów na scenie, również tych daleko od kamery. Co ciekawe korzystam z 4 kaskad. Wynika to z tego, że na obecną chwilę renderuje każdą z tych kaskad do tekstury 2048x2048, ale być może w przyszłości wrzucę to wszystko do jednej tekstury (tzw. atlasu) zamiast czterech różnych. Dzięki 4 kaskadom mógłbym to trzymać w optymalnej teksturze 2048x8192. Teoretycznie zabieg ten mógłby przynieść znaczącą poprawę wydajności silnika. Nie mniej na razie nie odczułem potrzeby takiej zmiany, a więc to raczej tylko teoria, ale jednak zaimplementowana np. w takim GTA V, więc dość wygrzana w poważnej produkcji.

    Jeżeli chodzi jeszcze o same cienie to dodałem również nowe opcje do "shadow preview", które umowożliwiają podglądanie poszczególnych kaskad. Niby taka głupotka, a mocno przyśpiesza dostrajanie parametrów dla cienii.

    No i przechodzimy do terenu. Temat rzeka, tak jak wspominałem wcześniej, ale postaram się go streścić i jednocześnie zachować sens. Także przed implementacją terenu wpadłem na pomysł, że zanim zacznę go implementować w silniku to poszukam sobie jakiegoś fajnego narzędzia do generowania map wysokości (heightmap) oraz tekstur przejść (splatmap), które będzie darmowe lub względnie tanie. Jak pomyślałem tak zrobiłem. Zacząłem od Houdini, ale szbko się odbiłem, ponieważ wersja, która by mi odpowiadała jest strasznie droga, także odpada. Naturalnym krokiem w tym momencie był Blender. O ile jest to świetny program do modelowania to do tworzenia terenu się nie nadaje. Po pierwsze zrobienie w nim czegoś sensownego to jest katorga. Po drugie nawet, jeżeli coś zrobimy naprawdę fajnego, ale uznamy, że chcieli byśmy wprowadzić jakąś zmianę to koniec. Zasadniczo płacz i ogromna inwestycja czasu. Także Blender odpada do terenu i dopisałem sobie do listy wymagań, że program musi mieć niedestrukcyjny proces tworzenia terenu. No więc szukałem dalej. Gdzie znaleźć program do terenu? Na GDC! Więc odpaliłem filmiki dużych firm jak robią teren i tam przewijał się jeden program - WorldMachine. Jest darmowa wersja, także ściągnąłem i przyznaję, że jest to genialny program do generowania terenu, ale jego prędkość działania oraz interfejs użytkownik pozostawiają wiele uwag. Zanim go jednak kupię, to chciałem jeszcze trochę poszperać i wtedy olśenienie. Trafiłem na program Gaea.

    Trafiłem na program, który wydawał się idealny. Tańszy od WorldMachine z tymi samymi możliwościami, a do tego z genialnym UI. Serio, interfejs jest tak dobry, jak byście korzystali z programu z programu o cenie 10x takiej. Także postanowiłem go kupić. Jako student miałem zniżkę ~50%, także skontaktowałem się z ich pomocą techniczną o taką licencję. Katastrofa... Nie wiem jak to opisać po prostu. Na początku musiałem udowodnić na milion sposobów, że jestem studentem. Korzystam z wielu programów na licencji studenckiej (np. Substance Painter) i nigdy nie byłem tak prześwietlany. No, ale okej. Może boją się wyłudzeń tańszej licencji. Potem jak przez to przeszedłem to dostałem klucz (plik z licencją do wgrania) do programu Gaea i co? Wyższa licencja, niż za nią zapłaciłem. Ech... Komedia po prostu. XD No, ale dobra nie jestem Januszem Biznesu. Dam im znać o tym, że dostałem wyższą licencję, niż zapłaciłem i zapytam, czy mi wystawią nowa, która będzie oczywiście zgodna z zamówieniem. I co? Odpisali, że dobrą licencję dostałem tylko jeden napis się źle wyświetla i mógł mnie wproadzić w błąd. No... może i tak jest, nie wiem, ale i tak to trochę śmieszne, że wyświetla się inna licencja, niż mam, ale co ja tam wiem? ¯\_(ツ)_/¯ W każdym razie mam korzystać z tej licencji, którą dostałem i się tym nie przejmować.

    Trochę śmiesznie, ale czy pomimo tej historii z licencją polecam program Gaea? TAK! I to jak! Jest to najlepszy program do generowania terenu. Fotka, którą widzicie dołączoną do tego wpisu to dość wczesna wersja terenu, którą chcę zaimplementować w moim silniku. Na mnie robi ten teren spore wrażenie. Mam nadzieję, że wy napiszecie w komentarzach co myślicie.

    No dobra, ale przejdźmy do implementacji, ponieważ zacząłem o tym pisać, więc płynnie przejdę od razu do tego tematu. Renderowanie dużego terenu to dość spory problem. Powiedzmy, że chcemy z tekstury 4096x4096 wygenerować siatkę. To będzie jakieś 16 milionów wierzchołków w VertexBufferze. Jest to dużo, ale teoretycznie nowoczesny PC obsłuży to. Nie mniej nie zapominajmy, że teren to tylko część sceny, a poświęcenie na nią 90% zasobów to moim zdaniem bezsens. Co jeżeli bym chciał użyć terenu 8192x8192 pikseli? No jest problem, bo wyrenderowanie 67 milionów wierzchołków to dramat i pchanie tego może być tak kosztowne, że nie uda się utrzymać 30 fps, jeżeli mielibyśmy tylko teren. Z tego właśnie powodu postanowiłem skorzystać z właściwości, którą na rynek wprowadził DX11, czyli teselacji. Dzięki teselacji możemy uzyskać blisko nas teren wyskoiej jakości (z dużą liczbą wielokątów), a daleko niższej jakości. Dodatkowo możemy na terenie zastosować technikę "frustum culling", która umożliwia ukrywanie niewidocznych części terenu. Teselacja umożliwia płynną zmianę wielokątów co klatkę. Nie jest to jednak bezproblemowe, a przynajmniej nie w mojej impelementacji. Po pierwsze Vertex Shader zmienił swoje działanie, a dodatkowo teren wykorzystuje dwa dodatkowe shadery, którymi są Hull Shader oraz Domain Shader. Ten pierwszy odpowiada za wyliczenie liczby podziałów kwadratu (w tym przypadku kwadratu, ale można korzstać z innych siatek), a drugi za wyliczenie pozycji nowo powstałych wielokątów. Problemem jest tutaj to, że musimy w Domain Shaderze samplować teksutrę wysokości, która jest wysokiej rozdzielczości. Dodatkowo, aby teren zachowywał się stabilnie dla gracza to normale muszą być wyliczane dla każdego piksela. Jest to ogromny koszt, ale nawet sama Nvidia pisze w swoich artykułach naukowych, że to najlepszy sposób na ukrycie zmiany ilości wielokątów. Piszą oni też, że wyliczanie wektorów normalnych w domain shaderze może koniec końców okazać się nie dość, że mniej stabilnym (normalne będą przeskakiwać) to jeszcze mniej wydajnym rozwiązaniem, ponieważ spora część terenu zostanie i tak zakryta przez inne obiekty, a jako, że wg. nich powinien on być renderowany na końcu to mało pikseli koniec końców zostanie wyliczonych.

    Także jak widzicie zrobienie teselacji to czytanie artykułów naukowych i przeglądanie ich kodu. Niestety ich kod jest często ogromny i zagmatwany, więc jest to masakrycznie wielka praca. Tutaj macie do filmu na YT jak działa to wszystko w akcji. Algorytmy YT nie lubią niestety dużej ilości linii, które szybko poruszają się po ekranie. Z tego właśnie powodu film trochę źle wygląda, ale może zobaczycie chociaż mniej więcej, jak to działa.

    Dla tych, którzy chcieliby zrozumieć lepiej technikalia bez zbędnego marudzenia to zapraszam do mojego artykułu w języku angielskim na Linkedin, który znajduje się tutaj. Kod źródłowy samego projektu natomiast znajdziecie tutaj, a wersję wykonywalną tutaj. Jeżeli natomiast ktoś chciałby wesprzeć moją wędrówkę po tej kamienistej drodze, gdzie ciągle wychodzą jakieś dziwne sytuacje to może zaplusować ten artykuł oraz dać like na Linkedin. Może uda mi się zebrać też dwie garści plusów za to, że nie byłem Januszem i chciałem zwrócić licencję i sam do nich napisałem. I tyle, jeżeli są literówki i błędy gramatyczne to przepraszam, ale piszę to od serca i nie mam czasu na redagowanie tego, a chcę na Wykop zawsze wrzucić nie kalkę z angielsksiego artykułu, a raczej oryginalną zawartość, która może kogoś zainteresuje i zmotywuje.

    #programowanie #gamedev #gry #directx #grafikakomputerowa #physx #clusek

    źródło: splash_feb_clusek.png