バーチャル リアリティがウェブに登場

バーチャル リアリティや拡張現実など、多種多様な没入体験を提供するための基本事項をご紹介します。

Joe Medley
Joe Medley

没入型エクスペリエンスは Chrome 79 でウェブに導入されました。WebXR Device API はバーチャル リアリティを実現し、拡張現実のサポートは Chrome 81 で実現します。GamePad API の更新により、VR への高度なコントロールの使用が拡張されます。Firefox Reality、Oculus Browser、Edge、Magic Leap の Helio ブラウザなど、他のブラウザでも近日中にこれらの仕様がサポートされる予定です。

この記事は、没入型ウェブに関するシリーズの第 1 回です。このパートでは、基本的な WebXR アプリケーションの設定と、XR セッションの開始と終了について説明します。以降の記事では、フレームループ(WebXR エクスペリエンスの中心となる機能)、拡張現実の詳細、AR セッションでサーフェスを検出する手段である WebXR Hit Test API について説明します。特に断りのない限り、この記事と以降の記事で扱う内容はすべて AR と VR の両方に等しく当てはまります。

没入体験を表す 2 つの用語、拡張現実とバーチャル リアリティは、完全な現実から完全なバーチャルまで、その間に多少の没入感を挟みながら、多くの人が思い描いていきます。XR の X は、没入型エクスペリエンスのスペクトル内のあらゆるものを表す一種の代数変数として、その考え方を反映することを目的としています。

完全な現実から完全に没入型までの視覚体験のスペクトルを示すグラフ。
没入型エクスペリエンスのスペクトル

没入型の体験の例:

  • ゲーム
  • 360° 動画
  • 臨場感のある環境に表示される従来型の 2D(または 3D)動画
  • 住宅購入
  • 購入前に商品を家で確認する
  • 没入型アート
  • 誰も思いつかないようなクールなもの

コンセプトと使用方法

ここでは、WebXR Device API の基本的な使用方法について説明します。ここまでで説明した内容よりも詳しく知りたい場合は、Immersive Web ワーキング グループの WebXR サンプルまたは MDN の参照資料をご覧ください。WebXR Device API の初期バージョンを使い慣れている方は、この資料のすべてに目を通してください。変更が加えられています。

この記事のコードは、Immersive Web Working Group のベアボーン サンプル(デモソース)に基づいていますが、わかりやすく簡潔にするために編集されています。

WebXR 仕様の作成の一環として、ユーザーを保護するためのセキュリティとプライバシー対策が強化されています。したがって、実装では特定の要件に準拠する必要があります。ウェブページやアプリは、閲覧者に機密性の高い情報を要求する前に、アクティブでフォーカスされている必要があります。ウェブページまたはアプリは HTTPS 経由で配信する必要がありますAPI 自体は、機能するために必要なセンサーやカメラから取得した情報を保護するように設計されています。

セッションをリクエストする

XR セッションに入るには、ユーザーの操作が必要です。これを取得するには、機能検出を使用して(navigator.xr を介して)XRSystem をテストし、XRSystem.isSessionSupported() を呼び出します。Chrome バージョン 79 と 80 では、XRSystem オブジェクトは XR と呼ばれていました。

次の例では、'immersive-vr' セッション タイプでバーチャル リアリティ セッションを希望していることを示しています。その他のセッション タイプ'immersive-ar''inline' です。インライン セッションは HTML 内でコンテンツを表示するためのもので、主にティーザー コンテンツに使用されます。没入型 AR セッションのサンプルで、この方法について確認できます。これについては、後の記事で説明します。

バーチャル リアリティ セッションがサポートされていることがわかったら、ユーザー ジェスチャーを取得するボタンを有効にします。

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-vr');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter VR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

ボタンを有効にした後、クリック イベントを待ってからセッションをリクエストします。

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-vr')
    .then((session) => {
      xrSession = session;
      xrButton.textContent = 'Exit XR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

このコードのオブジェクト階層に注目してください。navigator から xr に移動し、XRSession インスタンスになります。以前のバージョンの API では、セッションをリクエストする前にスクリプトでデバイスをリクエストする必要がありました。これで、デバイスが暗黙的に取得されます。

セッションに入る

セッションを取得したら、開始して入力する必要があります。ただし、まずいくつか設定を行う必要があります。ユーザーが終了したときにアプリまたはウェブページをリセットできるように、セッションには onend イベント ハンドラが必要です。

また、シーンを描画する <canvas> 要素も必要です。XR 互換の WebGLRenderingContext または WebGL2RenderingContext を使用する必要があります。すべての描画は、これらの関数または Three.js などの WebGL ベースのフレームワークを使用して行われます。

描画する場所が決まったので、描画するコンテンツのソースが必要です。そのために、XRWebGLLayer のインスタンスを作成します。XRSession.updateRenderState() を呼び出して、キャンバスに関連付けます。

セッション中に、バーチャル リアリティ内の物体の位置を特定する方法が必要です。参照スペースが必要になります。'local-floor' 参照空間は、原点が視聴者に近い位置にあり、床レベルで y 軸が 0 で、移動は想定されていない空間です。他にも参照空間の種類がありますが、ここでは説明できないほど複雑なトピックです。画面に描画するときに必要になるため、参照空間を変数に保存します。

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

参照スペースを取得したら、XRSession.requestAnimationFrame() を呼び出します。これは、フレームループで実行される仮想コンテンツの表示の開始です。

フレームループを実行する

フレームループは、コンテンツが繰り返し画面に描画される、ユーザー エージェント制御の無限ループです。コンテンツはフレームと呼ばれる個別のブロックに描画されます。連続したフレームによって、動きがあるように見えます。VR アプリケーションの場合、フレームレートは 60~144 の範囲で指定できます。Android の AR は 30 フレーム/秒で動作します。コードで特定のフレームレートを前提としないようにする必要があります。

フレームループの基本的なプロセスは次のとおりです。

  1. XRSession.requestAnimationFrame() を呼び出します。ユーザー エージェントは、ユーザーが定義した XRFrameRequestCallback を呼び出します。
  2. コールバック関数内:
    1. XRSession.requestAnimationFrame() をもう一度呼び出します。
    2. 視聴者のポーズを取得します。
    3. XRWebGLLayer から WebGLRenderingContextWebGLFramebuffer を渡します(「バインド」します)。
    4. XRView オブジェクトを反復処理し、その XRViewportXRWebGLLayer から取得して WebGLRenderingContext に渡します。
    5. フレームバッファに何かを描画します。

この記事の残りの部分では、ステップ 1 とステップ 2 の一部(XRFrameRequestCallback の設定と呼び出し)について説明します。ステップ 2 の残りの項目については パート II で説明します

XRFrameRequestCallback

XRFrameRequestCallback はユーザーが定義します。DOMHighResTimeStampXRFrame インスタンスの 2 つのパラメータを取ります。XRFrame オブジェクトは、ディスプレイに 1 つのフレームをレンダリングするために必要な情報を提供します。DOMHighResTimeStamp 引数は将来の使用に備えています。

まず、次のアニメーション フレームをリクエストします。前述のように、フレームのタイミングは、基盤となるハードウェアに基づいてユーザー エージェントによって決定されます。最初に次のフレームをリクエストすると、コールバック中に何かがエラーをスローしても、フレームループが続行されます。

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  // Render a frame.
}

ここまでで、視聴者向けに何かを描く準備が整いました。これはパート 2 で説明します。そちらに進む前に、セッションを終了する方法をご説明します。

セッションを終了する

没入型セッションは、XRSession.end() への呼び出しによる独自のコードによる終了など、さまざまな理由で終了する可能性があります。ヘッドセットが切断されている、または別のアプリがヘッドセットを制御している場合も、この問題が発生することがあります。そのため、適切に動作するアプリでは end イベントをモニタリングする必要があります。発生した場合は、セッションとその関連するレンダリング オブジェクトを破棄します。終了した没入型セッションを再開することはできません。没入感のあるエクスペリエンスを再開するには、アプリで新しいセッションを開始する必要があります。

セッションへの参加で説明したように、セットアップ中に onend イベント ハンドラを追加しました。

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);
  // More setup…
}

イベント ハンドラ内で、ユーザーがセッションに入る前のアプリの状態を復元します。

function onSessionEnded(event) {
  xrSession = null;
  xrButton.textContent = 'Enter VR';
}

まとめ

ウェブ XR または AR アプリケーションの作成に必要なすべての内容を説明したわけではありません。ここまでで、コードを理解し、テストを開始するのに十分な情報を提供できたと思います。次のセクションでは、コンテンツが画面に描画されるフレームループについて説明します。

写真提供: JESHOOTS.COMUnsplash