拡張現実: ご存じの方もいらっしゃいますか

WebXR Device API をすでに使用している場合は、ほとんどの作業が完了しています。

Joe Medley
Joe Medley

WebXR Device API は、昨年秋の Chrome 79 でリリースされました。当時お知らせしたとおり、Chrome での API の実装は現在開発中です。Chrome では、一部の作業が完了したことをお知らせいたします。Chrome 81 では、次の 2 つの新機能が追加されました。

この記事では、拡張現実について説明します。WebXR Device API をすでに使用している場合は、学ぶことがほとんどありません。WebXR セッションへのエントリはほぼ同じです。フレームループの実行もほとんど同じです。違いは、拡張現実でコンテンツを適切に表示できるようにする設定にあります。WebXR の基本コンセプトに馴染みがない場合は、WebXR Device API に関する以前の投稿をご覧になるか、少なくともそのトピックで取り上げているトピックをよく理解してください。セッションをリクエストして開始する方法と、フレームループを実行する方法を知っている必要があります。

ヒットテストの詳細については、関連記事実世界のビューに仮想オブジェクトを配置するをご覧ください。この記事のコードは、Immersive Web ワーキング グループの WebXR Device API サンプルの Immersive AR Session サンプル(デモソース)に基づいています。

コードの作成に取り掛かる前に、没入型 AR セッションのサンプルを少なくとも 1 回使用してください。Chrome 81 以降を搭載した最新の Android スマートフォンが必要です。

利点

拡張現実は、ブラウザを離れることなく AR のユースケースを実装できるため、既存または新しい多くのウェブページに価値を付加します。たとえば、教育サイトでユーザーが学習したり、購入を検討しているユーザーがショッピング中に家の中にオブジェクトを視覚化したりできます。

2 つ目のユースケースについて考えてみましょう。仮想オブジェクトの実寸大の表現を実際のシーンの上に配置することをシミュレートするとします。配置した画像は、選択したサーフェスに残り、実際のアイテムがそのサーフェスにある場合と同じサイズで表示されます。ユーザーは画像を移動したり、画像に近づいたり離れたりできます。これにより、視聴者は 2 次元画像では得られない、オブジェクトのより深い理解を得ることができます。

私は自分のことより少し先を行っています。ここまで説明した内容を実際に行うには、AR 機能とサーフェスを検出する手段が必要です。この記事では前者について説明します。WebXR Hit Test API に関する記事(上記にリンク)では、後者について説明しています。

セッションのリクエスト

セッションをリクエストする方法は、これまでとほとんど同じです。まず、xr.isSessionSupported() を呼び出して、目的のセッション タイプが現在のデバイスで使用可能かどうかを確認します。前と同じように 'immersive-vr' をリクエストするのではなく、'immersive-ar' をリクエストします。

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

これまでどおり、[AR を開始] ボタンが有効になります。ユーザーがクリックしたら、xr.requestSession() を呼び出し、'immersive-ar' も渡します。

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

便利なプロパティ

最後のコードサンプルでは、2 行をハイライト表示していることにお気づきかと思います。XRSession オブジェクトには isImmersive というプロパティがあるようです。これは私が作成した便利なプロパティであり、仕様の一部ではありません。後で、視聴者に何を表示するかを決定するために使用します。このプロパティが API の一部ではないのはなぜですか?アプリでこのプロパティを別途追跡する必要がある可能性があるため、仕様作成者は API をクリーンな状態に保つことにしました。

セッションへの参加

前回の記事で説明した onSessionStarted() の形式を思い出してください。

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

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

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

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

拡張現実のレンダリングに対応するために、いくつか追加する必要があります。背景をオフにする まず、背景が必要かどうかを判断します。ここでは、コンビニエンス プロパティを初めて使用します。

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

  if (session.isImmersive) {
    removeBackground();
  }

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

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

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });

}

参照スペース

前回の記事では、参照空間について簡単に説明しました。ここで説明するサンプルでは、そのうちの 2 つを使用します。

参照空間は、仮想世界とユーザーの物理環境の関係を表します。これは次の方法で行います。

  • 仮想世界内の位置を表現するために使用される座標系の起点を指定します。
  • ユーザーがその座標系内で移動すると想定するかどうかを指定する。
  • その座標系に事前定義された境界があるかどうか。(ここで示す例では、事前に定義された境界を持つ座標系は使用していません)。

すべての参照空間で、X 座標は左右、Y 座標は上下、Z 座標は前後を表します。正の値はそれぞれ右、上、逆方向です。

XRFrame.getViewerPose() によって返される座標は、リクエストされた参照空間のタイプによって異なります。これについてはフレームループのところで説明しますここでは、拡張現実に適した参照タイプを選択する必要があります。ここでも、便利なプロパティを使用しています。

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

  if (session.isImmersive) {
    removeBackground();
  }

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

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

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

没入型 AR セッションのサンプルをご覧になったことがある方は、最初はシーンが静止していて、拡張現実ではないことに気づかれたことでしょう。シーン内を移動するには、指でドラッグ&スワイプします。[AR を開始] をクリックすると、背景が消えて、デバイスを動かすことでシーン内を移動できるようになります。これらのモードでは、異なる参照空間タイプが使用されます。上のハイライト表示されたテキストは、この選択方法を示しています。次の参照型を使用します。

local - 原点は、セッション作成時の視聴者の位置です。つまり、エクスペリエンスに明確な下限が設定されているとは限らず、起点の正確な位置はプラットフォームによって異なる場合があります。空間に事前設定された境界はありませんが、コンテンツは回転以外の動きなしで表示できると想定されています。Google の AR の例からわかるように、空間内での移動が可能な場合があります。

viewer - ページ内にインラインで表示されるコンテンツで最も頻繁に使用されます。このスペースは表示デバイスの後に続きます。getViewerPose に渡された場合、トラッキングは行われないため、アプリが XRReferenceSpace.getOffsetReferenceSpace() で変更しない限り、常に原点の位置が報告されます。このサンプルでは、これを使用してタップベースのカメラ パンを有効にしています。

フレームループの実行

コンセプト的には、前回の記事で説明した VR セッションで行ったことと変わりはありません。参照空間タイプを XRFrame.getViewerPose() に渡します。返される XRViewerPose は、現在の参照空間タイプに対応しています。デフォルトで viewer を使用すると、AR または VR のユーザーの同意が求められる前に、ページにコンテンツのプレビューを表示できます。これは重要なポイントを示しています。インライン コンテンツは没入型コンテンツと同じフレームループを使用するため、メンテナンスする必要があるコードの量を削減できます。

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

まとめ

このシリーズの記事では、ウェブで没入型コンテンツを実装する際の基本事項のみを説明します。没入型ウェブ ワーキング グループの WebXR Device API サンプルでは、その他の多くの機能とユースケースが紹介されています。また、サーフェスの検出と、実世界のカメラビューへの仮想アイテムの配置に関する API について説明するヒットテストに関する記事も公開しました。ぜひご覧ください。また、今後も web.dev ブログで記事を公開していきますので、ご注目ください。

写真撮影: David GrandmouginUnsplash より)