Tomasz Wozniak´ - dsp.agh.edu.pldydaktyka:tomasz_wozniak_fin_fin.pdf · sa˛ze soba˛ ´sci ´sle...
Transcript of Tomasz Wozniak´ - dsp.agh.edu.pldydaktyka:tomasz_wozniak_fin_fin.pdf · sa˛ze soba˛ ´sci ´sle...
AKADEMIA GÓRNICZO-HUTNICZA IM.
STANISŁAWA STASZICA W KRAKOWIE
Wydział Inzynierii Mechanicznej i Robotyki
PRACA DYPLOMOWA
Inzynierska
Integracja biblioteki RAYA z silnikiem Unreal Engine 4
Tomasz WozniakInzynieria Akustyczna
19 stycznia 2016
Promotor pracy: dr inz. Bartosz Ziółko
Kraków. dnia.......................
Imię i nazwisko :
Nr albumu :
Kierunek studiów :
Profil dyplomowania :
OŚWIADCZENIE
Uprzedzony o odpowiedzialności karnej na podstawie art. 115 ust 1 i 2 ustawy z dnia 4
lutego 1994 r. o prawie autorskim i prawach pokrewnych (tj. Dz.U.z 2006 r. Nr 90, poz.
631 z późn.zm.) : „Kto przywłaszcza sobie autorstwo albo wprowadza w błąd co do
autorstwa całości lub części cudzego utworu albo artystycznego wykonania, podlega
grzywnie, karze ograniczenia wolności albo pozbawienia wolności do lat 3. Tej samej
karze podlega, kto rozpowszechnia bez podania nazwiska lub pseudonimu twórcy cudzy
utwór w wersji oryginalnej albo w postaci opracowania, artystyczne wykonanie albo
publicznie zniekształca taki utwór, artystyczne wykonanie, fonogram, wideogram lub
nadanie”, a także uprzedzony o odpowiedzialności dyscyplinarnej na podstawie art. 211
ust.1 ustawy z dnia 27 lip[ca 2005 r. Prawo o szkolnictwie wyższym (tj. Dz.U. z 2012 r.
poz. 572, z późn.zm.) „Za naruszenie przepisów obowiązujących w uczelni oraz za czyny
uchybiające godności student ponosi odpowiedzialność dyscyplinarną przed komisją
dyscyplinarną albo przed sądem koleżeńskim samorządu studenckiego, zwanym dalej
„sądem koleżeńskim”, oświadczam, że niniejszą pracę dyplomową wykonałem(-am)
osobiście i samodzielnie i że nie korzystałem (-am) ze źródeł innych niż wymienione w
pracy”.
.....................................................
podpis dyplomanta
Kraków, dn……………..
Imię i nazwisko:
Nr albumu:
Kierunek studiów:
Profil dyplomowania:
OŚWIADCZENIE
Świadomy/a odpowiedzialności karnej za poświadczanie nieprawdy oświadczam,
że niniejszą inżynierską pracę dyplomową wykonałem/łam osobiście i samodzielnie oraz
nie korzystałem/łam ze źródeł innych niż wymienione w pracy.
Jednocześnie oświadczam, że dokumentacja oraz praca nie narusza praw autorskich
w rozumieniu ustawy z dnia 4 lutego 1994 roku o prawie autorskim i prawach pokrewnych
(Dz. U. z 2006 r. Nr 90 poz. 631 z późniejszymi zmianami) oraz dóbr osobistych chronio-
nych prawem cywilnym. Nie zawiera ona również danych i informacji, które uzyska-
łem/łam w sposób niedozwolony. Wersja dokumentacji dołączona przeze mnie na nośniku
elektronicznym jest w pełni zgodna z wydrukiem przedstawionym do recenzji.
Zaświadczam także, że niniejsza inżynierska praca dyplomowa nie była wcześniej
podstawą żadnej innej urzędowej procedury związanej z nadawaniem dyplomów wyższej
uczelni lub tytułów zawodowych.
………………………………..
podpis dyplomanta
Kraków, ……………..
Imię i nazwisko:
Adres korespondencyjny:
Temat pracy dyplomowej inżynierskiej:
Rok ukończenia:
Nr albumu:
Kierunek studiów:
Profil dyplomowania:
OŚWIADCZENIE
Niniejszym oświadczam, że zachowując moje prawa autorskie , udzielam Akademii
Górniczo-Hutniczej im. S. Staszica w Krakowie nieograniczonej w czasie nieodpłatnej
licencji niewyłącznej do korzystania z przedstawionej dokumentacji inżynierskiej pracy
dyplomowej, w zakresie publicznego udostępniania i rozpowszechniania w wersji druko-
wanej i elektronicznej1.
Publikacja ta może nastąpić po ewentualnym zgłoszeniu do ochrony prawnej wyna-
lazków, wzorów użytkowych, wzorów przemysłowych będących wynikiem pracy inży-
nierskiej2.
Kraków, ...............… …………………………….. data podpis dyplomanta
1 Na podstawie Ustawy z dnia 27 lipca 2005 r. Prawo o szkolnictwie wyższym (Dz.U. 2005 nr 164 poz. 1365) Art. 239. oraz Ustawy z dnia 4 lutego 1994 r. o prawie autorskim i prawach pokrewnych (Dz.U. z 2000 r. Nr 80, poz. 904, z późn. zm.) Art. 15a. "Uczelni w rozumieniu przepisów o szkolnictwie wyższym przysługuje pierwszeństwo w opublikowaniu pracy dyplomowej studenta. Jeżeli uczelnia nie opublikowała pracy dyplomowej w ciągu 6 miesięcy od jej obrony, student, który ją przygotował, może ją opublikować, chyba że praca dyplomowa jest czę-ścią utworu zbiorowego." 2 Ustawa z dnia 30 czerwca 2000r. – Prawo własności przemysłowej (Dz.U. z 2003r. Nr 119, poz. 1117 z późniejszymi zmianami) a także rozporządzenie Prezesa Rady Ministrów z dnia 17 września 2001r. w spra-wie dokonywania i rozpatrywania zgłoszeń wynalazków i wzorów użytkowych (Dz.U. nr 102 poz. 1119 oraz z 2005r. Nr 109, poz. 910).
Kraków, dnia
AKADEMIA GÓRNICZO-HUTNICZA WYDZIAŁ INŻYNIERII MECHANICZNEJ I ROBOTYKI
TEMATYKA PRACY DYPLOMOWEJ INŻYNIERSKIEJ dla studenta IV roku studiów stacjonarnych
imię i nazwisko studenta
TEMAT PRACY DYPLOMOWEJ INŻYNIERSKIEJ:
Promotor pracy: [Tytuł, imię i nazwisko Promotora]
Recenzent pracy: [Tytuł, imię i nazwisko Recenzenta] Podpis dziekana:
PLAN PRACY DYPLOMOWEJ (Należy ustalić z Promotorem, np.:) 1. Omówienie tematu pracy i sposobu realizacji z promotorem. 2. Zebranie i opracowanie literatury dotyczącej tematu pracy. 3. Zebranie i opracowanie wyników badań. 4. Analiza wyników badań, ich omówienie i zatwierdzenie przez promotora. 5. Opracowanie redakcyjne.
Kraków, ...............… …………………………….. data podpis dyplomanta
TERMIN ODDANIA DO DZIEKANATU: 20 r.
podpis promotora
Akademia Górniczo-Hutnicza im. Stanisława Staszica Kraków, ................. Wydział Inżynierii Mechanicznej i Robotyki
Kierunek:
Profil dyplomowania:
[imię i nazwisko autora] Praca dyplomowa inżynierska [temat] Opiekun: [stopień naukowy, imię i nazwisko promotora]
STRESZCZENIE [Treść streszczenia, maksymalnie do końca strony, Times New Roman 12 pkt]
AGH University of Science and Technology Kraków, the............ Faculty of Mechanical Engineering and Robotics
Field of Study:
Specialisations:
[First name and family name of the Author] Engineer Diploma Thesis [Title of the project in English] Supervisor: [degree, first name and family name of the Supervisor]
SUMMARY [The summary content, must fit within the page limit Times New Roman 12 pkt]
Serdeczne podziekowania dla mojego promotora
dr inz. Bartosza Ziółko za cenne uwagi odnosnie pracy,
oraz
dla mgr inz. Ireneusza Gawlika za pomoc i inspiracje.
7
Spis tresci
Wstep 10
1 Unreal Engine 4 11
1.1 Wybrane aspekty silnika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.1 Edytor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.2 Architektura, model pamieci . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.3 Aktorzy i komponenty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.4 Mechanizm refleksji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.5 Serializacja, system asset-ów . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2 System audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.1 Sound Wave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.2 Sound Cue, Sound Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.2.3 Audio Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.2.4 Pogłos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2.5 Klasy audio i miksy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3 System wtyczek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2 RAYA 20
3 Opis integracji 20
3.1 Załozenia projektowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2 Ogólny schemat działania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3 Architekrura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4 RAYA-offline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4 Implementacja 25
4.1 Moduł gry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.1.1 Asset sceny akustycznej . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.1.2 Menadzer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.3 Komponent odbiornik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.4 Modyfikacja dzwieku w czasie rzeczywistym . . . . . . . . . . . . . . . . . 30
4.2 Moduł edytora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.1 Asset materiał akustyczny . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.2 Komponent materiału akustycznego . . . . . . . . . . . . . . . . . . . . . . 35
8
4.2.3 Konfiguracja RAYA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2.4 Eksport sceny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2.5 Wypalenie sceny akustycznej . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2.6 Kompresja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5 Zakonczenie 45
9
Wstep
Dzwiek, obok obrazu, od zawsze był nierozłaczna czescia wszelkiego rodzaju interaktywnych mul-
timediów, takich jak gry czy symulacje. Mimo tego, ze trudno sobie wyobrazic wciagajaca gre nie
posiadajacej oprawy dzwiekowej, aspekt ten jest, w porównaniu z obrazem, daleko w tyle. Na dzi-
siejszym rynku gier producenci przescigaja sie w oferowaniu w swoich produktach coraz to bardziej
realistycznej grafiki, jednoczesnie nie wprowadzajac zadnych ulepszen w aspekcie audio.
Obecnie, standardowa procedura tworzenia dzwieku w grach jest reczna konfiguracja róznych
jego aspektów i parametrów przez projektanta dzwieku. Pomijane przez to sa naturalne efekty aku-
styczne zwiazane z geometria wirtualnego swiata, takie jak odbicie czy dyfrakcja. W ostatnim okre-
sie, wraz z rozwojem technologii wirtualnej rzeczywistosci, na rynku komercyjnym zaczeły pojawiac
sie wyspecjalizowane aplikacje/biblioteki, których celem jest realistyczne modelowanie dzwieku w
trzech wymiarach w czasie rzeczywistym, np. 3Dception czy Oculus Audio SDK. Mimo, ze oferuja
one integracje z wiekszoscia najpopularniejszych narzedzi do tworzenia gier, korzystaja one z bardzo
uproszczonych modeli geometrycznych zamiast rzeczywistej geometrii sceny.
W niniejszej pracy zaprezentowany jest sposób i implementacja integracji biblioteki programi-
stycznej RAYA z popularnym na rynku gier silnikiem gier, Unreal Engine w wersji 4. Integracja
oznacza w tym przypadku mozliwosc wykorzystania funkcjonalnosci oferowanej przez biblioteke
RAYA z poziomu srodowiska Unreal Engine 4 za pomoca specjalnie napisanej w tym celu wtyczki.
Integracja taka pozwala na rozszerzenie mozliwosci systemu dzwiekowego Unreal Engine 4 o auto-
matyczna zmiane parametrów dzwieku w czasie rzeczywistym na podstawie rzeczywistej geometrii
sceny, materiałów akustycznych i aktualnego połozenia zródeł dzwieku oraz samego gracza.
Praca ta została podzielna na cztery rozdziały. Pierwszy z nich przybliza i opisuje architekture
oraz funkcjonalnosc silnika Unreal Engine 4 i wyjasnia kluczowe z punktu widzenia tej pracy ter-
miny. Drugi rozdział poswiecony jest ogólnemu opisowi zadaniu i mozliwosciach biblioteki RAYA.
W rozdziale trzecim przedstawiony został opis sposobu integracji: załozenia projektowe oraz archi-
tektura wtyczki. Własciwa implementacja została opisana w rozdziale czwartym.
W zwiazku z brakiem materiałów dotyczacych silnika Unreal Engine 4 w jezyku polskim, pod-
czas pisania tej pracy bazowano głównie na artykułach i dokumentacji angielskojezycznej. Tam gdzie
to mozliwe autor starał sie uzywac standardowych tłumaczen terminów. Wszystkie kluczowe sfor-
mułowania podane zostały zawsze z oryginalna wersja angielska.
10
1 Unreal Engine 4
Unreal Engine 4, w skrócie UE4, to wieloplatformowy silnik i srodowisko do tworzenia gier. Unreal
Engine 4 napisany jest wiekszosci w jezyku C/C++. Pierwsza wersja silnika została wydana w 1998
roku1. Obecnie cały kod silnika oraz towarzyszacych mu narzedzi jest open-source.
Ponizej przedstawione zostana wybrane aspekty silnika istotne z punktu widzenia tej pracy. Ze
wzgledu na bardzo złozona i rozbudowana architekture silnika, nie zostały one opisane w sposób
szczegółowy, równiez czesc powiazanych pojec została pominieta. Autor przyłozył jednak wszelkich
staran aby ich ogólny opis był dla czytelnika przejrzysty, a kluczowe zagadnienia zostały odpowiednio
wyjasnione.
1.1 Wybrane aspekty silnika
1.1.1 Edytor
Edytor jest podstawowym narzedziem developera tworzacego gre z uzyciem UE4. To za jego pomoca
tworzone sa poziomy, składajace sie na gre. Z poziomu edytora odbywa sie zarzadzanie własciwie
kazdym aspektem projektu. Tutaj nastepuje m.in. wyswietlanie i edycja sceny, testowanie rozgrywki,
rozmieszczanie i edycja wszelkiego rodzaju obiektów bedacych czescia gry oraz tworzenie animacji.
Innymi słowy, w edytorze zaczyna i konczy sie cały proces twórczy.
Graficzny interfejs edytora Unreal Engine 4 jest stworzony za pomoca platformy programistycznej
stworzonej specjalnie na potrzeby silnika o nazwie Slate.
1.1.2 Architektura, model pamieci
Fundamentalna czescia architektury silnika Unreal Engine 4 jest klasa UObject. Typ ten definiuje
szereg kluczowych funkcjonalnosci oferowanych przez silnik i znajduje sie na szczycie hierarchii
klas wszystkich obiektów. W Unreal Engine 4, własciwie kazdy obiekt bedacy czescia rozgrywki
jest typu dziedziczacego z UObject. Obiekty tej klasy sa m.in. zarzadzane przez odsmiecacz pamieci
(ang. garbage collection), biora udział w serializacji oraz mechanizmie refleksji (1.1.4).
1.1.3 Aktorzy i komponenty
Ponizej przedstawiony zostanie opis dwóch waznych z punktu widzenia tej pracy oraz działania sil-
nika (rozgrywki) terminów: aktor i komponent.1https://pl.wikipedia.org/wiki/Unreal_Engine
11
Rysunek 1: Okno edytora Unreal Engine 4. Zródło [8]
W terminologii Unreal Engine 4 aktor oznacza obiekt, który moze zostac umieszczony na scenie
[4]. Zachowanie i funkcjonalnosc aktora zdefiniowana jest przez komponenty. Komponent to pod-
obiekt aktora definiujacy pewna czesc jego zachowania/funkcjonalnosci [7]. Jak widac, oba te pojecia
sa ze soba scisle zwiazane. Kazdy aktor posiada co najmniej jeden komponent, tzw. komponent (ko-
rzen czy zródłowy?) który definiuje jego 3D-transformacje (połozenie, obrót i skale). Zarówno wiec
z technicznego punktu widzenia jak i uzytkownika, aktor to pewnego rodzaju kontener komponentów,
które wspólnie definiuja jego role w kontekscie gry. W Unreal Engine 4 wystepuje podział aktorów
ze wzgledu na rodzaj komponentu zródłowego: np. aktor reprezentujacy pewna statyczna geometrie,
Static Mesh Actor to aktor z komponentem zródłowym typu Static Mesh Component.
Aktorzy w Unreal Engine 4 posiadaja cykl zycia, który okresla zachowanie aktora od momentu
stworzenia jego instancji (automatycznie przez silnik badz na zadanie przez programiste) do momentu
jego zniszczenia (przez odsmiecanie pamieci). Konkretnie, cykl zycia aktora definiuje kolejnosc
oraz warunki wywoływania przez silnik poszczególnych funkcji interfejsu AActor (klasa bazowa dla
wszystkich aktorów, dziedziczaca z UObject) w zaleznosci od obecnego stanu rozgrywki i obecnego
stanu aktora.
W Unreal Engine 4 istnieja 3 podstawowe elementy hierarchii komponentów. Idac od góry hie-
rarchii (od najbardziej podstawowego) sa to:
• Actor Component (klasa UActorComponent) - baza dla wszystkich komponentów. Definiuje
12
funkcjonalnosc wspólna dla wszystkich komponentów. Komponenty z tej grupy nie posiadaja
transformacji oraz nie moga byc dołaczane do innych komponentów.
• Scene Component (klasa USceneComponent) - komponent posiadajacy transformacje. Kom-
ponenty sceny moga oddziaływac z innymi komponentami oraz byc dołaczane do innych kom-
ponentów w celu tworzenia hierarchii. Komponent zródłowy aktora jest tego typu.
• Primitive Component (klasa UPrimitiveComponent) - komponent posiadajacy graficzna repre-
zentacje w swiecie, na przykład w postaci geometrii (siatki) lub systemu czasteczek.
Wszystkie trzy wyzej wymienione klasy: UActorComponent, USceneComponent oraz UPrimi-
tiveComponent to klasy abstrakcyjne bedace baza dla wielu rodzaju komponentów dostepnych w
UE4. Pełna lista zawiera ponad 50 rodzajów komponentów oferujacych róznorodna funkcjonalnosc,
m.in komponenty systemu fizyki, sztucznej inteligencji, systemów czasteczek, systemu nawigacji,
obiektów na scenie i wiele innych. Naturalnie, istnieje mozliwosc definiowania własnych typów
komponentów z poziomu kodu C++ co zostanie wykorzystane w dalszej czesci pracy.
1.1.4 Mechanizm refleksji
Fundamentalna czescia silnika Unreal Engine 4, jest mechanizm refleksji. Cytujac Wikipedie, mecha-
nizm refleksji to "proces, dzieki któremu program komputerowy moze byc modyfikowany w trakcie
działania w sposób zalezny od własnego kodu oraz od zachowania w trakcie wykonania". Mecha-
nizm refleksji jest wykorzystywany przez wiele aspektów/modułów silnika: wyswietlanie własciwo-
sci obiektu w edytorze, system odsmiecania pamieci czy komunikacje miedzy systemem Blueprints
a kodem C++[3]. W zwiazku z tym, ze Unreal Engine 4 napisany jest w jezyku C++, który standar-
dowo nie wspiera zadnego rodzaju mechanizmu refleksji, Unreal Engine 4 implementuje swój własny
system refleksji, nazwany Unreal Property System [3].
Za proces budowania kodu zródłowego w silniku Unreal 4 odpowiada program Unreal Build Tool
(w skrócie: UBT). Przed rozpoczeciem procesu kompilacji kodu zródłowego przez kompilator C++,
UBT uruchamia tzw. Unreal Header Tool (w skrócie: UHT), którego zadaniem jest parsowanie kodu
zródłowego pod katem metadanych charakterystycznych dla systemu refleksji Unreal i wygenerowa-
nie specjalnego kodu na ich podstawie [12].
Programista chcac aby pisany przez niego kod widoczny był dla systemu refleksji, musi umiescic
w pliku nagłówkowym C++ zawierajacym definicje danego typu dołaczenie specjalnego pliku który
bedzie zawierac kod wygenerowany przez UHT:
# include " FileName . generated .h"
13
Gdzie FileName to nazwa oryginalnego pliku nagłówkowego. Od tego momentu mozliwe staje sie
stosowanie odpowiednich dla systemu refleksji makr w definicjach typów, pól i funkcji.
Z punktu widzenia tej pracy, najwazniejszy jest efekt zastosowania mechanizmu refleksji na wy-
swietlanie i obsługe własciwosci własnych typów przez edytor Unreal 4, dlatego tylko ten aspekt
zostanie tutaj szczegółowo omówiony.
Aby wartosc danego pola klasy/struktury mogła byc edytowana przez uzytkownika z poziomu
edytora, definicja danego pola musi byc poprzedzona makrem UPROPERTY() co spowoduje, ze dane
pole klasy bedzie traktowane przez silnik jako nowy obiekt typu UProperty1. Makro to przyjmuje
szereg opcjonalnych metadanych/specyfikacji definiujacych własnosci i zachowanie tego pola m.in w
edytorze.
Ponizej znajduje sie przykład definicji klasy z zastosowaniem mechanizmu refleksji Unreal Pro-
perty System.
1 UCLASS ( NotPlaceable )
2 class AFoo : public AActor
3 {
4 GENERATED_BODY ()
5
6 UPROPERTY ( EditAnywhere , meta =( ClampMin = "0.0", ClampMax = "1.0"))
7 float someEditableProperty ;
8
9 UPROPERTY ()
10 int someHiddenProperty ;
11
12 bool ordinaryMember ;
13 };
Do poinformowania systemu UHT, ze dana klasa ma brac udział w mechanizmie refleksji słuzy
makro UCLASS([opcjonalna lista specyfikacji]). Analogicznie dla typu strukturalnego, jest to
USTRUCT(...). Opcjonalna lista specyfikacji definiuje zachowanie klasy/struktury wobec róznych
aspektów edytora i silnika. Na przykład, specyfikacja NotPlaceable powoduje, ze obiekt danej klasy
nie moze zostac umieszczony na scenie. Lista dostepnych specyfikacji jest rózna dla struktur i klas
i o wiele krótsza dla tych pierwszych2. Obowiazkowe makro GENERATED_BODY() (linijka 4) ma za
1Typ UProperty jest odpowiednikiem zmiennej w systemie Unreal Property System. Inne odpowiedniki to m.in.UClass (odpowiednik klasy), UStruct (odpowiednik struktury), UFunction (odpowiednik funkcji, metody). Opis zadan ifunkcjonalnosci tych typów wykracza poza ramy tej pracy.
2W Unreal Engine 4 stosowanie struktur ogranicza sie do grupowania powiazanych ze soba danych (zmiennych)- własciwie wszystkie pozostałe typy, które definiuja pewne zachowanie zwiazane z rozgrywka/edytorem musza byc
14
zadanie "wstrzykniecie"dodatkowych funkcji/definicji typów do ciała klasy/struktury wymaganych
do poprawnego działania mechanizmu refleksji. Własnosc klasy someEditableProperty (linijka 7)
moze byc modyfikowane przez uzytkownika w panelu własciwosci w edytorze (specyfikacja Edi-
tAnywhere) i przyjmowac wartosci z zakresu 0-1 (metadane ClampMin oraz ClampMax). Z kolei
własnosc someHiddenProperty (linijka 10) jest niewidoczna w edytorze i modyfikowalna tylko z po-
ziomu kodu. Pole ordinaryMember (linijka 12) jest zwykła zmienna jezyka C++ i niewidoczna dla
systemu refleksji.
USTRUCT ()struct FReverbSettings{
GENERATED_USTRUCT_BODY ()
UPROPERTY ( EditAnywhere , Category =ReverbSettings )
uint32 bApplyReverb :1;
UPROPERTY ( EditAnywhere , Category =ReverbSettings )
class UReverbEffect * ReverbEffect;
UPROPERTY ( EditAnywhere , Category =ReverbSettings )
float Volume ;
UPROPERTY ( EditAnywhere , Category =ReverbSettings )
float FadeTime ;};
(a) (b)
Rysunek 2: Definicja typu (struktury) z widocznymi adnotacjami dla systemu refleksji Unreal Engine4 (a) oraz odpowiadajace mu okno własciwosci obiektu tego typu w edytorze (b)
1.1.5 Serializacja, system asset-ów
Mechanizm Unreal Property System umozliwia zapisanie stanu obiektów na dysk w procesie zwanym
serializacja. W terminologii UE4, taki obiekt bedacy na dysku i mozliwy do ponownego wykorzy-
stania to asset (z ang. asset). W postaci asset-ów przechowywane jest wszystko co moze byc czescia
projektu i gry w UE4: mapy, obiekty graficzne, dzwieki, animacje, materiały, tekstury, efekty i wiele
innych. Asset-y moga byc przenoszone miedzy projektami oraz edytowane w edytorze. W zwiazku
klasami i potomkami klasy bazowej dla wszystkich typów: UObject.
15
z tym developerzy moga sprzedawac i kupowac asset-y do wykorzystania w swoich produkcjach - do
tego celu słuzy sklep internetowy, Unreal Marketplace.
Serializacja, wraz z deserializacja (odtworzenie stanu obiektu na podstawie pliku) jest czescia
funkcjonalnosci oferowanej przez klase UObject. Domyslnie, tylko pola klasy biorace udział w me-
chanizmie refleksji (makro UPROPERTY) sa brane pod uwage podczas serializacji i deserializacji. 1.
W zwiazku z tym, ze asset nic innego niz reprezentacja obiektu (typu dziedziczacego z UObject) w
postaci pliku, w terminologii UE4 czesto uzywa sie tych dwóch pojec wymiennie i tak tez postapiono
na ramach tej pracy. Jest to dodatkowo uzasadnione faktem, iz uzytkownicy UE4 moga nie byc
programistami i nie znac reprezentacji asset-u po stronie kodu a mimo tego dokonywac ich tworzenia
oraz edycji. Autor rozróznia te dwa pojecia tam gdzie jest to istotne.
1.2 System audio
System audio w Unreal Engine 4 daje mozliwosc kontroli wszystkich podstawowych aspektów dzwieku
typowych dla silników gier. Jest wiec mozliwosc kontrolowania m.in głosnosci, wysokosci dzwieku,
tłumienia wraz z odległoscia czy definiowania obszarów w których na dzwiek nałozony zostanie dany
efekt pogłosowy. Oprócz tego, developer ma mozliwosc tworzenia złozonych dzwieków i efektów w
postaci tzw. Sound Cues za pomoca graficznego edytora.
Ponizej przedstawione zostana główne komponenty systemu audio Unreal 4, do których autor
bedzie wielokrotnie odnosic sie w pozostałej czesci pracy.
1.2.1 Sound Wave
Sound Wave reprezentuje plik dzwiekowy w srodowisku Unreal. Jest to asset, który zawiera w so-
bie zaimportowany z pliku sygnał audio o danej czestotliwosci próbkowania i konfiguracji kanałów.
Na ten moment, silnik Unreal 4 wspiera tylko importowanie nieskompresowanych plików 16-bit w
formacie WAV (Waveform Audio File Format). Oprócz podstawowych ustawien takich jak regulacja
głosnosci, wysokosci (pitch) i zapetlenia Unreal pozwala m.in na ustawienie parametrów kompre-
sji, globalnego efektu tłumienia, priorytetu a nawet zdefiniowanie transkrypcji tekstu mówionego
w danym pliku dzwiekowym (wykorzystywane do np. dialogów). Obiekt typu Sound Wave jest
współdzielony w silniku, to znaczy, ze zmiana parametrów wpłynie na wszystkie zródła dzwieku go
wykorzystujace.
1Uzytkownik ma mozliwosc nadpisania domyslnej implementacji serializacji i deserializacji przy tworzeniu własnegotypu.
16
1.2.2 Sound Cue, Sound Node
Sposób odtwarzania dzwieku w Unreal Engine 4 zdefiniowany jest poprzez assety typu Sound Cue.
Najwazniejsza czescia Sound Cue jest tzw. graf audio (ang. Audio Node Graph). Kazdy wezeł tego
grafu reprezentowany jest poprzez obiekt typu Sound Node, który z kolei reprezentuje pewna operacje
na sygnale audio. Kazdy wezeł moze miec rózna ilosc wejsc i wyjsc. Np. wezeł wezeł typu Wave
Player, którego zadaniem jest odtwarzanie dzwieku z assetu Sound Wave bedzie posiadac zero wejsc
i jedno wyjscie. Z kolei wezeł typu Delay (opózniajacy), którego zadaniem jest opóznienie sygnału
dzwiekowego bedzie posiadac jedno wejscie i wyjscie, a wezeł typu Mixer, który dodaje do siebie
sygnały - wiele wejsc i jedno wyjscie [10]. Wezły moga byc łaczone ze soba w rozmaity sposób
pozwalajac na tworzenie złozonych efektów. Warto w tym miejscu zaznaczyc, ze oprócz domyslnej
palety róznego rodzaju podtypów Sound Node, uzytkownik ma mozliwosc definiowania własnych, z
poziomu kodu C++ (wiecej na ten temat w dalszej czesci pracy). Edycji grafu uzytkownik dokonuje
w całosci wizualnie w oknie edytora Sound Cue (patrz Rysunek 3)
Rysunek 3: Okno edytora Sound Cue w Unreal Engine 4 z widocznymi przykładowym połaczeniemmiedzy roznymi rodzajami wezłów Sound Node. Zródło: [9]
1.2.3 Audio Component
Zródło dzwieku na scenie w Unreal Engine 4 reprezentowane jest przez komponent sceny (patrz
1.1.3) typu Audio Component. Zadaniem tego komponentu jest odtwarzanie w czasie rozgrywki
dołaczonego do niego assetu typu Sound Wave badz Sound Cue. Audio Component, jak kazdy inny
komponent w Unreal Engine 4, moze zostac dołaczony do kazdego rodzaju aktora. W zaleznosci od
ustawien i od połozenia komponentu (aktora) wzgledem słuchacza (gracza) bedzie zalezec poziom
17
tłumienia oraz połozenie zródła dzwieku w panoramie.
1.2.4 Pogłos
W Unreal Engine 4, dodanie pogłosu odbywa sie wyłacznie za pomoca tzw. Audio Volumes.Audio
Volume definiuje obszar na scenie (np. szescienny) do którego przypisany jest pewien statyczny efekt
pogłosu, który zostanie nałozony na wszystkie dzwieki w momencie pojawienia sie gracza wewnatrz
tego obszaru. Mechanizm ten pozwala na łatwy i intuicyjny podział swiata gry na obszary o róznym
pogłosie i jest bardzo popularny wsród silników gier - w ten sam sposób funkcjonuja np. Reverb
Zones w silniku Unity 3D 1.
Asset reprezentujacy efekt pogłosu oferuje kontrole szeregu parametrów charakterystycznych dla
wiekszosci cyfrowych procesorów pogłosowych, m.in Density, Diffusion, Decay Time, Reflections
Delay i wiele innych.
Audio Volume, oprócz ustawienia samego efektu pogłosu, umozliwia kontrole czasu przejscia
(ang. transition) pomiedzy dwoma objetosciami oraz zestaw parametrów opisujacych zachowanie
zródeł dzwieku znajdujacych sie poza dana objetoscia (np. wartosc tłumienia, filtr dolnoprzepu-
stowy). Objetosci audio moga nachodzic na siebie - w takim przypadku o koncowym efekcie decy-
duje ich priorytet.
Warto w tym miejscu zaznaczyc, ze zarówno geometria (wymiary, kształt) obiektów Audio Vo-
lume, jak i parametry przypisanego do niego efektu pogłosu, nie sa modyfikowalne w czasie gry, tylko
i wyłacznie w edytorze.
1.2.5 Klasy audio i miksy
Ostatnim wartym uwagi w kontekscie tej pracy komponentem systemu audio Unreal Engine 4 sa tzw.
klasy audio (asset typu Sound Class). Klasa audio to zestaw własciwosci, które moga dodatkowo mo-
dyfikowac dzwiek pochodzacy z asset-u Sound Wave lub Sound Cue. Jedna klasa audio moze zostac
przypisana do wielu asset-ów. Czesc własciwosci klasy audio działa jako mnoznik w stosunku do
tych zdefiniowanych przez Sound Wave/Sound Cue (np. Volume, Pitch) a czesc okresla zachowanie
dzwieku, np. parametr Reverb decyduje o tym czy na dzwieki nalezace do tej klasy moze zostac
nałozony pogłos. Klasy audio moga byc układane w hierarchie.
Do klas audio moga dodatkowo zostac przypisane tzw. miksy (asset typu Sound Mix) które umoz-
liwiaja zastosowanie korekcji barwy. W momencie powstawania tej pracy, Unreal oferuje prosta
3-pasmowa korekcje z regulowanymi czestotliwoscia srodkowa i wzmocnieniem.
1http://docs.unity3d.com/Manual/class-AudioReverbZone.html
18
1.3 System wtyczek
Unreal Engine 4 pozwala na tworzenie wtyczek, które umozliwiaja modyfikacje lub rozszerzenie funk-
cjonalnosci silnika oraz edytora bez koniecznosci edycji ich kodu zródłowego.
Ogólnie rzecz biorac, wtyczka w Unreal Engine 4 to zbiór tzw. modułów napisanych w jezyku
C++ kompilowanych do zewnetrznej biblioteki .dll (.so w systemach Linux). Kazdy moduł to ze-
staw klas definiujacych nowa funkcjonalnosc składajacy sie na pewna samowystarczalna (lub zalezna
od innego modułu) jednostke. Moduły sa ładowane przez silnik automatycznie lub na zadanie, w
zaleznosci od rodzaju (np. moduł moze byc ładowany tylko podczas działania edytora).
Silnik Unreal Engine 4 jest sam w sobie złozony z modułów. Przewazajaca czesc kodu rdzenia
silnika oraz edytora implementuje swoje moduły na podstawie tego samego interfejsu co developer
tworzacy wtyczke. Interfejs, z którego powinny dziedziczyc wszystkie implementacje modułów to
klasa IModuleInterface.
Kazda wtyczka posiada tzw. deskryptor, czyli plik okreslajacy podstawowe cechy wtyczki oraz
liste jego modułów. Plik ten jest wczytywany w momencie uruchomienia silnika w celu wyszukania i
odpowiedniego załadowania bibliotek modułów. Dwie najwazniejsze cechy modułu to jego typ oraz
moment załadowania. Unreal rozróznia nastepujace typu modułów:
• Runtime - moduł tego typu zostanie załadowany zawsze, to znaczy zarówno w grze jak i edy-
torze oraz we wszystkich konfiguracjach gry, równiez w tej przeznaczonej do dystrybucji (ang.
shipping).
• Developer - moduł tego typu zostanie załadowany tylko w developerskich konfiguracjach gry i
edytora.
• Editor - moduł, który zostanie załadowany tylko w edytorze.
Kazdy z tych rodzajów modułów moze dodatkowo definiowac jeden z nastepujacych momentów
załadowania:
• PostConfigInit - moment przed załadowaniem wszystkich podstawowych modułów silnika.
Opcja przydatna w przypadku implementacji modułu modyfikujacego/rozszerzajacego którys z
kluczowych podsystemów.
• PreDefault - moment po załadowaniu wszystkich podstawowych modułów silnika, lecz przed
momentem domyslnym dla wszystkich nie-kluczowych modułów.
• Default - domyslny moment ładowania dla wszystkich modułów.
Wtyczka moze składac sie z modułów róznego typu o roznym momencie załadowania.
19
2 RAYA
RAYA (skrót od ang. Raytracer Audio) jest biblioteka programistyczna, której zadaniem jest odpo-
wiednie przetwarzanie dzwieku w czasie rzeczywistym na podstawie połozenia i ruchu gracza oraz
zródeł dzwieku w wirtualnym swiecie. Biblioteka ta przeznaczona jest przede wszystkim dla develo-
perów gier i symulacji, którzy chca swoja produkcje wzbogacic o realistyczne zjawiska akustyczne
wystepujace w otaczajacym swiecie bez koniecznosci ich manualnego re-kreowania za pomoca do-
stepnych do tej pory komercyjnych narzedzi.
Zasada działania biblioteki RAYA opiera sie na technice sledzenia wiazek (ang. beam tracing)
dzwieku. Na podstawie rzeczywistej geometrii sceny i materiałów akustycznych, trasa przebywana
przez wiazki od zródła do odbiornika (np. gracza) jest rekonstruowana z uwzglednieniem wszystkich
fizycznych efektów akustycznych, takich jak odbicie, dyfrakcja i transmisja. Na tej podstawie wyzna-
czane sa parametry dzwieku, a konkretnie filtr cyfrowy okreslajacy jak zmienia sie dzwiek podczas
swojej drogi od zródła do odbiornika.
Z punktu widzenia uzytkownika, konieczne jest tylko zdefiniowanie geometrii sceny oraz przy-
pisanie odpowiednich materiałów akustycznych. Biblioteka RAYA wspiera szereg parametrów ma-
teriału wpływajacych na jego własciwosci akustyczne, m.in współczynniki pochłaniania, dyfrakcji
czy gestosc materiału. Rozłozenie materiałów na scenie i ich parametry wpływaja na koncowy efekt
dzwiekowy, tak jak ma to miejsce w rzeczywistym swiecie.
Biblioteka RAYA jest przeznaczona do działania w czasie rzeczywistym i umozliwia sledzenie i
jednoczesne przetwarzanie dzwieku pochodzacego z wielu zródeł. W swojej obecnej wersji zoptyma-
lizowana jest na komputery stacjonarne (tzw. PC-ty). Dla zwiekszenia swojej wydajnosci, umozliwia
przeniesienie czesci swoich obliczen na karte graficzna.
3 Opis integracji
3.1 Załozenia projektowe
Ponizej przedstawione zostana wymagania i załozenia projektowe, które wpłyneły na decyzje doty-
czace sposobu implementacji i oferowanej funkcjonalnosci. W zwiazku z faktem, iz, jak juz wcze-
sniej wspomniano, autor nie był jedyna osoba pracujaca nad niniejszym projektem tylko czescia de-
veloperskiego zespołu, czesc wymagan została narzucona odgórnie i nie zostanie przedstawiona ich
argumentacja, tylko wpływ na sposób implementacji.
Pierwszym i kluczowym załozeniem projektu była jego wieloplatformowosc. To znaczy, sposób
20
implementacji powinien byc identyczny dla wszystkich wspieranych platform, bez znaczacych róznic
w kodzie zródłowym. Konkretnie, wymagane było wsparcie dla minimum trzech platform: systemu
Windows oraz konsol Xbox One oraz PlayStation 4. Konsole róznia sie znaczaco pod wzgledem ar-
chitektury i wydajnosci liczonej per procesor w porównaniu do komputerów stacjonarnych. Na tych
drugich, RAYA potrzebuje ok. 10% zasobów procesora (jednego rdzenia)1. W zwiazku z tym, w
swojej obecnej implementacji biblioteka RAYA mogła by zbytnio obciazyc procesor konsoli. W kon-
sekwencji, zdecydowano sie na podejscie, które znaczaco zredukuje obciazenie procesora kosztem
zwiekszonej ilosci wymaganej pamieci: rezygnacje z sledzenia sciezek w czasie rzeczywistym. W
zamian, kalkulacje sciezek i wynikowych efektów akustycznych na podstawie geometrii sceny beda
dokonywane tylko raz, na etapie tworzenia sceny w edytorze UE4. Wyniki tych obliczen beda prze-
chowywane w postaci pliku i wykorzystywane w czasie rozgrywki (szczegółowy opis znajduje sie w
3.3). Podejscie takie prawie w całosci niweluje oryginalny koszt obliczen sprowadzajac go wyłacz-
nie do kosztu ekstrakcji i ewentualnej interpolacji przechowywanych parametrów, kosztem oczywi-
stych wymagan pamieciowych. Mimo to, podejscie to jest duzo łatwiejsze w optymalizacji: jedynym
znaczacym czynnikiem decydujacym o wydajnosci tej implementacji jest ilosc dostepnej pamieci
dyskowej oraz RAM na danym urzadzeniu, a ilosc potrzebnej pamieci moze byc łatwo zmniejszona
(kosztem dokładnosci) przez developera poprzez zmniejszenie gestosci dyskretyzacji sceny.
Drugim waznym wymaganiem projektu była implementacja wtyczki bez koniecznosci edycji
kodu zródłowego silnika UE42. Aby spełnic to wymaganie, trzeba wykorzystac istniejaca funkcjo-
nalnosc silnika audio w Unreal Engine 4 podczas implementacji wtyczki. W tym miejscu pojawił
sie drugi istotny problem: system audio UE4 nie umozliwia dostepu do próbek sygnału audio na
zadnym etapie jego przetwarzania3 w zwiazku z czym nie jest mozliwe zastosowania filtracji filtrami
FIR obliczonymi przez RAYA. Oznacza to, ze konieczna jest konwersja efektu akustycznego zdefi-
niowanego za pomoca odp. impulsowej/widma na w miare mozliwosci podobny efekt uzyskany za
pomoca kontroli parametrów udostepnionych przez silnik audio UE4. Szczegółowy opis rozwiazania
tego problemu i jego implementacji znajduje sie w kolejnych rozdziałach tej pracy.
1Testowane na przecietnym, współczesnym domowym procesorze2Edycja kodu zródłowego silnika UE4 w celu integracji niesie ze soba szereg niedogodnosci i problemów. Najwiek-
szy z nich to brak uniwersalnosci takiego rozwiazania: kazda osoba chcaca wykorzystac wtyczke musiałaby dokonacrekompilacji całego kodu zródłowego silnika.
3Nie w sposób który byłby wieloplatformowy. Istnieje nieudokumentowana mozliwosc dostepu do próbek sygnałuaudio w celu implementowania własnych efektów, ale tylko na platforme Windows w wersji 64-bit.
21
3.2 Ogólny schemat działania
Jak wspomniano w poprzednim rozdziale, sledzenie wiazek i wyliczenie efektów akustycznych nie
bedzie odbywac sie w czasie rzeczywistym. Proces ten bedzie odbywac sie na etapie tworzenia gry
i inicjowany bedzie przez developera z poziomu edytora po przypisaniu materiałów akustycznych
do obiektów na scenie. Wyliczone w ten sposób efekty akustyczne beda nastepnie wykorzystane do
modyfikacji dzwieku w czasie rzeczywistym, juz podczas rozgrywki.
Jako, ze biblioteka RAYA przeznaczona jest do działania w czasie rzeczywistym, bezposrednia
integracja nie jest mozliwa. Jak opisano w 2, obliczenie efektu akustycznego odbywa sie na podstawie
aktualnego połozenia odbiornika i zródeł i geometrii sceny. Natomiast w tym przypadku, czyli poza
rozgrywka, informacje o połozeniu zwyczajnie nie istnieja.
W zwiazku z tym zastosowano podejscie oparte na “dyskretyzacji“ (geometrii) sceny, tzn. gestym
podziale na trójwymiarowe, prostopadłoscienne obszary o tych samych wymiarach, tworzac pewnego
rodzaju siatke. Obszary te beda dalej przez autora nazywane komórkami. Geometryczny srodek
tej komórki wyznacza mozliwe połozenie gracza lub odbiornika. Wyliczenie efektów akustycznych
nastepuje dla kazdej komórki z osobna poprzez "umieszczenie"w jej srodku odbiornika i nastepne
sledzenie wiazek dochodzacych ze zródeł umieszczonych we wszystkich pozostałych komórkach.
Dla kazdej komórki wiec obliczony zostaje zestaw wzglednych efektów akustycznych okreslajacy jak
zmienia sie docierajacy do niej dzwiek z poszczególnych komórek. Oprócz tego, dla kazdej komórki
aproksymowane zostaja czasy pogłosu dla wybranych czestotliwosci.
W nastepnym kroku nastepuje zamiana obliczonych efektów akustycznych (reprezentowanych
przez charakterystyke czestotliwosciowa filtru cyfrowego) na w pewien sposób przyblizajacy dany
efekt zestaw parametrów dzwieku bedacych czescia API silnika Unreal Engine 4. Konkretnie, sa to
trzy parametry: wzmocnienie amplitudy, wzmocnienie najwyzszych czestotliwosci oraz kierunek
dzwieku1. Ostatecznie, wynikowa struktura danych zawiera zestawy ww. parametrów zalezne od
wzglednego połozenia odbiornika i zródła oraz czasy pogłosu zalezne tylko od połozenia odbiornika.
Strukturze tej nadano nazwe wypalona scena akustyczna, a proces jej powstania wypaleniem sceny
akustycznej2.
Wypalona scena akustyczna zostaje nastepnie wykorzystana do modyfikacji dzwieków w czasie
rzeczywistym. Jako ze parametry zmiany dzwieku w wypalonej scenie akustycznej dotycza dyskret-
nych punktów na regularnej siatce, podczas rozgrywki konieczna jest ich interpolacja, na zadanie,
kazdorazowo dla danej pary pozycji zródła i odbiornika.
1Wyjasnienie powodu wyboru konkretnie tych parametrów zostało opisano w rozdziale dot. implementacji 4.1.42Analogicznie do terminu “wypalania“ oswietlenia (ang. light baking) - okreslajacego popularny wsród trójwymia-
rowych aplikacji graficznych (w tym równiez gier) proces wczesniejszego wyliczenia statycznego oswietlenia w celupózniejszego uzycia.
22
Zmiana parametrów dzwieku zalezna od wzglednego połozenia gracza i zródła implementowana
jest poprzez napisany specjalnie w tym celu SoundNode (patrz 1.2.2). Zatem to developer decyduje,
z których zródeł dzwieku dzwiek bedzie modyfikowany. Efekt pogłosu, który jest zalezny tylko od
połozenia gracza, bedzie aplikowany za pomoca obejmujacego cała scene obiektu AudioVolume (patrz
1.2.4).
3.3 Architekrura
Wtyczka podzielona została na dwa moduły:
• Moduł edytora - załadowany tylko wraz z edytorem (patrz 1.3) odpowiedzialny za funkcjonal-
nosc zwiazana z procesem tworzenia gry, czyli praca w edytorze:
– rozszerzenie graficznego interfejsu edytora o kontrolki oraz komunikaty specyficzne dla
wtyczki
– implementacje asset-u reprezentujacego materiał akustyczny wraz z pełna funkcjonalno-
scia w aspekcie tworzenia i edycji przez uzytkownika 4.2.1
– eksport sceny do biblioteki RAYA, tzn. informacji o jej geometrii wraz z rozmieszczeniem
materiałów akustycznych
– zarzadzanie procesem konfiguracji biblioteki RAYA oraz od strony UE4: tworzenia mapy
akustycznej i jej pózniejszej kompresji
• Moduł gry - załadowany zawsze, który odpowiedzialny jest za czesc funkcjonalnosci nieza-
lezna od edytora, czyli ta zwiazana z rozgrywka i samym przetwarzaniem dzwieku, a w szcze-
gólnosci:
– implementacje Sound Node nakładajacego efekt na strumien audio zródła dzwieku w za-
leznosci od pozycji gracza
– sledzenie aktualnej pozycji gracza za pomoca komponentu doczepianego do aktora ka-
mery
– aplikowanie zmiennego efektu pogłosu
Moduł gry definiuje takze dwa typy wykorzystywane przez oba moduły, mianowicie typ re-
prezentujacy asset zawierajacy wypalona scene akustyczna oraz tzw. aktor-menadzer, którego
zadaniem jest m.in. przechowywanie ww. asset-u dla danej sceny (poziomu).
23
W tym miejscu nalezy zaznaczyc, ze powyzszy podział na moduły i zakres ich obowiazków ule-
gał zmianie wielokrotnie w czasie pracy nad projektem. Nadrzednym celem takiego podziału jest
wyizolowanie funkcjonalnosci zaleznej od modułów edytora UE4 i usuniecie jej z modułu gry, co
jest dobra praktyka programistyczna, m.in. poniewaz pozwala na unikniecie stosowania kodu warun-
kowej kompilacji1.
Oba moduły komunikuja sie z zewnetrzna biblioteka, RAYA-offline, stworzona specjalnie na po-
trzeby wtyczki. Biblioteka ta pełni role posrednika miedzy modułami wtyczki a własciwa biblioteka
RAYA i to w niej jest zaimplementowana cała czesc integracji nie zalezna od silnika UE4, w szczegól-
nosci wypalenie sceny akustycznej oraz pózniejsza interpolacja parametrów akustycznych w czasie
rzeczywistym. Szczegółowy opis tej biblioteki znajduje sie w rozdziale 3.4.
BibliotekaRAYA-OFFLINE
Moduł edytora Moduł gry
Wtyczka
geometria
scenyi
rozkładm
ateriałów
wypalona
scena
akustyczna
inte
rpol
owan
epa
ram
etry
pozy
cja
grac
zai
zród
ełdz
wie
ku
Rysunek 4: Schemat komunikacji miedzy modułami wtyczki oraz biblioteka RAYA-offline.
3.4 RAYA-offline
Jak juz wczesniej wspomniano, wewnatrz biblioteki RAYA-offline ma miejsce implementacja algo-
rytmów nie powiazanych z API silnika UE4. Tutaj zaimplementowane zatem sa: wypalanie sceny
akustycznej (dyskretyzacja sceny, aproksymacja efektów akustycznych), interpolacja parametrów
oraz kompresja danych wypalonej sceny akustycznej. Biblioteka ta do swojej implementacji wy-
1Duza czesc kodu UE4 jest warunkowo kompilowana w zaleznosci od edytora (słuza do tego specjalne makra).Pewne klasy i interfejsy sa eksportowane tylko w edytorowych konfiguracjach, tzn. nie beda widoczne dla konsolidatoraw konfiguracjach bez edytora, np. tej przeznaczonej do dystrybucji. Pozostawienie kodu zaleznego od edytora w modulegry, który jest ładowany we wszystkich konfiguracjach oznaczało by koniecznosc otoczenia go makrami warunkowejkompilacji co zmniejsza czytelnosc kodu.
24
korzystuje szereg zewnetrznych bibliotek, w szczególnosci oryginalna biblioteke RAYA. Biblioteka
RAYA-offline jest statycznie linkowana przez moduły wtyczki.
Na wstepie nalezy zaznaczyc, ze implementacja tej biblioteki nie była dziełem autora, za wyjat-
kiem funkcjonalnosci zwiazanej z kompresja i dekompresja wypalonej sceny akustycznej oraz efek-
tem pogłosu.
Biblioteka RAYA-offline komunikuje sie z wtyczka za pomoca dwóch publicznych interfejsów
(klas): AudioOffline i Audio.
Klasa AudioOffline reprezentuje funkcjonalnosc nie zwiazana z przetwarzaniem w czasie rzeczy-
wistym (“offline“). Wykorzystywana jest przez moduł edytora i za jej pomoca przeprowadzana jest
konfiguracja silnika RAYA przed wypaleniem sceny akustycznej, eksport sceny z edytora oraz utwo-
rzenie mapy parametrów akustycznych.
Klasa Audio reprezentuje funkcjonalnosc zwiazana z przetwarzaniem w czasie rzeczywistym i jest
wykorzystywana przez moduł gry wtyczki. Zadaniem obiektu tej klasy jest przekazanie do modułu
gry interpolowanych parametrów modyfikujacych dzwiek i czasów pogłosy na podstawie pozycji
gracza i zródła dzwieku.
4 Implementacja
W rozdziale tym przedstawiona zostanie szczegółowy opis implementacji funkcjonalnosci wtyczki
bedacy dziełem autora tej pracy. W szczególnosci, opisane zostana poszczególne komponenty obu
modułów, ich rola w kontekscie wtyczki oraz sposób działania wraz z argumentacja.
W rozdziale tym pojawiaja sie wycinki rzeczywistego kodu zródłowego implementacji w jezyku
C++ oraz opisy algorytmów za pomoca pseudokodu. Przy implementacji wtyczki stosowano kon-
wencje przyjete w kodzie zródłowym Unreal Engine 4[6], w szczególnosci nazwa typu poprzedzana
jest jedna z trzech liter:
• U - klasa dziedziczaca z UObject
• A - klasa dziedziczaca z AActor
• F - pozostałe typy: klasy oraz struktury
25
4.1 Moduł gry
4.1.1 Asset sceny akustycznej
Dane reprezentujace wypalona scene akustyczna dla danego poziomu, która opisano w 3.2, beda wy-
korzystywane przez oba moduły: edytora i gry. Naturalnie, musza byc one dostepne dla modułu gry
po tzw. spakowaniu gry (ang. packaging), czyli ostatecznym procesie przygotowujacym gre do dys-
trybucji. W zwiazku z tym, konieczne jest przechowanie tych danych w postaci pliku, który zostanie
spakowany wraz z pozostała czescia gry i nastepnie rozpakowany i wczytany w czasie rozgrywki.
Naturalne było wiec wykorzystanie do tego celu systemu asset-ów i ich mechanizmu serializacji i
deserializacji (patrz 1.1.5). Dzieki temu, nie było konieczne programowanie procesu zapisu i od-
czytu danych z dysku - jest to automatyczne implementowane przez mechanizm refleksji silnika i
odpowiednie metody klasy UObject. Reprezentacja za pomoca asset-u pozwala równiez na łatwa
integracje z edytorem, np. w celu intuicyjnego dla uzytkownika przypisywania danych wypalonej
sceny akustycznej do danego poziomu (mapy).
Dane sceny akustycznej wewnatrz asset-u postanowiono przechowywac w postaci zwykłego ciagu
bajtów. Postapiono tak, poniewaz, jak opisano w 3.4, cała implementacja wypalania sceny akustycz-
nej oraz pózniejszej interpolacji znajduje sie w bibliotece RAYA-offline, natomiast mechanizmowi
refleksji podlegaja tylko typy natywne oraz USTRUCT/UCLASS. W zwiazku z tym, aby umozliwic se-
rializacje i deserializacje, konieczne byłoby odzwierciedlenie klas/struktur reprezentujacych scene
akustyczna definiowanych przez RAYA-offline za pomoca odpowiednich klas i struktur oznaczonych
za pomoca makr USTRUCT/UCLASS lub nadpisanie domyslnych metod odpowiedzialnych za serializa-
cje i deserializacje. Wprowadzony w ten sposób dodatkowy poziom abstrakcji byłby zupełnie nie-
potrzebny. O wiele łatwiejsza jest serializacja struktury danych do ciagu (tablicy) bajtów, który jest
typem natywnym.
Bajt w jezyku C++ reprezentowany jest przez typ unsigned char. W UE4, typem reprezentujacy
dynamiczna tablice jest TArray<>. Przechowanie ciagu bajtów za pomoca tej tablicy i oznaczenie pola
poprzez UPROPERTY wystarcza do jego automatycznej serializacji na dysk i deserializacji przez silnik.
Pole to jest domyslnie ukryte przed uzytkownikiem w edytorze. Poniewaz scena akustyczna jest se-
rializowana i deserializowana po stronie biblioteki RAYA-offline, która oczywiscie nie wykorzystuje
typu TArray, lecz odpowiednika z biblioteki standardowej jezyka C++, std::vector, konieczna jest
kazdorazowa konwersja pomiedzy tymi dwoma typami po stronie wtyczki.
Klase reprezentujaca asset wypalonej sceny akustycznej nazwano URAYALevelBakeAsset. Oprócz
zdeserializowanej do ciagu bajtów sceny akustycznej klasa ta zawiera pola dotyczace jej kompresji
opisanej w rozdziale 4.2.6.
26
4.1.2 Menadzer
Jak wspomniano w 4.1.1, kazdy poziom (mapa) musi miec przypisany asset który zostanie załado-
wany przez moduł gry w czasie rozgrywki. Ponadto, uzytkownik musi posiadac mozliwosc zmiany
przypisania asset-u wypalonej sceny akustycznej do danego poziomu. Łatwo sobie wyobrazic sytu-
acje, w której developer wypala scene kilkukrotnie z uzyciem róznych ustawien w celu ich porów-
nania i wybrania najlepszego z nich. Konieczne było wiec zapewnienie uzytkownikowi intuicyjnego
interfejsu, który by taka funkcjonalnosc oferował. Natomiast z technicznego punktu widzenia, takie
przypisanie wypalonej sceny do danego poziomu musi spełniac dwa warunki:
1. Byc jednoznaczne. To znaczy, kazdy poziom musi miec przypisany tylko jeden asset wypalo-
nej sceny akustycznej.
2. W czasie rozgrywki byc łatwo dostepne w momencie załadowania nowego poziomu lub przej-
scia gracza na inny poziom - tak aby moduł gry mógł odpowiednio zareagowac poprzez zmiane
aktualnie wykorzystywanej mapy parametrów akustycznych do modyfikacji dzwieku.
Zadecydowano, ze powyzsze warunki zostana łatwo spełnione poprzez implementacje tzw. obiektu
menadzera reprezentowanego za pomoca specjalnego rodzaju aktora. Aktor ten posiada wskaznik
do instancji obiektu asset-u wypalonej sceny akustycznej odpowiadajacej poziomowi na którym sie
znajduje. Wskaznik ten jest polem objetym systemem refleksji i oznaczonym do edycji z poziomu
edytora (za pomoca makra UPROPERTY(EditAnywhere), patrz 1.1.4) dzieki czemu uzytkownik moze
w łatwy i intuicyjny sposób wybierac i przypisywac asset wypalonej sceny akustycznej w oknie edycji
aktora w edytorze.
Spełnienie warunku 1 osiagnieto w dwóch powiazanych krokach. Pierwszym było całkowite
ukrycie aktora z edytora sceny oraz okna pokazujaca liste wszystkich aktorów na scenie, tzw. scene
outliner. W UE4 dokonac tego mozna ustawiajac specjalne flagi (pola klasy bazowej AActor) w
konstruktorze klasy. Ponizej przedstawiony jest wycinek kodu konstruktora klasy reprezentujacej
aktora-menadzera (nazwanej ARAYAManager) odpowiedzialny za jego niewidocznosc na scenie:
ARAYAManager :: ARAYAManager ()
{
#if WITH_EDITORONLY_DATA
bHiddenEd = true;
bHiddenEdLayer = true;
bHiddenEdLevel = true;
bListedInSceneOutliner = false ;
27
#endif
}
Dzieki temu, ze aktor-menadzer jest niewidoczny na scenie i w edytorze, nie moze on zostac usuniety
lub zreplikowany przez uzytkownika. W zwiazku z faktem iz powyzsze flagi sa zwiazane z edyto-
rem, a aktor-menadzer jest czescia modułu gry, konieczne było otoczenie ich makrem warunkowej
kompilacji WITH_EDITORONLY_DATA, które powoduje, ze kod nie bedzie widoczny dla kompilatora w
konfiguracji bez edytora.
Drugim krokiem podjetym w celu zapewnienia, ze na scenie bedzie tylko jeden aktor-menadzer,
było połaczenie miejsca jego tworzenia z miejscem dostepu do jego instancji z poziomu kodu. Odpo-
wiedzialna jest za to metoda klasy modułu edytora GetManagerForCurrentLevel() której zadaniem
jest odnalezienie aktualnego obiektu menadzera lub, w przypadku gdy taki jeszcze nie istnieje, stwo-
rzenie go:
ARAYAManager * FRAYAEditorModule :: GetManagerForCurrentLevel () const
{
auto World = GetLevelEditorWorld ();
TActorIterator < ARAYAManager > It(World);
if (It)
return *It;
else
return World ->SpawnActor < ARAYAManager >();
}
Dzieki takiej implementacji powyzszej metody, załozenie, ze na scenie bedzie istniec maksymalnie
jeden obiekt aktor-menadzer jest zawsze spełnione (pod warunkiem, ze nie bedzie on tworzony w
zadnym innym miejscu w kodzie). W celu odnalezienia obiektu menadzera, mozna posłuzyc sie
specjalnym typem iteratora, TActorIterator<>, który pozwala na efektywna iteracje po wszystkich
aktorach danego typu znajdujacych sie obecnie na scenie.
Warunek 4.1.2 spełniono wykorzystujac mechanizm cyklu zycia aktora, o którym wspomniano w
1.1.3. W momencie załadowania przez silnik nowego poziomu, silnik wywołuje szereg metod (klasy
bazowej AActor) na obiekcie kazdego aktora nalezacych do tego poziomu w celu jego poprawnego
załadowania i przygotowania. Jedna z nich jest wirtualna metoda BeginPlay(). Postanowiono, ze
za pomoca tej metody aktor-menadzer bedzie “rejestrowac sie“ w instancji modułu gry, przekazujac
siebie jako parametr. Analogicznie, aktor-menadzer bedzie “de-rejestrowac“ wewnatrz analogicznej
metody EndPlay(), która jest wywoływana na aktorze przed jego zniszczeniem (co nastepuje np. w
momencie dany poziom zostaje usuniety z pamieci):
28
void ARAYAManager :: BeginPlay ()
{
IRAYAGameModule :: Get (). ManagerBeginsPlay (this);
}
void ARAYAManager :: EndPlay (const EEndPlayReason :: Type)
{
IRAYAGameModule :: Get (). ManagerEndsPlay (this);
}
Metoda IRAYAGameModule::Get() interfejsu modułu gry zwraca jego instancje. Moduł gry przecho-
wuje wewnatrz liste obecnie zarejestrowanych aktorów-menedzerów. Dzieki temu, ze kazdy z nich
przechowuje wskaznik do asset-u wypalonej sceny akustycznej odpowiadajacej poziomowi (scenie)
której jest czescia, moduł gry moze w kazdym momencie jednoznacznie okreslic, który asset powi-
nien byc w danym momencie “aktualnym“ (opisano w 4.1.3).
Nalezy jeszcze wspomniec, ze klasa reprezentujaca aktora-menadzera (nazwana URAYAManager)
dziedziczy z klasy AInfo - abstrakcyjnej klasy bazowej dla wszystkich aktorów, które w UE4 repre-
zentuja aktorów nie posiadajacych fizycznej manifestacji w swiecie gry i których głównym uzyciem
sa własnie klasy typu "menadzer". Deklaracja klasy AInfo m.in ukrywa wszystkie cechy aktora, które
nie maja sensu aby były wyswietlane w oknie edycji, np. zwiazane z ruchem czy kolizjami.
4.1.3 Komponent odbiornik
Jak wspomniano w 4.1.2, moduł gry zawiera liste aktualnie zarejestrowanych aktorów-menedzerów.
Sa oni rejestrowani i de-rejestrowani wraz z ładowaniem i “od-ładowywaniem“ poziomów przez sil-
nik w czasie rozgrywki. Moduł gry musi byc w kazdym momencie w stanie jednoznacznie okreslic,
który asset wypalonej sceny akustycznej jest tym aktualnym w celu przekazania tej informacji cze-
sciom wtyczki, które przetwarzaja dzwiek na jego podstawie.
Problem z jakim autorowi przyszło sie tutaj zmierzyc jest fakt, iz w danym momencie rozgrywki
moze byc załadowanych kilka poziomów jednoczesnie. Jest to zwiazane z tzw. strumieniowaniem
poziomów (ang. level streaming), tzn. funkcjonalnoscia silnika UE4 umozliwiajaca ich stopniowe
ładowanie i usuwanie z pamieci wraz z przemieszczaniem gracza w celu zapewnienia graczowi nie-
przerwanej rozgrywki oraz odczucia, ze cały swiat gry to jeden, duzy poziom1.
W celu rozwiazania tego problemu i identyfikacji poziomu “wewnatrz“ którego aktualnie znajduje
sie gracz i na tej podstawie własciwego aktora-menadzera (i przypisanego do niego asset-u sceny
1https://docs.unrealengine.com/latest/INT/Engine/LevelStreaming/index.html
29
akustycznej) postanowiono wykorzystac obecna pozycje gracza, czyli odbiornika (słuchacza). Moduł
gry nieustannie sprawdza, wewnatrz, której sceny akustycznej, sposród tych przypisanych do obecnie
zarejestrowanych menedzerów aktualnie znajduje sie gracz. Sprawdzenie to odbywa sie poprzez
prosty test czy punkt (pozycja gracza) w przestrzeni trójwymiarowej znajduje sie wewnatrz objetosci
obejmujacej dana scene akustyczna. Wymiary tej objetosci, tzw. z ang. bounding-box zawarte sa
wewnatrz obiektu URayaLevelBakeAsset i zdefiniowane w momencie wypalenia sceny na podstawie
jej geometrii.
Moduł gry jednak nie posiada informacji o pozycji “gracza“ gdyz z technicznego punktu widzenia
jest to pojecie czysto abstrakcyjne. Postanowiono wiec wprowadzic komponent odbiornik, którego
zadaniem bedzie przekazywanie modułowi gry swojej pozycji. Uzytkownik doczepiajac ten kom-
ponent np. do aktora-kamery na scenie definiuje, ze to jest obiekt, który ma byc traktowany jako
odbiornik dzwieku przez moduł gry.
Klase reprezentujaca komponent-odbiornik nazwano URAYAListenerComponent. Implementacja
tego komponentu do przekazania swojej pozycji do instancji moduły gry nadpisuje metode wirtualna
TickComponent() klasy bazowej komponentu aktora (UActorComponent). Metoda ta jest wywoły-
wana przez silnik co klatke. Ponizej przedstawiony jest wycinek kodu zródłowego implementacji:
void URAYAListenerComponent :: TickComponent ( float DeltaTime , ELevelTick
TickType , FActorComponentTickFunction * ThisTickFunction )
{
FVector Position = GetOwner () ->ActorToWorld (). GetLocation ();
IRAYAGameModule :: Get (). UpdateListenerPosition ( Position );
// pozostała część ciała metody
}
Nastepnie, moduł gry wewnatrz metody UpdateListenerPosition(), iterujac po kolekcji zareje-
strowanych aktorów-menedzerów, sprawdza dla kazdego, czy przekazane współrzedne znajduja sie
wewnatrz przypisanej do niego sceny akustycznej i na tej podstawie ustawia wskaznik na aktualnego
menadzera. W przypadku gdy komponent-odbiornik znajduje sie poza granicami wszystkich obecnie
załadowanych scen akustycznych, wskaznik reprezentujacy aktualnego menedzera ustawiany jest na
wartosc NULL.
4.1.4 Modyfikacja dzwieku w czasie rzeczywistym
Jak wspominano w 3.2, modyfikacja sygnału akustycznego w zaleznosci od połozenia gracza wzgle-
dem zródła dzwieku zaimplementowana została poprzez napisanie klasy rozszerzajacej interfejs So-
30
undNode, bedacy czescia API silnika UE4 (patrz 1.2.2). Klasa ta została nazwana URAYASoundNode. Z
kolei nałozenie zmiennego w czasie rzeczywistym efektu pogłosu odbywa sie za pomoca manipulacji
parametrami przypisanego do obiektu Audio Volume efektu pogłosu (patrz 1.2.4).
RAYA Sound Node
Implementacja własnego podtypu SoundNode sprowadza sie do nadpisania metody wirtualnej USo-
undNode::ParseNodes(). Głównym zadaniem tej metody jest modyfikacja przekazanej do niej jako
parametr struktury zawierajacej zestaw parametrów nakładanych na sygnał audio, FSoundParseParameters
. To własnie modyfikacja tych parametrów definiuje efekt implementowany przez dany Sound Node.
Silnik, podczas tzw. ewaluacji grafu audio, czyli, w tym przypadku przechodzenia go od ostatniego
wierzchołka do pierwszego, wywołuje metode ParseNodes() na kazdym aktywnym wierzchołku kaz-
dorazowo przekazujac ta sama instancje struktury FSoundParseParameters.
Jak juz wspomniano wczesniej w 3.2, opisana tutaj implementacja modyfikuje tylko trzy parame-
try: wzmocnienie amplitudy, wzmocnienie najwyzszych czestotliwosci oraz wzgledna transformacje
zródła dzwieku. Jest tak, poniewaz sa to jedyne parametry struktury FSoundParseParameters, które
moga byc zalezne od wzglednego połozenia gracza1. Parametry te reprezentowane sa kolejno przez
pola:VolumeMultiplier, HighFrequencyGain i Transform.
Jak opisano w 3.4, wypalona scena akustyczna przechowywana jest wewnatrz obiektu klasy Au-
dio bedacej czescia API biblioteki RAYA-offline. Aktualny w danym momencie obiekt tej klasy
uzyskiwany jest za pomoca metody interfejsu modułu gry GetCurrentAudio()2.
Algorytm wewnatrz metody ParseNodes() pobiera od modułu gry wskaznik do aktualnego obiektu
Audio, a nastepnie, za pomoca odpowiedniej metody tej klasy pobiera zestaw ww. parametrów mo-
dyfikujacych dzwiek przekazujac do niej współrzedne odbiornika oraz zródła sygnału audio. Obie
współrzedne przekazywane sa do metody ParseNodes() jako parametry. Ostatecznie, modyfikowane
sa odpowiednie pola struktury FSoundParseParameters.
Pogłos
W rozdziale 1.2.4 opisano pokrótce w jaki sposób implementowany jest mechanizm efektu pogłosu
w silniku Unreal Engine 4. Developer (projektant dzwieku) definiuje obszary na scenie poprzez
rozstawianie specjalnych aktorów typu Audio Volume przypisujac do kazdego z nich statyczne efekty
1Fakt ten jest prawdziwy równiez dla najnowszej wersji silnika w momencie pisania tej pracy - 4.10. Dokumentacjastruktury FSoundParseParameters: https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/FSoundParseParameters/index.html
2W implementacji modułu gry, kazdy zarejestrowany aktor-menedzer posiada odpowiadajacy mu obiekt klasy Audio.Jak opisano w 4.1.3, moduł gry przechowuje i uaktualnia wskaznik na aktualnego obecnie aktora-menedzera.
31
Rysunek 5: Obiekt RAYASoundNode w oknie edycji SoundCue.
pogłosu, reprezentowane przez obiekt (asset) typu UReverbEffect.
Z punktu widzenia przedstawionej tutaj implementacji wazne jest, ze zmiana parametrów instancji
obiektu UReverbEffect nie spowoduje uaktualnienia efektu pogłosu aplikowanego przez silnik audio.
Silnik audio UE4 działa w ten sposób, ze co klatke, na podstawie aktualnej pozycji gracza, przy-
gotowywana jest lista wszystkich obszarów Audio Volume wewnatrz których aktualnie znajduje sie
gracz. Sposród nich nastepnie wybierany jest ten o najwyzszym priorytecie i nastepuje sprawdzenie
czy przypisana do niego instancja klasy UReverbEffect jest inna niz w poprzedniej klatce. Jezeli tak,
nastepuje interpolacja do nowego efektu1.
Do implementacji zmiany efektu pogłosu w czasie rzeczywistym wystarczy wiec podmiana co
klatke przypisanego obiektu UReverbEffect do obejmujacego obszar całej sceny akustycznej aktora
Audio Volume.
Za ustawienie obiektu Audio Volume na scenie odpowiedzialny jest aktor-menedzer. Jak wspo-
mniano w 1.2.4, nie jest mozliwe utworzenie obiektu Audio Volume w czasie rozgrywki - musi sie to
odbyc przy załadowanym edytorze.
W momencie gdy uzytkownik w edytorze zmieni przypisany do aktora-menedzera asset wypalo-
nej sceny akustycznej, tworzy on nowa instancje Audio Volume o prostopadłosciennym kształcie i wy-
miarach obejmujacych cała scene akustyczna. Silnik UE4, a konkretnie mechanizm refleksji umoz-
liwia wykrycie momentu zmiany wartosci pola klasy oznaczonego przez makro UPROPETY. Słuzy do
tego metoda wirtualna metoda klasy UObject, PostEditChangeProperty(). Ponizej przedstawiona
jest implementacja tej metody wewnatrz klasy aktora-menadzera:
void ARAYAManager :: PostEditChangeProperty ( struct FPropertyChangedEvent
1Algorytm opisano na podstawie analizy kodu zródłowego silnika.
32
& PropertyChangedEvent )
{
auto PropertyName = ( PropertyChangedEvent . Property ) ?
PropertyChangedEvent .Property -> GetFName () : NAME_None ;
if ( PropertyName == GET_MEMBER_NAME_CHECKED ( ARAYAManager , BakeAsset )
)
{
if ( RayaAudioVolume )
{
RayaAudioVolume -> Destroy ();
}
SpawnRayaAudioVolume ();
}
}
Najpierw sprawdzane jest czy zmienione zostało pole BakeAsset, czyli wskaznik na asset sceny aku-
stycznej (słuzy do tego makro bedace czescia API UE4 GET_MEMBER_NAME_CHECKED). Jezeli tak, to
w przypadku gdy na scenie znajduje sie juz obiekt Audio Volume, zostaje on uprzednio zniszczony.
Ostatecznie, metoda SpawnRayaAudioVolume() tworzy nowy taki obiekt o wymiarach zdefiniowa-
nych przez bounding-box sceny akustycznej, czyli obejmujacy cała scene.
Za utworzenie efektów pogłosu (obiektów UReverbEffect) i ich podmiany w stworzonym w po-
wyzszy sposób obiekcie Audio Volume odpowiada komponent-odbiornik (opisany w 4.1.3). Dokonuje
tego wewnatrz opisanej juz wczesniej metody TickComponent().
W obecnej implementacji, biblioteka RAYA-Offline oferuje algorytm do aproksymacji czasu po-
głosu tylko dla dwóch czestotliwosci: 0Hz oraz czestotliwosci Nyquista, czyli czestotliwosci równej
połowie czestotliwosci próbkowania zatem tylko te dwa czasy pogłosu sa zawarte w danych wypa-
lonej scenie akustycznej. Na podstawie tych dwóch czasów, wyrazonych w sekundach, autor po-
stanowił dokonac wyznaczenia wartosci dwóch parametrów bedacych czescia struktury UReverbEf-
fect: DecayHFRatio oraz RTAt1Khz. Pierwszy z nich to, według dokumentacji, stosunek szybkosci
zanikania wysokich czestotliwosci w stosunku do czestotliwosci niskich. Postanowiono zatem wy-
znaczyc go jako iloraz czasu pogłosu dla czestotliwosci Nyquista i czasu pogłosu dla czestotliwosci
0Hz: DecayHFRatio = rtNyquist/rtDC. Drugi parametr, RTAt1Khz, czyli czas pogłosu wyrazony w
sekundach dla czestotliwosci 1kHz, obliczany jest za pomoca logarytmicznej interpolacji.
33
4.2 Moduł edytora
4.2.1 Asset materiał akustyczny
W celu umozliwienia uzytkownikowi przypisywania materiałów akustycznych do obiektów na scenie
zaimplementowano dwa elementy. Pierwszym z nich jest asset reprezentujacy materiał akustyczny,
czyli zestaw parametrów definiujacych własciwosci akustyczne danego materiału. Drugim elemen-
tem jest komponent sceny, który po podpieciu do aktora reprezentujacego pewna geometrie umozliwi
przypisanie do niego asset-u reprezentujacego materiał akustyczny. Teoretycznie, sam komponent
zawierajacy w sobie liste własciwosci materiału wystarczałby do tego aby uzytkownik mógł okreslac
własciwosci akustyczne obiektów na scenie. Jednakze implementacja specjalnego asset-u reprezentu-
jacego materiał akustyczny pozwala na intuicyjne tworzenie i edytowanie materiałów akustycznych w
taki sam sposób jak innych rodzajów materiałów w Unreal Engine 4. Taka funkcjonalnosc umozliwia
dodatkowo np. łatwe stworzenie biblioteki materiałów akustycznych.
Biblioteka RAYA udostepnia szereg parametrów okreslajacych lub wpływajacych na własciwosci
akustyczne materiału. Sa to m.in: współczynnik pochłaniania i rozpraszania (w 8 pasmach czestotli-
wosci), predkosc dzwieku w materiale, gestosc, grubosc, moduł Younga czy współczynnik Poissona.
Wszystkie te parametry powinny znalezc odzwierciedlenie w typie asset reprezentujacych materiał
akustyczny. Zdecydowano, ze dla prostoty implementacji wystarczy tylko aby klasa definiujaca as-
set zawierała pola odpowiadajace kolejnym parametrom materiału. Kazde z pól jest typu zmienno-
przecinkowego pojedynczej precyzji float, tak jak odpowiadajace im pola w typie reprezentujacym
materiał akustyczny w bibliotece RAYA. Zastosowano równiez takie samo nazewnictwo wszystkich
parametrów.
Klasa reprezentujaca materiał akustyczny nazwana została URAYAMaterial. Ponizej podany jest
wycinek kodu jej definicji:
UCLASS ()
class URAYAMaterial : public UObject
{
GENERATED_BODY ()
public :
UPROPERTY ( EditAnywhere )
float AbsorptionCoefficients [8];
// pozostałe pola reprezentujące właściwości akustyczne
};
34
Wszystkie pola klasy poprzedzone sa makrem UPROPERTY(EditAnywhere) aby uzytkownik miał
mozliwosc ich edycji z poziomu edytora.
Aby uzytkownik posiadał mozliwosc tworzenia asset-u z poziomu okna przegladarki asset-ów
wewnatrz edytora, nalezało dodatkowo zaimplementowac interfejs tzw. fabryki, UFactory, którego
zadaniem jest stworzenie nowej instancji danego asset-u. Do implementacji wystarczy nadpisac me-
tode wytwórcza UFactory::FactoryCreateNew() zwracajaca wskaznik do nowej instancji obiektu,
w tym przypadku typu URAYAMaterial. Ponizej podany jest kod implementacji tej metody wewnatrz
klasy URAYAMaterialFactory:
UObject * URAYAMaterialFactory :: FactoryCreateNew ( UClass * Class , UObject
* InParent , FName Name , EObjectFlags Flags , UObject *,
FFeedbackContext *)
{
return NewObject < URAYAMaterial >( InParent , Class , Name , Flags);
}
Rysunek 6: Materiał RAYA w menu tworze-nia nowego asset-u.
Rysunek 7: Okno edycji materiału RAYA.
4.2.2 Komponent materiału akustycznego
Zadaniem komponentu materiału akustycznego jest przypisanie asset-u URAYAMaterial do aktora na
scenie reprezentujacego pewien statyczny obiekt geometryczny (obiekt ten nie powinien byc dyna-
miczny, czyli posiadac mozliwosc zmiany transformacji podczas gry, poniewaz mapa parametrów
akustycznych jest z definicji statyczna). Podstawowym i najczesciej uzywanym typem obiektu w
UE4, który posiada geometrie, jest komponent sceny Static Mesh Component lub aktor typu Sta-
35
tic Mesh Actor, który posiada taki komponent jako komponent zródłowy (patrz 1.1.3). Geometria
tych obiektów zdefiniowana jest poprzez trójwymiarowa siatke (ang. mesh) trójkatów. Słowo Static
oznacza, ze siatka ta jest statyczna, czyli jej geometria nie moze sie zmieniac w czasie gry.
W zwiazku z tym, ze uzytkownik moze tworzyc hierarchie komponentów typu Static Mesh w
celu tworzenia złozonych geometrii i chciec przypisac inny materiał do kazdego z nich, komponent
reprezentujacy materiał akustyczny musi posiadac mozliwosc bycia doczepianym do innych kompo-
nentów, czyli dziedziczyc co najmniej z typu komponentu sceny, USceneComponent. (1.1.3).
Komponent materiału akustycznego reprezentowany jest przez klase URAYAMaterialComponent.
Klasa ta zawiera tylko jedno pole, którym jest wskaznik do aktualnej instancji asset-u materiału:
UCLASS (...)
class URAYAMaterialComponent : public USceneComponent
{
// ...
UPROPERTY ( EditAnywhere )
class URAYAMaterial * Material ;
};
Autor dodatkowo postanowił uniemozliwic uzytkownikowi mozliwosci doczepiania innych kom-
ponentów do komponentu materiału, gdyz, z logicznego punktu widzenia nie miało by to zadnego
sensu. Do tego celu posłuzyła metoda wirtualna CanAttachAsChild() w klasie USceneComponent,
która jest wywoływana przez silnik w momencie gdy uzytkownik próbuje dołaczyc jakis komponent
do danego. Metoda ta przyjmuje dołaczany komponent oraz jego nazwe jako parametry i zwraca
wartosc logiczna okreslajaca czy takie dołaczenie jest mozliwe (bazowa implementacja zwraca za-
wsze wartosc true). W zwiazku z tym, ze do komponentu materiału akustycznego nie powinien byc
dołaczany zaden typ komponentu, wystarczy, ze zwrócona zostanie zawsze wartosc logiczna false:
bool URAYAMaterialComponent :: CanAttachAsChild ( USceneComponent *, FName)
const
{
return false;
}
Ponadto, autor postanowił zakomunikowac uzytkownikowi, w przypadku gry ten próbuje doła-
czyc komponent materiału akustycznego do komponentu innego niz typu StaticMeshComponent lub
komponentu, który nie jest statyczny. Do tego celu posłuzyła wirtualna metoda komponentu sceny,
OnAttachmentChanged(), która wywoływana jest przez silnik na komponencie w momencie gdy
36
zmianie ulega komponent do którego jest dołaczony. W ponizszej implementacji nastepuje sprawdze-
nie czy oba powyzsze warunki sa spełnione i jezeli nie, w konsoli wyswietlony zostaje odpowiedni
komunikat:
void URAYAMaterialComponent :: OnAttachmentChanged ()
{
if ( AttachParent && ( AttachParent -> Mobility != EComponentMobility ::
Static || Cast < UStaticMeshComponent >( AttachParent ) == nullptr ))
{
UE_LOG ( LogRayavEditorModule , Warning , TEXT(" RAYAMaterial
components should only be attached to Static Mesh components
with static mobility "));
}
}
To czy transformacja danego komponentu moze sie zmieniac w trakcie rozgrywki reprezentowane
jest przez aktualna wartosc pola Mobility komponentu sceny. Wartosc typu EComponentMobility::
Static oznacza, ze dany komponent jest statyczny, czyli nie moze zmienic swojej transformacji.
4.2.3 Konfiguracja RAYA
Przed rozpoczeciem procesu wypalenia sceny akustycznej, konieczna jest konfiguracja silnika RAYA.
Celem tej konfiguracji jest odpowiednie przygotowanie lub zmiana domyslnych ustawien modułów
bioracych udział w procesie symulacji propagacji wiazek, w szczególnosci modułu geometrii. Czesc
konfiguracji jest obowiazkowa, np. podanie odpowiednich sciezek do plików wykorzystywanych
przez silnik, a czesc dotyczy parametrów, które bezposrednia wpływaja na jakosc i dokładnosc symu-
lacji, np. właczenie/wyłaczenie symulacji efektu dyfrakcji.
Konfiguracja silnika RAYA moze nastapic z pliku badz z poziomu kodu. W tym przypadku
konfiguracja nastepuje z poziomu kodu, na podstawie ustawien zawartych w obiekcie menadzera
(typ URAYAManager) otrzymanego za pomoca metody GetManagerForCurrentLevel() modułu edy-
tora (patrz 4.1.2]).
Parametry konfiguracyjne w bibliotece RAYA maja postac pary [nazwa parametru, wartosc] gdzie
nazwa parametru jest łancuchem znakowym, a wartosc moze byc róznego typu. Interfejs AudioOffline
biblioteki RAYA-offline oferuje zestaw przeładowanych metod dla róznych typów parametrów.
Ponizej przedstawiony jest wycinek kodu funkcji wewnatrz modułu edytora wtyczki SetRayaConfig
odpowiedzialnej za skonfigurowanie silnika RAYA z wykorzystaniem parametrów ustawionych przez
uzytkownika zawartych w strukturze typu FRAYAConfiguration bedacej czescia klasy menadzera
37
(4.1.2):
void SetRayaConfig ( raya_offline :: AudioOffline & AudioOffline , const
FRAYAConfiguration & RayaConfiguration )
{
AudioOffline . setConfig ("max beam generation ", RayaConfiguration .
MaxBeamGeneration );
AudioOffline . setConfig ("use varying beam accuracy ",
RayaConfiguration . bUseVaryingBeamAccuracy );
// pozostała część konfiguracji
}
Rysunek 8: Okno edycji aktora-menadzera wewnatrz edytora słuzace do konfiguracji ustawien dlaobecnie edytowanego poziomu.
4.2.4 Eksport sceny
W celu symulacji propagacji wiazek i na tej podstawie utworzenia mapy parametrów dzwiekowych
przez biblioteke RAYA-offline konieczne jest uprzednie przekazanie do niej informacji o geometrii
38
sceny i rozkładzie materiałów akustycznych. Proces ten w kontekscie niniejszej wtyczki nazwany
został eksportem sceny.
Eksport sceny sprowadza sie do zbudowania sceny akustycznej korzystajac z interfejsu biblio-
teki RAYA-offline na podstawie sceny akustycznej w Unreal Engine 4. W tym przypadku, na scene
akustyczna składaja sie tylko aktorzy (komponenty) typu Static Mesh wraz z ew. materiałem aku-
stycznym (asset RAYAMaterial) (patrz 4.2.2).
W celu zbudowania sceny, biblioteka RAYA-offline udostepnia interfejs SceneBuilder. Schemat
korzystania z tego interfejsu jest bardzo prosty: po zarejestrowaniu materiałów akustycznych uzyt-
kownik po prostu dodaje kolejne trójkaty składajace sie na dana siatke jednoczesnie podajac indeks
materiału akustycznego otrzymany przy jego rejestracji.
Problem eksportu sceny ogranicza sie zatem do ekstrakcji informacji o trójkatach z komponentu
Static Mesh. Na szczescie, interfejs tego komponentu umozliwia dostep do listy trójkatów składa-
jacych sie na jego siatke. W tym miejscu nalezy zaznaczyc, ze siatka w UE4 złozona jest z tzw.
“pod-siatek“ o róznej gestosci (szczegółowosci), w jez. ang. LOD (Level of Detail). Kazda z tych
pod-siatek posiada osobna liste trójkatów. Siatki o róznym poziomie szczegółowosci wykorzysty-
wane sa przez silnik w celu poprawy wydajnosci - wraz z oddalaniem sie kamery od obiektu ren-
derowana zostaje coraz mniej szczegółowa siatka. Z punktu widzenia eksportu sceny akustycznej,
interesujaca jest tylko siatka o najmniejszym stopniu szczegółowosci.
Ponizej przedstawiony jest uproszczony pseudokod algorytmu eksportu sceny:
SceneBuilder : rozpocznij budowe sceny
dla kazdego aktora AStaticMeshActor na scenie :
dla kazdego statycznego komponentu UStaticMeshComponent tego aktora :
jezeli posiada podpiety material URAYAMaterial :
SceneBuilder : zarejestruj material akustyczny jezeli nie
zarejestrowany
w przeciwnym wypadku , uzyj domyslny
SceneBuilder : rozpocznij nowa siatke
sposrod pod - siatek komponentu znajdz siatke o najmniejszej
szczegolowosci
dla kazdego trojkata tej siatki :
SceneBuilder : dodaj trojkat
SceneBuilder : zakoncz siatke
SceneBuilder : zakoncz budowe sceny
W implementacji załozono, ze wszystkie komponenty Static Mesh znajdujace sie na scenie beda
39
czescia aktora typu Static Mesh Actor. Autor zdecydował sie na takie załozenie, poniewaz wydaje
sie mało prawdopodobne aby uzytkownik dołaczał komponenty typu Static Mesh do aktorów innego
rodzaju1 W przeciwnym wypadku, konieczna byłaby iteracja po wszystkich obiektach na scenie (któ-
rych moze byc tysiace) w celu znalezienia komponentów typu statycznej siatki.
4.2.5 Wypalenie sceny akustycznej
Rozpoczecie procesu wypalania sceny akustycznej inicjowane jest przez uzytkownika z poziomu edy-
tora. Jego wynikiem jest utworzenie sceny akustycznej i zapisanie jej do reprezentujacego ja asset-u
klasy RAYALevelBakeAsset (4.1.1). Jak wspomniano w 3.3, za zarzadzanie tym procesem odpowie-
dzialny jest moduł edytora.
Po stworzeniu nowego obiektu klasy AudioOffline, konfiguracji silnika RAYA (patrz 4.2.3) i eks-
porcie sceny (4.2.4) nastepuje rozpoczecie wypalenie sceny akustycznej, inicjowane przez wywołanie
odpowiedniej metody klasy AudioOffline.
W zwiazku z tym, ze proces wypalania sceny akustycznej moze potrwac od kilku minut do kilku
godzin konieczne jest aby odbywał sie asynchronicznie, czyli na watku innym niz watek interfejsu
uzytkownika. W przeciwnym razie, uzytkownik po kliknieciu przycisku inicjujacego wypalenie sceny
nie bedzie mógł korzystac w tym czasie z edytora.
W celu zarzadzania procesem wypalania sceny zaimplementowano obiekt menedzera, reprezento-
wany przez klase FRAYABakeManager. Zadaniem obiektu tej klasy jest stworzenie nowego watku, tzw.
roboczego z którego rozpoczynany bedzie proces wypalania sceny oraz utworzenie asset-u wypalonej
sceny po zakonczeniu procesu wypalania. Oprócz tego, obiekt tej klasy pełni role posrednika pomie-
dzy interfejsem uzytkownika a watkiem roboczym: do jego zadan nalezy wyswietlanie komunikatów
informujacych uzytkownika o obecnym stanie wypalania oraz ew. zatrzymanie watku roboczego na
zadanie uzytkownika.
W projekcie załozono, ze nie bedzie mozliwosci jednoczesnego wypalania kilku scen akustycz-
nych. W zwiazku z tym klasa FRAYABakeManager zaimplementowana jest jako tzw. signgleton. Sin-
gleton to kreacyjny wzorzec projektowy, który ogranicza mozliwosc tworzenia wiecej niz jednej in-
stancji danego obiektu oraz umozliwia globalny dostep do tego obiektu.
Klasa reprezentujaca tzw. obiekt robotnik (ang. worker), nazwana została FRAYABakeWorker.
Klasa ta dziedziczy z klasy bazowej FRunnable bedacej czescia API Unreal Engine 4, która reprezen-
tuje obiekt wykonujacy pewna “prace“ na pewnym watku. Kluczowymi elementami tego interfejsu
1Wniosek ten wysnuty został na podstawie studiowania kodu zródłowego silnika oraz oficjalnej dokumentacji, wktórej panuje powszechne załozenie, ze obiekty o statycznej geometrii reprezentowane sa poprzez aktora typu StaticMesh Actor [11]
40
sa trzy wirtualne metody: Init(), Run() i Exit(), wywoływane w tej kolejnosci. W metodzie Init()
obiekt robotnik powinien dokonac swojej inicjalizacji, w Exit() zwolnic wszystkie uzywane zasoby
natomiast własciwa praca powinna odbyc sie w metodzie Run(). W tym przypadku w metodzie Run()
odbywa sie wywołanie metody rozpoczynajacej wypalanie sceny.
Aby uzytkownik posiadał mozliwosc sledzenia postepu wypalania sceny (jak juz wczesniej wspo-
mniano proces ten moze trwac bardzo długo), w tym czasie na ekranie wyswietlone zostaja komuni-
katy, w postaci nieinwazyjnych, małych okienek (przedstawione na rysunku 9). Okna te sa czescia
standardowego systemu notyfikacji wykorzystywanego przez silnik do wyswietlania postepu długo
trwajacych procesów, np. wypalania statycznego oswietlenia.
W celu implementacji tej funkcjonalnosci obiekt klasy FRAYABakeWorker wykorzystuje tzw. obiekty
delegujace (ang. delegate). W skrócie, zadaniem delegatów jest oddelegowanie pewnego zadania do
innej czesci programu. Odbywa sie to poprzez tzw. powiazanie (ang. binding) metody/funkcji z
obiektem delegata. Obiekt delegata nie wie co dana funkcja lub metoda robi, jego przeznaczeniem
jest tylko jej wywołanie. W tym przypadku, obiekt klasy FRAYABakeWorker przechowuje dwa obiekty
delegujace: jeden odpowiedzialny za oddelegowanie w momencie zwiekszenia postepu (procento-
wego) wypalania sceny akustycznej, drugi za oddelegowanie w momencie zakonczenia sukcesem.
Obiekt menedzera, w momencie tworzenia obiektu robotnika wiaze swoje metody odpowiedzialne za
wyswietlanie notyfikacji z odpowiednimi delegatami.
Ponizej przedstawiony jest kod metody Run() klasy FRAYABakeWorker:
uint32 FRAYABakeWorker :: Run ()
{
// Utworzenie funkcji anonimowej wywoływanej podczas zmiany postępu
// Progress - procentowa wartość postępu
auto ProgressCallback = [&]( unsigned int Progress )
{
auto OldValue = PercentCompleted .Set( Progress );
// Jeżeli z delegatem powiązana jest jakaś funkcja, oddeleguj
if (( int32) Progress > OldValue && OnBakeProgressDelegate . IsBound ()
)
OnBakeProgressDelegate . Execute ();
};
// Rozpocznij wypalanie sceny
AudioOffline ->bake (* BakeSettings , *BakeOutput , ProgressCallback );
41
// Jeżeli użytkownik nie anulował i z delegatem reprezentującym pomyślne
// zakończenie wypalania powiązana jest jakaś funkcja, oddeleguj
if (! Canceled && OnBakeCompletedDelegate . IsBound ())
OnBakeCompletedDelegate . Execute ();
IsFinished = true;
return 0;
}
Metoda bake() klasy AudioOffline przyjmuje jako parametr strukture zawierajaca parametry konfi-
guracyjne procesu wypalania, wektor (dynamiczna tablice) do którego zapisany zostanie ciag bajtów
reprezentujacy zdeserializowany obiekt wypalonej sceny akustycznej oraz anonimowa funkcje (tzw.
lambda) która jest wywoływana w momencie zwiekszenia postepu procesu wypalania sceny aku-
stycznej.
Proces rozpoczecia wypalania sceny akustycznej inicjowany jest przez wywołanie na obiekcie
menedzera FRAYABakeManager metody StartBake() która przyjmuje instancje obiektu AudioOffline
jako parametr. Wewnatrz metody tej nastepuje utworzenie nowego obiektu roboczego, pobranie pa-
rametrów dot. konfiguracji samego procesu wypalania (liczba watków oraz długosc boku komórki
determinujaca gestosc dyskretyzacji sceny) z obiektu aktora-menedzera oraz powiazanie odpowied-
nich delegatów.
Po zakonczeniu procesu wypalenia sceny akustycznej, czyli powrocie z metody bake(), wywo-
ływany jest delegat OnBakeCompletedDelegate. Z tym delegatem powiazana jest metoda obiektu
FRAYABakeManager odpowiedzialna za utworzenie i zapisanie asset-u wypalonej sceny akustycznej
oraz wyswietlenie okienka notyfikacji informujacego o pozytywnie zakonczonym procesie wypala-
nia.
Rysunek 9: Notyfikacje o postepie procesu wypalania sceny akustycznej wyswietlane wewnatrz edy-tora.
42
4.2.6 Kompresja
Duzym problemem zwiazanym z wypalona scena akustyczna była ilosc zajmowana przez nia pa-
mieci. Jak juz wspomniano w 3.2, parametry wzgledne zmiany dzwieku, mianowicie wzmocnie-
nie amplitudy, wzmocnienie najwyzszych czestotliwosci oraz kierunek dzwieku przechowywane we-
wnatrz struktury sceny akustycznej zaleza od dwóch zmiennych: komórki odbiornika i komórki zró-
dła dzwieku. Oznacza to wiec, ze ilosc pamieci potrzebna do ich przechowania jest wprost propor-
cjonalna do kwadratu liczby komórek na które podzielona została scena.
Przykładowo, gry poczatkowo parametry te przechowywano jako typ dwu-bajtowy ze znakiem1 to
ilosc pamieci potrzebna do reprezentacji jednego zestawu tych parametrów wynosiła 2+2+(2 ·3) =
10 bajtów (kierunek reprezentowany jest przez wektor trzech współrzednych). Zatem, dla przykłado-
wej, “małej“ sceny o wymiarach 30mx30mx5m i długosci boku pojedynczej komórki równej 1 metra,
ilosc komórek na które zostanie podzielona scena wynosi 30 · 30 · 5 = 4500. Ostatecznie wiec, ilosc
pamieci potrzebna w tym przypadku do przechowania ww. parametrów to 45002 · 10 bajtów, czyli
≈ 193MB. Łatwo sobie wyobrazic, ze w przypadku gdy developer bedzie chciec dokonac gestszego
podziału sceny lub scena bedzie wieksza (podane powyzej rozmiary sceny sa stosunkowo małe na
dzisiejsze standardy gier) to ilosc pamieci zajmowanej na dysku/wczytanej do RAM przez wypalona
scene akustyczna moze byc rzedu gigabajtów. Nalezy równiez wsiasc pod uwage, ze typowa gra
składa sie z wielu poziomów (scen), rzedu kilkunastu. Przy takiej implementacji same pliki wypalo-
nej sceny akustycznej mogłyby przerosnac swoim rozmiarem pozostała czesc gry.
W celu rozwiazania tego problemu postanowiono zastosowac kompresje tych danych. Kazdy z
wyzej wymienionych parametrów wzglednej zmiany dzwieku mozna przedstawic w postaci macie-
rzy, gdzie kolumny/wiersze odpowiadaja komórkom odbiornika/zródła. W zwiazku z tym, zastoso-
wana metoda kompresji bedzie pewnym rodzajem kompresji macierzy. Istnieje wiele algorytmów
kompresji macierzy, dotycza one jednak tzw. macierzy rzadkich, czyli takich, których wiekszosc
elementów jest zerowa. W przypadku macierzy parametrów wzglednej zmiany dzwieku miedzy ko-
mórkami, zdecydowana wiekszosc elementów nie bedzie zerowa, poniewaz dzwiek z jednej komórki
dociera do wiekszosci, a nawet wszystkich pozostałych. Zatem zastosowany algorytm kompresji
musiał byc rodzajem kompresji macierzy gestej.
Popularna i zarazem łatwa metoda kompresji macierzy gestych, szczególnie stosowanej w dzie-
dzinie przetwarzania obrazów jest kompresja z wykorzystaniem rozkładu na według wartosci oso-
bliwych (ang. Singular value decomposition, w skrócie SVD). SVD to rozkład macierzy na iloczyn
trzech specyficznych macierzy. Zgodnie z teoria, kazda macierz o wartosciach rzeczywistych lub
1Docelowo parametry te w API silnika UE4 sa typu zmiennoprzecinkowego pojedynczej precyzji (4 bajty).
43
zespolonych mozna przedstawic za pomoca rozkładu SVD. Zakładajac rzeczywista macierz M o wy-
miarach m x n, jej rozkład według wartosci osobliwych dany jest przez
M = USV∗
gdzie
• U - macierz ortogonalna o wymiarach m x m
• S - macierz diagonalna o wymiarach m x n
• V - macierz ortogonalna o wymiarach n x n
Wartosci na przekatnej macierzy S nazywane sa wartosciami osobliwymi macierzy M. Jezeli
posortowane one sa według malejacych wartosci, to poprzez eliminacje kolejnych (od najmniejszych)
wartosci osobliwych i odpowiadajacych im kolumn/wierszy z macierzy U i V otrzymywana zostanie
coraz gorsza, według kryterium błedu srednio-kwadratowego, aproksymacja oryginalnej macierzy M.
Fakt ten jest istota metody kompresji za pomoca SVD.
Algorytm obliczenia SVD nie jest istotny z punktu widzenia tej pracy, poniewaz dostepne sa bi-
blioteki programistyczne do algebry liniowej obliczajace SVD. Autor do implementacji kompresji
sceny akustycznej zdecydował sie skorzystac z biblioteki Eigen1. Jest to popularna i ceniona biblio-
teka w jezyku C++ do algebry liniowej. Jej zaleta jest fakt, iz jest w całosci szablonowa, czyli w celu
jej dołaczenia do projektu wystarczy załaczenie pliku nagłówkowych. Waznym aspektem przy wybo-
rze tej biblioteki była tez jej licencja - biblioteka Eigen jest obecnie na licencji MPL22, która pozwala
na uzywanie jej w komercyjnych aplikacjach bez koniecznosci dystrybucji kodu zródłowego.
Natura algorytmów do obliczania SVD jest wspieranie tylko macierzy o wartosciach typu zmien-
noprzecinkowego. Algorytm biblioteki Eigen nie jest tutaj wyjatkiem. Zatem aby mozliwe było za-
stosowanie kompresji konieczna była uprzednia zamiana parametrów z reprezentacji stałoprzecinko-
wej (dwu-bajtowej) na zmiennoprzecinkowa pojedynczej precyzji. Warto w tym miejscu zaznaczyc,
ze kompresji podlegaja tylko dwa parametry: wzmocnienie amplitudy i najwyzszych czestotliwosci.
Zrezygnowano z kompresji informacji o kierunku ze wzgledu na wyraznie słyszalne błedy kompresji.
Implementacja algorytmu kompresji odbywa sie w bibliotece RAYA-offline. Kompresja doko-
nywana jest po wypaleniu sceny akustycznej. Aby umozliwic uzytkownikowi intuicyjna kompresje
asset-u wypalonej sceny akustycznej z poziomu edytora, stworzono do tego celu specjalna klase ak-
tora. Aktor ten, podobnie jak aktor-menedzer (patrz 4.1.2 nie jest ustawiany na scenie, jest tylko wy-
1http://eigen.tuxfamily.org/index.php?title=Main_Page2https://www.mozilla.org/en-US/MPL/2.0/
44
swietlany w oknie edycji aktora. Posiada on tylko trzy pola: wskaznik (typu URAYALevelBakeAsset*)
do asset-u nie-skompresowanej sceny akustycznej, wskaznik na asset do którego zostanie zapisana
skompresowana scena akustyczna oraz pole reprezentujace stopien kompresji, przyjmujace wartosci
od 0 (brak kompresji) do 1 (maksymalna kompresja). W momencie gdy uzytkownik zmienia ten
parametr, implementacja wywołuje odpowiednia metode wewnatrz biblioteki RAYA-offline, która
dokonuje rozkładu za pomoca SVD usuwajac odpowiednia ilosc wartosci osobliwych. Ilosc usunie-
tych wartosci osobliwych jest wprost proporcjonalna do ww. stopnia kompresji. Klase reprezentujacy
obiekt słuzacy do kompresji sceny akustycznej nazwano ARAYABakeAssetCompressor.
Rysunek 10: Okno edycji obiektu klasy ARAYABakeAssetCompressor wewnatrz edytora.
5 Zakonczenie
W pracy tej przedstawiono sposób i mozliwosci integracji biblioteki RAYA z silnikiem Unreal En-
gine 4. Za pomoca integracji tej udało sie rozszerzyc funkcjonalnosc silnika w aspekcie kreowania
dzwieku o automatyczna zmiane niektórych parametrów dzwieku na podstawie rzeczywistej geome-
trii sceny i materiałów akustycznych na biezaco podczas rozgrywki.
Integracji dokonano za pomoca systemu wtyczek Unreal Engine 4 pozwalajacy na rozszerzenie
funkcjonalnosci edytora i silnika bez koniecznosci edycji kodu zródłowego. Dzieki temu projektant
dzwieku chcac wykorzystac funkcjonalnosc biblioteki RAYA w swoim projekcie nie musi porzucac
ani zmieniac znanego mu srodowiska pracy.
Oprócz samej funkcjonalnosci zwiazanej z automatyczna zmiana dzwieku, zaimplementowano
system pozwalajacy uzytkownikowi na utworzenie i zarzadzanie biblioteka materiałów akustycznych
z poziomu edytora poprzez integracje z systemem asset-ów Unreal Engine 4.
W zwiazku z przyjetymi załozeniami, duzym problemem podczas implementacji była znaczna
ilosc pamieci potrzebna do przechowania informacji na temat parametrów dzwiekowych wykorzy-
45
stywanych do modyfikacji dzwieku w czasie rozgrywki. Problem ten rozwiazano za pomoca metody
kompresji z wykorzystaniem rozkładu na wartosci osobliwe.
Do propozycji dalszej pracy mozna by zaliczyc m.in przetestowanie wydajnosci obecnej imple-
mentacji przy jednoczesnej obecnosci duzej ilosci zródeł dzwieku oraz ulepszenie implementacji
zmiennego efektu pogłosu poprzez kontrole wiekszej ilosci parametrów. Istotnym rozszerzeniem
obecnej funkcjonalnosci mogłoby byc równiez uwzglednianie geometrii obiektów reprezentujacych
krajobraz w scenie akustycznej.
46
Bibliografia
[1] B. Ziółko, T. Pedzimaz, Sz. Pałka, I. Gawlik, B. Miga , P. Bugiel. „Real-time 3D Audio Simu-
lation in Video Games with RAYAV”. W: Making Games vol.1 (2015).
[2] B.Miga, B.Ziółko. „Real-time acoustic phenomena modelling for computer games audio en-
gine”. W: Archives of Acoustics (IF) vol. 2 (2015).
[3] Michael Noland. Unreal Property System (Reflection). Dostep: 18.01.2016. URL: https://
www.unrealengine.com/blog/unreal-property-system-reflection.
[4] Unreal Engine 4 Documentation, Actors. Dostep: 18.01.2016. URL: https://docs.unrealengine.
com/latest/INT/Programming/UnrealArchitecture/Actors/index.html.
[5] Unreal Engine 4 Documentation, Audio System Overview. Dostep: 18.01.2016. URL: https:
//docs.unrealengine.com/latest/INT/Engine/Audio/Overview/index.html.
[6] Unreal Engine 4 Documentation, Coding Standard, Naming Conventions. Dostep: 18.01.2016.
URL: https://docs.unrealengine.com/latest/INT/Programming/Development/
CodingStandard/index.html#namingconventions.
[7] Unreal Engine 4 Documentation, Components. Dostep: 18.01.2016. URL: https://docs.
unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/Components/
index.html.
[8] Unreal Engine 4 Documentation, Level Editor. Dostep: 18.01.2016. URL: https://docs.
unrealengine.com/latest/INT/Engine/UI/LevelEditor/index.html.
[9] Unreal Engine 4 Documentation, Sound Cue Editor. Dostep: 18.01.2016. URL: https : / /
docs.unrealengine.com/latest/INT/Engine/Audio/SoundCues/Editor/index.
html.
[10] Unreal Engine 4 Documentation, Sound Cue Reference. Dostep: 18.01.2016. URL: https:
//docs.unrealengine.com/latest/INT/Engine/Audio/SoundCues/NodeReference/
index.html.
[11] Unreal Engine 4 Documentation, Static Mesh Actors. Dostep: 18.01.2016. URL: https://
docs.unrealengine.com/latest/INT/Engine/Actors/StaticMeshActor/index.
html.
[12] Unreal Engine 4 Documentation, Unreal Build System. Dostep: 18.01.2016. URL: https :
//docs.unrealengine.com/latest/INT/Programming/UnrealBuildSystem/index.
html.
47
[13] I. Gawlik, T. Pedzimaz, Sz. Pałka, B. Ziółko. „Efficient Vectorized Architecture for Feedback
Delay Network Reverberator with Policy Based Design”. W: SPA Conference, Poznan, 2015.
[14] Sz. Pałka, B. Głut, B Ziółko. „Visibility determination in beam tracing with application to
real-time sound simulation”. W: Computer Science, AGH vol.15.2 (2014).
48