Rzeczywistość wirtualna w internecie pojawia się w części II

Wszystko o pętli ramek

Joe Medley
Joe Medley

Niedawno opublikowałam artykuł wirtualna rzeczywistość, który pojawia się w internecie, w którym omówiono podstawowe koncepcje interfejsu WebXR Device API. Znajdziesz tam też instrukcje dotyczące żądania, rozpoczynania i kończenia sesji XR.

W tym artykule opisano pętlę klatek, czyli nieskończoną pętlę kontrolowaną przez klienta użytkownika, w której treści są wielokrotnie wyświetlane na ekranie. Treści są rysowane w odrębnych blokach zwanych ramkami. Kolejne sekwencje ujęć stwarzają iluzję ruchu.

Czego nie ma w tym artykule

WebGL i WebGL 2 to jedyne sposoby renderowania treści w pętli klatki w aplikacji WebXR. Na szczęście wiele platform zapewnia warstwę abstrakcji nad WebGL i WebGL 2. Przykładami takich platform są three.js, babylonjs i PlayCanvas. Z kolei A-Frame i React 360 zostały zaprojektowane do interakcji z WebXR.

Ten artykuł nie jest samouczkiem WebGL ani nie jest samouczkiem dotyczącym platformy. Objaśnia podstawowe informacje o pętli ramek, korzystając z przykładu sesji Immersive VR Session (prezentacja, źródło). Jeśli chcesz zgłębić program WebGL lub jeden z jego elementów, możesz znaleźć w internecie coraz więcej artykułów.

Gracze i mecz

Próbując zrozumieć pętlę ramek, stale gubiłam się w szczegółach. Bierzemy pod uwagę wiele obiektów, a niektóre z nich noszą wyłącznie nazwy właściwości odwołań. Aby Ci pomóc, opiszę obiekty. Omówię je „odtwarzacze”. Następnie opiszę sposób ich interakcji i określę to „gra”.

Zawodnicy

XRViewerPose

Pozycja to pozycja i orientacja obiektu w przestrzeni 3D. Zarówno widzowie, jak i urządzenia wejściowe mają określoną pozycję, ale tę kwestię bardziej przejmuje się tutaj. Zarówno osoby przeglądające, jak i urządzenia wejściowe mają atrybut transform opisujący jego pozycję jako wektora i jego orientację jako kwartionę względem źródła. Źródło jest określane na podstawie żądanego typu przestrzeni referencyjnej podczas wywoływania funkcji XRSession.requestReferenceSpace().

Wyjaśnienie miejsc w pliku referencyjnym zajmuje trochę czasu. Omawiam je szczegółowo w sekcji Rzeczywistość rozszerzona. W przykładzie użytym jako podstawa tego artykułu wykorzystano przestrzeń referencyjną 'local', co oznacza, że punkt początkowy znajduje się w miejscu przeglądającego w momencie tworzenia sesji bez jasno określonej ceny minimalnej, a jego dokładna pozycja może się różnić w zależności od platformy.

XRView

Widok odpowiada kamerze oglądającej wirtualną scenę. Widok ma też atrybut transform opisujący jego pozycję jako wektora i jego orientację. Są one dostarczane zarówno jako para wektor/kwaternion, jak i jako macierz równoważna. Możesz użyć dowolnej reprezentacji w zależności od tego, która najlepiej pasuje do Twojego kodu. Każdy widok odpowiada wyświetlaczowi lub części wyświetlacza, za pomocą którego urządzenie wyświetla obraz użytkownikowi. Obiekty XRView są zwracane w tablicy z obiektu XRViewerPose. Liczba wyświetleń w tablicy jest zmienna. Na urządzeniach mobilnych scena AR ma jeden widok, który może zasłaniać ekran urządzenia. Zestawy słuchawkowe zwykle mają dwa widoki, po jednym dla każdego oka.

XRWebGLLayer

Warstwy są źródłem obrazów bitmapowych i opisów sposobu renderowania tych obrazów na urządzeniu. Opis nie oddaje dokładnie tego, co robi ten odtwarzacz. To coś w rodzaju pośrednika między urządzeniem a WebGLRenderingContext. W MDN, że „zapewnia ona połączenie” między nimi, wygląda bardzo podobnie. Dzięki temu jest dostępna dla innych graczy.

Ogólnie obiekty WebGL przechowują informacje o stanie renderowania grafiki 2D i 3D.

WebGLFramebuffer

Ramka bufora dostarcza dane obrazu do elementu WebGLRenderingContext. Po pobraniu pliku XRWebGLLayer przekazujesz go do bieżącego WebGLRenderingContext. Poza wywołaniem funkcji bindFramebuffer() (więcej informacji na ten temat później) nigdy nie uzyskasz bezpośredniego dostępu do tego obiektu. Po prostu przekażesz go z interfejsu XRWebGLLayer do interfejsu WebGLRenderingContext.

XRViewport

W widocznym obszarze znajdują się współrzędne i wymiary prostokątnego obszaru w obszarze WebGLFramebuffer.

WebGLRenderingContext

Kontekst renderowania to zautomatyzowany punkt dostępu do przestrzeni roboczej. Potrzebuje do tego zarówno WebGLFramebuffer, jak i XRViewport.

Zwróć uwagę na związek między XRWebGLLayer a WebGLRenderingContext. Jedna z nich odpowiada urządzeniu przeglądającego, a druga – stronie internetowej. Wartości WebGLFramebuffer i XRViewport są przekazywane z pierwszego do drugiego.

Zależność między XRWebGLLayer i WebGLRenderingContext
Relacja między XRWebGLLayer a WebGLRenderingContext

Gra

Wiemy już, kim są gracze, więc teraz przyjrzyjmy się grze, w którą grają. To gra, która zaczyna się od początku każdej klatki. Pamiętaj, że klatki są częścią pętli, która następuje z częstotliwością zależną od używanego sprzętu. W aplikacjach do rzeczywistości wirtualnej liczba klatek na sekundę może wynosić od 60 do 144. AR na Androida działa z 30 klatkami na sekundę. W kodzie nie należy zakładać żadnej konkretnej liczby klatek.

Podstawowy proces pętli ramki wygląda tak:

  1. Zadzwoń do firmy XRSession.requestAnimationFrame(). W odpowiedzi klient użytkownika wywołuje zdefiniowane przez Ciebie polecenie XRFrameRequestCallback.
  2. Funkcja wywołania zwrotnego:
    1. Zadzwoń jeszcze raz do: XRSession.requestAnimationFrame().
    2. Przyjęcie pozycji widza.
    3. Przekaż („bind”) metodę WebGLFramebuffer z: XRWebGLLayer do: WebGLRenderingContext.
    4. Wykonaj iterację nad każdym obiektem XRView, pobierając jego wartość XRViewport z XRWebGLLayer i przekazując do WebGLRenderingContext.
    5. Narysuj coś do bufora ramki.

Ponieważ kroki 1 i 2a zostały omówione w poprzednim artykule, zacznę od kroku 2b.

Przyjęcie pozycji widza

Pewnie to zrozumiałe. Aby rysować w AR lub VR, muszę wiedzieć, gdzie widz jest i gdzie patrzy. Położenie i orientację widza jest określane przez obiekt XRViewerPose. Rozpoznaję pozycję widza, wywołując XRFrame.getViewerPose() w bieżącej klatce animacji. Przekażę mu przestrzeń referencyjną uzyskaną podczas konfigurowania sesji. Wartości zwracane przez ten obiekt zawsze odnoszą się do przestrzeni referencyjnej żądanej podczas otwierania bieżącej sesji. Jak być może pamiętasz, muszę oddać mu bieżący obszar referencyjny, gdy prosisz o ułożenie ciała.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    // Render based on the pose.
  }
}

Występuje 1 pozycja, która reprezentuje ogólną pozycję użytkownika, czyli głowę widza lub aparat w telefonie w przypadku smartfona. Pozycja informuje aplikację, gdzie znajduje się widz. Rzeczywiste renderowanie obrazu korzysta z obiektów XRView, do których zajmę się za chwilę.

Zanim przejdziemy dalej, sprawdzę, czy widz zwróciła się w pozycji widza na wypadek, gdyby system przestał śledzić lub zablokować pozę ze względu na ochronę prywatności. Śledzenie to zdolność urządzenia XR do śledzenia położenia urządzenia lub urządzeń wejściowych względem środowiska. Śledzenie może zostać utracone na wiele sposobów i różni się w zależności od metody użytej do śledzenia. Na przykład, jeśli do śledzenia urządzenia są używane kamery w zestawie słuchawkowym lub telefonie, mogą one stracić zdolność do określenia, w którym miejscu znajduje się w sytuacji przy słabym lub całkowitym oświetleniu oraz czy kamery są zakryte.

Przykładem blokowania ułożenia ze względu na ochronę prywatności jest sytuacja, w której na goglach wyświetla się okno bezpieczeństwa (np. prośba o przyznanie uprawnień), a przeglądarka może w tym czasie przestać je udostępniać. Już wywołałem(-am) funkcję XRSession.requestAnimationFrame(), więc jeśli system będzie w stanie przywrócić działanie systemu, pętla będzie kontynuowana. Jeśli nie, klient użytkownika zakończy sesję i wywoła moduł obsługi zdarzeń end.

Krótka trasa

Następny krok wymaga obiektów utworzonych podczas konfigurowania sesji. Przypominam, że utworzyłem obiekt canvas i poinstruowałem go o utworzeniu kontekstu renderowania Web GL zgodnego z XR, który udało mi się uzyskać, wywołując funkcję canvas.getContext(). Wszystkie rysunki wykonuje się przy użyciu interfejsu API WebGL, interfejsu WebGL 2 lub platformy opartej na WebGL, np. Three.js. Ten kontekst został przekazany do obiektu sesji przez updateRenderState() wraz z nowym wystąpieniem XRWebGLLayer.

let canvas = document.createElement('canvas');
// The rendering context must be based on WebGL or WebGL2
let webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

Przekaż („bind”) zawartość bufora WebGLFrame

Element XRWebGLLayer udostępnia bufor ramki na potrzeby elementu WebGLRenderingContext, który jest przeznaczony do użycia z WebXR i zastępuje domyślny bufor ramek kontekstu renderowania. W języku WebGL jest to tzw. „wiązanie”.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    // Iterate over the views
  }
}

Wykonanie iteracji każdego obiektu XRView

Po ustaleniu pozycji i połączeniu bufora ramki należy pobrać widoczne obszary. Element XRViewerPose zawiera tablicę interfejsów XRView, z których każdy reprezentuje wyświetlacz lub jego część. Zawierają one informacje potrzebne do renderowania treści w prawidłowym ustawieniu dla urządzenia i przeglądającego, takie jak pole widzenia, przesunięcie oczu i inne właściwości optyczne. Rysuję dwa oczy, więc mam dwa widoki, które przepuszczam i rysuję po jednym obrazie dla każdego z nich.

Podczas implementowania rzeczywistości rozszerzonej na telefonie mam tylko jeden widok, ale używam pętli. Chociaż powtarzanie jednego widoku może wydawać się bezsensowne, taki sposób pozwala uzyskać 1 ścieżkę renderowania dla spektrum atrakcyjnych wrażeń. Jest to istotna różnica między WebXR a innymi systemami interaktywnymi.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    for (let xrView of xrViewerPose.views) {
      // Pass viewports to the context
    }
  }
}

Przekaż obiekt XRViewport do WebGLRenderingContext

Obiekt XRView odnosi się do tego, co można obserwować na ekranie. Aby wyświetlić ten widok, potrzebuję współrzędnych i wymiarów właściwych dla mojego urządzenia. Tak jak w przypadku bufora ramek, żądam ich z XRWebGLLayer i przekazuję do WebGLRenderingContext.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
  if (xrViewerPose) {
    let glLayer = xrSession.renderState.baseLayer;
    webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
    for (let xrView of xrViewerPose.views) {
      let viewport = glLayer.getViewport(xrView);
      webGLRenContext.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
      // Draw something to the framebuffer
    }
  }
}

Parametr webGLRenContext

Pisząc ten artykuł, rozmawiałem z kilkoma kolegami na temat nazewnictwa obiektu webGLRenContext. Przykładowe skrypty i większość kodu WebXR w prosty sposób wywołują tę zmienną gl. Podczas pracy nad zrozumieniem próbek wciąż zapomniałam, do czego odnosi się gl. Nazywam to webGLRenContext, aby przypomnieć Ci, że to przykład zdarzenia z kategorii WebGLRenderingContext.

Wynika to z tego, że użycie gl sprawia, że nazwy metod w interfejsie OpenGL ES 2.0 API wyglądają jak ich odpowiedniki, co jest używane do tworzenia rzeczywistości wirtualnej w językach skompilowanych. Jest to oczywiste, jeśli tworzysz aplikacje VR za pomocą OpenGL, ale jeśli nie masz doświadczenia w tej technologii, możesz pomylić.

Rysuj coś do bufora ramki

Jeśli masz ambitne cele, możesz użyć WebGL bezpośrednio, ale nie polecam tego. Znacznie prościej jest użyć jednej z platform wymienionych u góry.

Podsumowanie

To nie koniec artykułów i aktualizacji dotyczących WebXR. Informacje o wszystkich interfejsach i członkach WebXR znajdziesz na stronie MDN. W przypadku nadchodzących ulepszeń interfejsów postępuj zgodnie z poszczególnymi funkcjami w sekcji Stan Chrome.

Autor zdjęcia: JESHOOTS.COM na stronie Unsplash