Dance Tonite w WebVR

Byłem podekscytowany, gdy zespół Google Data Arts zwrócił się do firmy Moniker i ja zaproponował, że możemy wspólnie badać możliwości oferowane przez WebVR. Przez lata obserwowałem pracę ich zespołu, a ich projekty zawsze mnie intrygują. Dzięki współpracy z twórcą LCD Soundsystem i jej fanami powstała z udziałem Dance Tonite, stale zmieniającego się doświadczenia tanecznego w technologii VR. Osiągnęliśmy to w następujący sposób.

Wprowadzenie

Zaczęliśmy od opracowania serii prototypów z wykorzystaniem WebVR – otwartego standardu umożliwiającego korzystanie z rzeczywistości wirtualnej z poziomu przeglądarki. Chcemy ułatwić wszystkim korzystanie z rzeczywistości wirtualnej, niezależnie od urządzenia.

Wzięliśmy to sobie do serca. To, co opracowaliśmy, powinno działać we wszystkich typach VR – od gogli do rzeczywistości wirtualnej współpracujących z telefonami komórkowymi (np. Google Daydream View, Cardboard i Samsung Gear VR) po systemy przeznaczone do dużych pomieszczeń, takie jak HTC VIVE i Oculus Rift, które odzwierciedlają Twoje ruchy w wirtualnym środowisku. A przede wszystkim uznaliśmy, że w dumie internetu najbardziej przydatne byłoby stworzenie czegoś, co spodoba się każdemu, kto nie ma urządzenia do rzeczywistości wirtualnej.

1. Robienie zdjęć „zrób to sam”

Chcieliśmy zaangażować kreatywnie użytkowników, dlatego zaczęliśmy rozważać możliwości uczestnictwa i wyrażania siebie za pomocą rzeczywistości wirtualnej. Zaskoczyło nas, jak precyzyjnie można się poruszać i rozglądać w rzeczywistości wirtualnej oraz jak bardzo jest ona wierna. To podsunęło nam pomysł. Zamiast zmuszania użytkowników do patrzenia lub tworzenia czegoś, możemy nagrywać ich ruchy?

Ktoś nagrywa się w Dance Tonite. Ekran za nimi pokazuje to, co widać na ich słuchawkach.

Przygotowaliśmy prototyp, w którym podczas tańca rejestrowaliśmy położenie naszych gogli i kontrolerów VR. Zastąpiliśmy zarejestrowane pozycje abstrakcyjnymi kształtami i byliśmy zachwyceni wynikami. Wyniki były bardzo ludzkie i prezentowały tak wiele osobowości! Szybko zdaliśmy sobie sprawę, że możemy za pomocą WebVR robić tanie zdjęcia ruchome w domu.

W przypadku WebVR deweloper może za pomocą obiektu VRPose określić pozycję i orientację głowy użytkownika. Ta wartość jest aktualizowana każdej klatki przez sprzęt do rzeczywistości wirtualnej, aby Twój kod mógł renderować nowe klatki z właściwego punktu widzenia. Dzięki interfejsowi GamePad API z WebVR mamy też dostęp do pozycji/orientacji kontrolerów za pomocą obiektu GamepadPose. Wszystkie te wartości pozycji i orientacji zapisujemy po prostu w każdej klatce, co umożliwia nagranie ruchu użytkownika.

2. Minimalizm i kostiumy

Dzięki współczesnemu sprzętowi do rzeczywistości wirtualnej z użyciem wagi pomieszczeń możemy śledzić 3 punkty ciała użytkownika: głowę i dwie dłonie. W grze Dance Tonite chcemy skupić się na ludzkości w ruchu tych 3 punktów w przestrzeni kosmicznej. Aby osiągnąć ten cel, zadbaliśmy o jak najprostszy układ estetyczny, aby móc skupić się na ruchu. Spodobał nam się pomysł wykorzystania mózgów użytkowników.

Ten film demonstrujący pracę szwedzkiego psychologa Gunnara Johanssona był jednym z przykładów, o których wspomnieliśmy, gdy rozważaliśmy skrócenie różnych materiałów. Pokazuje, jak unoszące się białe kropki są natychmiast rozpoznawane jako ciała w ruchu.

Kolorowe pokoje i geometryczne stroje zainspirowały nas do tego, by odtworzyć balet triadyczny Oskara Schlemmera w 1970 roku.

Podczas gdy Schlemmer wybrał abstrakcyjne stroje geometryczne, chciał ograniczyć ruchy jego tancerzy do marionetek i marionetek, natomiast w przypadku Dance Tonite mieliśmy przeciwny cel.

Decyzję o tym, jak kształtować, wybraliśmy na podstawie ilości informacji przekazanych przez rotację. Kula wygląda tak samo niezależnie od obrócenia, ale stożek naprawdę wygląda w kierunku, w który patrzy, i wygląda inaczej niż z przodu.

3. Pedał pętlą do ruchu

Chcieliśmy pokazać duże grupy nagrywanych osób, które tańczą i poruszają się razem. Nie byłoby to możliwe, bo urządzeń VR nie używa się wystarczająco dużo urządzeń. Nadal jednak chcieliśmy, aby grupy ludzi reagowały na siebie za pomocą ruchu. W myślach Normana McClarena wystąpił rekurencyjny występ w filmie „Canon” z 1964 r.

W występie McClarena pojawia się seria dobrze dobranych ruchów, które po każdej pętli wchodzą w interakcje ze sobą. Chcieliśmy sprawdzić, czy możemy stworzyć środowisko, w którym użytkownicy będą mogli swobodnie improwizować luźne wersje występów.

4. Połączone pokoje

Połączone pokoje

Podobnie jak wiele innych utworów muzycznych, ścieżki dźwiękowe z LCD Soundsystem są tworzone z precyzyjnymi pomiarami w czasie. Utwór Tonite, który występuje w naszym projekcie, zawiera wskaźniki trwające dokładnie 8 sekund. Chcieliśmy, aby użytkownicy wykonali każdą 8-sekundową pętlę na ścieżce. Choć rytm tych środków się nie zmienia, ich treści muzyczne już tak. W miarę pokonywania utworu pojawiają się momenty z różnymi instrumentami i wokalami, na które wykonawcy mogą reagować w różny sposób. Każdy z tych środków ma charakter pokoju, w którym ludzie mogą wykonywać dopasowane do niego występy.

Optymalizacja skuteczności: nie usuwaj klatek

Stworzenie wieloplatformowego środowiska VR, które działa na jednej bazie kodu, z optymalną wydajnością dla każdego urządzenia lub platformy nie jest łatwe.

Największą mdłością podczas korzystania z VR jest liczba klatek, która nie nadąża za ruchem. Jeśli odwrócisz głowę, ale obraz nie będzie odpowiadać ruchowi Twojego ucha wewnętrznego, będzie to chwilowe skurcze żołądka. Dlatego trzeba było uniknąć dużego opóźnienia liczby klatek. Oto niektóre z wprowadzonych przez nas optymalizacji.

1. Geometria bufora instancji

Ponieważ w całym naszym projekcie wykorzystano tylko kilka obiektów 3D, dzięki zastosowaniu geometrii bufora instancji możemy znacząco zwiększyć wydajność. Zasadniczo umożliwia on jednorazowe przesłanie obiektu do GPU i rysowanie dowolnej liczby jego „wystąpień” w ramach jednego wywołania rysowania. W tańcowym Tonicie mamy tylko 3 różne obiekty (grożek, walc i pomieszczenie z otworem), ale potencjalnie nawet setki ich kopii. Geometria bufora instancji jest częścią zestawu ThreeJS, ale użyliśmy rozwiązań eksperymentalnych i w trakcie przetwarzania Dusan Bosnjak z implementacją THREE.InstanceMesh, co znacznie ułatwia pracę z geometrią bufora instancji.

2. Unikam kolektora śmieci

Podobnie jak w przypadku wielu innych języków skryptów JavaScript automatycznie zwalnia pamięć, sprawdzając, które przydzielone obiekty nie są już używane. Ten proces jest nazywany czyszczeniem śmieci.

Deweloperzy nie mają kontroli nad tym, kiedy to nastąpi. Specjalista od odpadów może w każdej chwili przyjść do naszych drzwi i zacząć opróżniać śmieci. W efekcie w miarę przyjemnej chwili pozbywa się śmieci.

Rozwiązaniem jest recykling obiektów – liczba odpadów nie jest generowana jak najmniejsza ilość śmieci. Zamiast tworzyć nowy obiekt wektorowy do każdego obliczenia, oznaczyliśmy obiekty w formacie rysunkowym do ponownego wykorzystania. Ponieważ przenosimy nasze odwołania do tych treści poza nasz zakres, nie oznaczamy ich usunięcia.

Poniżej znajduje się na przykład kod do konwersji macierzy lokalizacji głowy i dłoni użytkownika na tablicę wartości pozycji i obrotu, które przechowujemy w każdej klatce. Stosując ponownie funkcje SERIALIZE_POSITION, SERIALIZE_ROTATION i SERIALIZE_SCALE, unikamy alokacji pamięci i czyszczenia pamięci, które miałyby miejsce, jeśli utworzyli nowe obiekty przy każdym wywołaniu funkcji.

const SERIALIZE_POSITION = new THREE.Vector3();
const SERIALIZE_ROTATION = new THREE.Quaternion();
const SERIALIZE_SCALE = new THREE.Vector3();
export const serializeMatrix = (matrix) => {
    matrix.decompose(SERIALIZE_POSITION, SERIALIZE_ROTATION, SERIALIZE_SCALE);
    return SERIALIZE_POSITION.toArray()
    .concat(SERIALIZE_ROTATION.toArray())
    .map(compressNumber);
};

3. Serializuję ruch i odtwarzanie progresywne

Aby rejestrować ruch użytkowników w rzeczywistości wirtualnej, musieliśmy zserializować położenie i obrót ich gogli oraz kontrolerów, a potem przesłać te dane na nasze serwery. Zaczęliśmy rejestrować pełne macierze przekształceń w każdej klatce. Było to skuteczne, ale po pomnożeniu 16 liczb przez 3 pozycje przy 90 klatkach na sekundę spowodowało to utworzenie bardzo dużych plików i długie oczekiwanie na przesłanie i pobranie danych. Wyodrębniając z jednostek transformacji tylko dane pozycjonujące i obrotowe, udało nam się obniżyć te wartości z 16 do 7.

Użytkownicy internetu często klikają linki, nie wiedząc, czego dokładnie się spodziewać, dlatego treści wizualne muszą być szybko pokazywane, ponieważ w przeciwnym razie opuszczają stronę w ciągu kilku sekund.

Chcieliśmy mieć pewność, że nasz projekt rozpocznie się jak najszybciej. Początkowo do wczytywania danych dotyczących ruchu używaliśmy formatu JSON. Problem polega na tym, że zanim da się przeanalizować plik JSON, musimy wczytać cały plik. Niezbyt progresywne.

Aby projekt taki jak Dance Tonite wyświetlała się z najwyższą możliwą liczbą klatek, w obliczeniach JavaScriptu obowiązuje krótki czas trwania każdej klatki. Jeśli wydłużysz ten czas, animacje zaczynają się zacinać. Początkowo występowały zacinanie się, ponieważ duże pliki JSON były dekodowane przez przeglądarkę.

Natknęliśmy się na wygodny format strumieniowania danych o nazwie NDJSON lub JSON z rozdzielanym znakami nowego wiersza. Sztuczna inteligencja polega na utworzeniu pliku z serią prawidłowych ciągów JSON, z których każdy znajduje się w osobnym wierszu. Pozwala to przeanalizować plik podczas ładowania i zobaczyć wyniki, zanim zostaną w pełni wczytane.

Tak wygląda sekcja jednego z naszych nagrań:

{"fps":15,"count":1,"loopIndex":"1","hideHead":false}
[-464,17111,-6568,-235,-315,-44,9992,-3509,7823,-7074, ... ]
[-583,17146,-6574,-215,-361,-38,9991,-3743,7821,-7092, ... ]
[-693,17158,-6580,-117,-341,64,9993,-3977,7874,-7171, ... ]
[-772,17134,-6591,-93,-273,205,9994,-4125,7889,-7319, ... ]
[-814,17135,-6620,-123,-248,408,9988,-4196,7882,-7376, ... ]
[-840,17125,-6644,-173,-227,530,9982,-4174,7815,-7356, ... ]
[-868,17120,-6670,-148,-183,564,9981,-4069,7732,-7366, ... ]
...

Dzięki użyciu NDJSON możemy zachować reprezentację danych poszczególnych klatek wyników w formie ciągów znaków. Możemy zaczekać, aż dojdziemy do potrzebnego czasu, i zdekodować je na dane pozycjonujące, aby rozłożyć potrzebne przetwarzanie na przestrzeni czasu.

4. Interpolacja ruchu

Ponieważ chcieliśmy wyświetlić od 30 do 60 występów jednocześnie, musieliśmy zmniejszyć szybkość transmisji danych jeszcze bardziej niż dotychczas. Zespół Data Arts zajął się tym samym problemem w ramach projektu Virtual Art Sessions, w ramach którego odtwarza nagrania artystów malowanych w rzeczywistości wirtualnej za pomocą aplikacji Tilt Brush. Problem został rozwiązany, tworząc pośrednie wersje danych użytkownika z niższą liczbą klatek i stosując interpolację między klatkami podczas ich odtwarzania. Zaskoczyło nas, że trudno było dostrzec różnicę między nagraniem z interpolacją z szybkością 15 FPS a oryginalnym nagraniem 90 FPS.

Aby się przekonać, możesz wymusić odtwarzanie danych w Dance Tonite z różną szybkością, korzystając z ciągu zapytania ?dataRate=. Możesz użyć tych informacji, aby porównać zarejestrowany ruch z 90 klatkami na sekundę, 45 klatek na sekundę lub 15 klatkami na sekundę.

Jeśli chodzi o położenie, stosujemy interpolację liniową między poprzednią a następną klatką kluczową w zależności od tego, jak bardzo blisko siebie znajdujemy się w czasie między klatkami kluczowymi (współczynnik):

const { x: x1, y: y1, z: z1 } = getPosition(previous, performanceIndex, limbIndex);
const { x: x2, y: y2, z: z2 } = getPosition(next, performanceIndex, limbIndex);
interpolatedPosition = new THREE.Vector3();
interpolatedPosition.set(
    x1 + (x2 - x1) * ratio,
    y1 + (y2 - y1) * ratio,
    z1 + (z2 - z1) * ratio
    );

Na potrzeby orientacji używamy sferycznej interpolacji liniowej (slerp) między klatkami kluczowymi. Orientacja jest przechowywana w postaci kwartionów.

const quaternion = getQuaternion(previous, performanceIndex, limbIndex);
quaternion.slerp(
    getQuaternion(next, performanceIndex, limbIndex),
    ratio
    );

5. Synchronizuję ruchy z muzyką

Aby ustalić, która klatka nagranej animacji odtworzyć, musimy znać aktualny czas odtwarzania muzyki z dokładnością do milisekundy. Okazuje się jednak, że chociaż element audio HTML doskonale nadaje się do stopniowego wczytywania i odtwarzania dźwięku, udostępniana właściwość czasu nie zmienia się synchronicznie z pętlą klatek przeglądarki. Zawsze jest trochę za późno. Czasem o ułamek ms za wcześnie, a czasem o trochę za późno.

To prowadzi do zacinania się w naszych pięknych nagraniach tanecznych, których chcemy za wszelką cenę uniknąć. Aby temu zaradzić, wdrożyliśmy nasz własny licznik czasu w JavaScript. W ten sposób mamy pewność, że odstęp czasu między klatkami jest dokładnie taki sam jak czas, który upłynął od ostatniej klatki. Kiedy licznik czasu przekroczy 10 ms, synchronizujemy z muzyką ponownie.

6. Uśmiech i mgła

Każda historia musi mieć dobre zakończenie, a chcieliśmy zrobić coś zaskakującego dla użytkowników, którzy dotarli do końca serii. Wychodząc z ostatniego pokoju, wejdziesz w spokojny krajobraz pełen stożków i walców. „Czy to koniec?”, zastanawiasz się. W miarę, jak wkraczasz w teren, nagle dochodzi do rytmu muzyki, która powoduje, że różne grupy pachołków i walców tworzą tancerzy. Znajdujesz się w samym środku wielkiej imprezy! Gdy muzyka gwałtownie się wycisza, wszystko spada na ziemię.

Było to świetnie dla widzów, ale przyszło kilka problemów z wydajnością. Wysokiej klasy urządzenia VR działająca na skalę pomieszczeń i ich wysokiej klasy sprzęty do gier świetnie się bawiły – do naszego nowego zakończenia zastosowaliśmy 40 wyjątkowych, dodatkowych funkcji. Na niektórych urządzeniach mobilnych liczba klatek spadła jednak o połowę.

Aby temu zapobiec, wprowadziliśmy mgłę. Po pewnej odległości wszystko powoli stają się czarne. Nie musimy obliczać ani rysować danych, które nie są widoczne, dlatego pobieramy dane o wydajności w niewidocznych pomieszczeniach, co pozwala nam zaoszczędzić czas poświęcany zarówno na procesorze, jak i na GPU. Jak jednak ustalić odpowiednią odległość?

Niektóre urządzenia potrafią wszystko, co do nich rzucasz, a inne są bardziej restrykcyjne. Zdecydowaliśmy się na przesuwaną wagę. Stale mierząc liczbę klatek na sekundę, możemy odpowiednio dostosować odległość mgły. Jeśli liczba klatek działa płynnie, próbujemy kontynuować renderowanie, wypchając mgłę. Jeśli liczba klatek na sekundę nie jest wystarczająco płynna, przybliżamy mgłę, co pozwoli nam pominąć renderowanie w ciemności.

// this is called every frame
// the FPS calculation is based on stats.js by @mrdoob
tick: (interval = 3000) => {
    frames++;
    const time = (performance || Date).now();
    if (prevTime == null) prevTime = time;
    if (time > prevTime + interval) {
    fps = Math.round((frames * 1000) / (time - prevTime));
    frames = 0;
    prevTime = time;
    const lastCullDistance = settings.cullDistance;

    // if the fps is lower than 52 reduce the cull distance
    if (fps <= 52) {
        settings.cullDistance = Math.max(
        settings.minCullDistance,
        settings.cullDistance - settings.roomDepth
        );
    }
    // if the FPS is higher than 56, increase the cull distance
    else if (fps > 56) {
        settings.cullDistance = Math.min(
        settings.maxCullDistance,
        settings.cullDistance + settings.roomDepth
        );
    }
    }

    // gradually increase the cull distance to the new setting
    cullDistance = cullDistance * 0.95 + settings.cullDistance * 0.05;

    // mask the edge of the cull distance with fog
    viewer.fog.near = cullDistance - settings.roomDepth;
    viewer.fog.far = cullDistance;
}

Coś dla każdego: tworzenie rzeczywistości wirtualnej z myślą o internecie

Projektowanie i rozwijanie wieloplatformowych, asymetrycznych rozwiązań oznacza uwzględnianie potrzeb każdego użytkownika w zależności od jego urządzenia. Przy każdej decyzji dotyczącej projektu chcieliśmy sprawdzać, jak może ona wpłynąć na innych użytkowników. Co zrobić, by treści, które pojawiają się w rzeczywistości wirtualnej, były równie ciekawe co w rzeczywistości wirtualnej i odwrotnie?

1. Żółta kula

Osoby, które korzystają z rzeczywistości wirtualnej w dużych salach, będą tworzyć występy, ale jak użytkownicy urządzeń mobilnych (np. Cardboard, Daydream View czy Samsung Gear) będą z nimi korzystać? W tym celu wprowadziliśmy do naszego środowiska nowy element: żółtą kulę.

Żółta kula
Żółta kula

Gdy oglądasz projekt w rzeczywistości wirtualnej, robisz to z perspektywy żółtej kuli. Gdy unosisz się z pokoju do pokoju, tancerze reagują na Twoją obecność. Gestykulują, tańczą dookoła, robią śmieszne ruchy za plecami i szybko się przesuwają, aby nie wpadły na Ciebie. Żółta kula zawsze jest w centrum uwagi.

Dzieje się tak, ponieważ podczas nagrywania występu żółta kula porusza się przez środek pokoju w synchronizacji z muzyką i wraca dookoła. Pozycja kuli daje wykonawcy wyobrażenie o tym, w jakim czasie się znajduje i ile czasu pozostało w pętli. W naturalny sposób koncentruje się na zwiększaniu skuteczności.

2. Inny punkt widzenia

Nie chcieliśmy pomijać użytkowników bez technologii VR, ponieważ to prawdopodobnie to nasi najwięksi odbiorcy. Chcieliśmy nie tylko sztuczna inteligencja, lecz także oferować urządzeniom z ekranem coś, w jaki sposób. Wpadliśmy na pomysł, by pokazać skuteczność z góry w perspektywie izometrycznej. Ta perspektywa ma długą historię w grach komputerowych. Po raz pierwszy pojawiła się w Zaxxon, kosmicznej strzelance z 1982 roku. Użytkownicy rzeczywistości wirtualnej nie czują się pewnie w tej grze, a perspektywa izometryczna pozwala ukazać akcję z perspektywy boskiej. Postanowiliśmy nieco powiększyć modele, aby podkreślić estetykę domu dla lalek.

3. Cienie: udawaj to, dopóki tego nie zrobisz

Odkryliśmy, że niektórzy użytkownicy mają problem z dogłębną analizą w naszej izometrycznej perspektywie. Właśnie dlatego Zaxxon jest jedną z pierwszych gier komputerowych w historii, której obrazuje dynamiczny cień pod obiektami w locie.

Cienie

Okazuje się, że ciężko jest robić cienie w 3D. Zwłaszcza w przypadku urządzeń z ograniczeniami, takich jak telefony komórkowe. Początkowo musieliśmy podjąć trudną decyzję, aby wyeliminować ich z równania, ale po poproszeniu autora Three.js i doświadczonego hakera, pana dooba, wpadł na pomysł, że tak właśnie jest.

Zamiast obliczać, jak każdy z naszych obiektów pływających zasłania nasze światła i rzuca cienie o różnych kształtach, rysujemy pod każdym z nich ten sam okrągły, rozmyty obraz tekstury. Nasze elementy wizualne na początku nie próbują naśladować rzeczywistości, więc odkryliśmy, że łatwo to obejrzą się po paru zmianach. Gdy obiekty zbliżają się do podłoża, tekstury stają się ciemniejsze i mniejsze. Przesuwając się w górę, tekstury stają się bardziej przezroczyste i większe.

Użyliśmy tej tekstury z miękkim gradientem bieli i czerni (bez przezroczystości alfa). Ustawiamy materiał jako przezroczysty i stosujemy mieszanie subtelne. Dzięki temu będą się lepiej łączyć:

function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        side: THREE.BackSide,
        depthWrite: false,
        blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
    }

4. Obecność

Po kliknięciu główki wykonawcy widzowie bez rzeczywistości wirtualnej mogą obejrzeć coś z perspektywy tancerza. Patrząc z tej perspektywy, widać wiele drobnych szczegółów. Próbując utrzymać tempo, tancerze szybko patrzą na siebie. Gdy kula wchodzi do pomieszczenia, widz nerwowo patrzy w swoją stronę. Jako widz nie masz wpływu na te ruchy, ale w zaskakujący sposób oddaje on poczucie zaangażowania. Zdecydowaliśmy się na niego tak niż prezentować użytkownikom sztuczną, sterowaną myszką wersję sztucznej rzeczywistości wirtualnej.

5. Udostępnianie nagrań

Wiemy, że możesz być dumny z wykonania misternie przygotowanego nagrania z 20 komponentami reagującymi na siebie artystów. Wiedzieliśmy, że nasi użytkownicy będą chcieli pokazywać ją znajomym. Ale nieruchomy obraz takiego przedsięwzięcia nie wystarcza. Chcieliśmy jednak umożliwić użytkownikom udostępnianie filmów z ich występów. A może GIF? Są one płaskie, co sprawdza się w ograniczonych paletach kolorów w tym formacie.

Udostępnianie nagrań

Postawiliśmy na GIF.js – bibliotekę JavaScript, która umożliwia kodowanie animowanych GIF-ów z poziomu przeglądarki. Pozwala ono przenieść kodowanie ramek do instancji roboczych, które mogą działać w tle jako osobne procesy, dzięki czemu mogą korzystać z wielu procesorów działających obok siebie.

Niestety ze względu na liczbę klatek potrzebnych w animacjach proces kodowania był nadal za wolny. Ten GIF umożliwia tworzenie małych plików z użyciem ograniczonej palety kolorów. Okazuje się, że najwięcej czasu zajęło nam szukanie najbliższego koloru dla każdego piksela. Mogliśmy zoptymalizować ten proces dziesięciokrotnie, używając krótkich skrótów: jeśli kolor piksela jest taki sam jak ostatnio, użyj tego samego koloru z palety jak poprzednio.

Kodowanie było szybkie, ale wynikowe pliki GIF były za duże. Format GIF pozwala określić sposób usuwania każdej klatki z ostatniej klatki, czyli określić, jak ma być wyświetlana na tle ostatniej. Aby zmniejszyć rozmiar plików, nie aktualizujemy wszystkich pikseli w każdej klatce, bo aktualizujemy tylko te, które się zmieniły. Ponowne spowolnienie procesu kodowania spowodowało jednak zmniejszenie rozmiaru plików.

6. Stałe: Google Cloud i Firebase

Backend witryny z treściami użytkowników może być często skomplikowany i niezawodny, ale dzięki Google Cloud i Firebase stworzyliśmy system, który jest prosty i skuteczny. Gdy wykonawca przesyła nowy taniec do systemu, jest uwierzytelniany anonimowo przez uwierzytelnianie Firebase. Otrzymają oni uprawnienia do przesyłania nagrania do pamięci tymczasowej za pomocą Cloud Storage dla Firebase. Po zakończeniu przesyłania komputer kliencki wywołuje aktywator HTTP Cloud Functions dla Firebase, korzystając z tokena Firebase. Powoduje to aktywowanie procesu serwera, który weryfikuje zgłoszenie, tworzy rekord bazy danych i przenosi nagranie do katalogu publicznego w Google Cloud Storage.

Grunt linowy

Wszystkie nasze treści publiczne są przechowywane w serii prostych plików w zasobniku Cloud Storage. Oznacza to, że nasze dane są szybko dostępne na całym świecie i nie musimy się martwić, że duże obciążenie będzie miało wpływ na dostępność danych.

Skorzystaliśmy z Bazy danych czasu rzeczywistego Firebase i punktów końcowych Cloud Functions, aby stworzyć proste narzędzie do moderowania/selekcji, które pozwala nam oglądać wszystkie nowo przesłane treści w rzeczywistości wirtualnej i publikować nowe playlisty na dowolnym urządzeniu.

7. Skrypty service worker

Skrypty service worker to niedawna innowacja, która ułatwia zarządzanie przechowywaniem zasobów witryny w pamięci podręcznej. W naszym przypadku pracownicy service worker wczytują się błyskawicznie, gdy powracają użytkownicy z Twojej witryny, a nawet umożliwiają działanie witryny w trybie offline. Są to ważne funkcje, ponieważ wielu użytkowników korzysta z komórkowych połączeń o różnej jakości.

Dodanie mechanizmów Service Worker do projektu było łatwe dzięki poręcznej wtyczce pakietu internetowego, która wykonuje większość zadań. W poniższej konfiguracji generujemy mechanizm service worker, który automatycznie zapisuje w pamięci podręcznej wszystkie pliki statyczne. Następnie pobiera z sieci najnowszy plik playlisty (jeśli jest dostępny), ponieważ playlista będzie cały czas aktualizowana. Wszystkie pliki json dotyczące nagrań powinny pobierać z pamięci podręcznej, jeśli są dostępne, ponieważ te dane nigdy się nie zmienią.

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
config.plugins.push(
    new SWPrecacheWebpackPlugin({
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'service-worker.js',
    minify: true,
    navigateFallback: 'index.html',
    staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
    runtimeCaching: [{
        urlPattern: /playlist\.json$/,
        handler: 'networkFirst',
    }, {
        urlPattern: /\/recordings\//,
        handler: 'cacheFirst',
        options: {
        cache: {
            maxEntries: 120,
            name: 'recordings',
        },
        },
    }],
    })
);

Obecnie wtyczka nie obsługuje stopniowo wczytywanych zasobów multimedialnych, takich jak nasze pliki muzyczne, więc pracowaliśmy nad tym, ustawiając dla tych plików nagłówek Cloud Storage Cache-Control na public, max-age=31536000, dzięki czemu przeglądarka będzie zapisywać plik w pamięci podręcznej przez maksymalnie rok.

Podsumowanie

Jesteśmy ciekawi, jak wykonawcy uatrakcyjnią tę funkcję i wykorzystają ją jako narzędzie do twórczej ekspresji za pomocą ruchu. Opublikowaliśmy cały kod open source, który znajdziesz na https://github.com/puckey/dance-tonite. Na początku, a zwłaszcza w WebVR, z niecierpliwością czekamy na nowe kreatywne i nieoczekiwane kierunki rozwoju tego nowego medium. Włącz tryb tańca.