没入型ウェブとは、ブラウザでホストされる仮想世界体験のことです。このバーチャル リアリティ エクスペリエンス全体が、ブラウザまたは VR 対応ヘッドセットに表示されます。
没入型ウェブとは、ブラウザでホストされる仮想世界体験のことです。これには、ブラウザや VR 対応ヘッドセット(Google Daydream、Oculus Rift、Samsung Gear VR、HTC Vive、Windows Mixed Reality ヘッドセットなど)で表示されるバーチャル リアリティ(VR)エクスペリエンス全体、および AR 対応モバイル デバイス用に開発された拡張現実エクスペリエンスが含まれます。
没入型エクスペリエンスを説明するために 2 つの用語を使用していますが、これらは、完全な現実から完全に没入型の VR 環境までのスペクトルであり、その間にさまざまなレベルの AR があります。
没入型の体験の例:
- 没入型の 360° 動画
- 没入感のある環境で表示される従来の 2D(または 3D)動画
- データの可視化
- ホーム ショッピング
- アート
- 誰も思いつかないようなクールなもの
そこにはどうやって行けばよいですか?
没入型ウェブは、ほぼ 1 年前から初期段階で利用可能になっています。これは、WebVR 1.1 API を介して行われました。この API は、Chrome 62 以降、オリジン トライアルで利用可能でした。この API は、Firefox と Edge でもサポートされています。また、Safari 用のポリフィルもサポートされています。
ですが、そろそろ次のステップに進む時期です。
オリジン トライアルは 2018 年 7 月 24 日に終了し、この仕様は WebXR Device API と新しいオリジン トライアルに置き換えられました。
WebVR 1.1 はどうなったのですか?
Google は WebVR 1.1 から多くのことを学びましたが、時間の経過とともに、デベロッパーが構築したいタイプのアプリケーションをサポートするには、いくつかの大きな変更が必要であることが明らかになりました。学んだ教訓の全リストは長すぎるためここでは説明しません。しかし、API がメインの JavaScript スレッドに明示的に関連付けられていること、デベロッパーが明らかに間違った構成を設定できる機会が多すぎること、マジック ウィンドウなどの一般的な用途が意図された機能ではなく副作用であることなどの問題が含まれています。(マジック ウィンドウは、ヘッドセットなしで没入型コンテンツを表示する手法です。アプリはデバイスの向きセンサーに基づいて単一のビューをレンダリングします)。
新しい設計により、実装が簡素化され、パフォーマンスが大幅に向上します。同時に、AR などのユースケースが登場し、将来的にそれらをサポートできるように API を拡張することが重要になりました。
WebXR Device API は、これらの拡張されたユースケースを念頭に置いて設計および命名されており、より優れた方法を提供します。WebVR の実装者は、WebXR Device API への移行に取り組んでいます。
WebXR Device API とは
以前の WebVR 仕様と同様に、WebXR Device API は、Google、Microsoft、Mozilla などのコントリビューターが参加する Immersive Web Community Group の成果物です。XR の X は、没入型エクスペリエンスのスペクトル内のあらゆるものを表す一種の代数変数として使用されます。これは、前述のオリジン トライアルで利用できるほか、ポリフィルでも利用できます。
この記事が最初に公開されたのは Chrome 67 ベータ版の期間中であり、VR 機能のみが有効になっていました。拡張現実は Chrome 69 で導入されました。詳しくは、ウェブ向けの拡張現実をご覧ください。
この新しい API には、この記事で説明できる以上の機能があります。ここでは、WebXR サンプルを理解するのに十分な情報を提供します。詳しくは、元の説明と 没入型ウェブ早期導入ガイドをご覧ください。後者は、オリジン トライアルの進展に伴い拡大していく予定です。問題を報告したり、プル リクエストを送信したりしてください。
この記事では、XR セッションの開始、停止、実行と、入力処理の基本について説明します。
ここでは、AR/VR コンテンツを画面に描画する方法については説明しません。WebXR Device API には画像レンダリング機能はありません。ご判断にお任せします。描画は WebGL API を使用して行われます。野心があるなら、ただし、フレームワークを使用することをおすすめします。没入型ウェブのサンプルでは、デモ専用に作成された Cottontail を使用します。Three.js は 5 月から WebXR をサポートしています。A-Frame については何も聞いていません。
アプリの起動と実行
基本的なプロセスは次のとおりです。
- XR デバイスをリクエストします。
- 利用可能な場合は、XR セッションをリクエストします。ユーザーがスマートフォンをヘッドセットに装着することを希望する場合は、没入型セッションと呼ばれ、ユーザーのジェスチャーで開始する必要があります。
- セッションを使用して、1 秒あたり 60 個の画像フレームを提供するレンダリング ループを実行します。各フレームで適切なコンテンツを画面に描画します。
- ユーザーが終了するまでレンダリング ループを実行します。
- XR セッションを終了します。
コードを含めて、もう少し詳しく見てみましょう。これから説明する内容からアプリを実行することはできません。なお、これはあくまでも概要を説明するためのものです。
XR デバイスをリクエストする
ここでは、標準の特徴検出コードを確認できます。これは、checkForXR()
などの関数にラップできます。
没入型セッションを使用していない場合は、機能のアドバタイズとユーザー ジェスチャーの取得をスキップして、セッションのリクエストに直接進むことができます。没入型セッションは、ヘッドセットが必要なセッションです。没入型以外のセッションでは、デバイスの画面にコンテンツが表示されるだけです。前者は、バーチャル リアリティや拡張現実について考えると、ほとんどの人が思い浮かべるものです。後者は「マジック ウィンドウ」と呼ばれることもあります。
if (navigator.xr) {
navigator.xr.requestDevice()
.then(xrDevice => {
// Advertise the AR/VR functionality to get a user gesture.
})
.catch(err => {
if (err.name === 'NotFoundError') {
// No XRDevices available.
console.error('No XR devices available:', err);
} else {
// An error occurred while requesting an XRDevice.
console.error('Requesting XR device failed:', err);
}
})
} else{
console.log("This browser does not support the WebXR API.");
}
XR セッションをリクエストする
デバイスとユーザー ジェスチャーが用意できたので、セッションを取得します。セッションを作成するには、描画するキャンバスが必要です。
xrPresentationContext = htmlCanvasElement.getContext('xrpresent');
let sessionOptions = {
// The immersive option is optional for non-immersive sessions; the value
// defaults to false.
immersive: false,
outputContext: xrPresentationContext
}
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Use a WebGL context as a base layer.
xrSession.baseLayer = new XRWebGLLayer(session, gl);
// Start the render loop
})
レンダリング ループを実行する
このステップのコードは少し複雑です。混乱を解消するために、これからたくさんの言葉を投げかけます。最終的なコードを確認したい場合は、先に進んで確認してから、戻って詳細を説明します。推測できないことがかなりあります。
レンダリング ループの基本的なプロセスは次のとおりです。
- アニメーション フレームをリクエストします。
- デバイスの位置をクエリします。
- デバイスの位置に基づいて、デバイスの位置にコンテンツを描画します。
- 入力デバイスに必要な処理を行います。
- ユーザーが終了するまで、1 秒間に 60 回繰り返します。
プレゼンテーション フレームをリクエストする
ウェブ XR のコンテキストでは、「フレーム」という単語には複数の意味があります。1 つ目は参照フレームで、座標系の原点がどこから計算されるか、デバイスが移動したときにその原点に何が起こるかを定義します。(ユーザーが動いてもビューは同じままですか?それとも現実世界のように移動しますか?)
2 つ目のフレームタイプは、XRFrame
オブジェクトで表されるプレゼンテーション フレームです。このオブジェクトには、AR/VR シーンの 1 つのフレームをデバイスにレンダリングするために必要な情報が含まれます。プレゼンテーション フレームは requestAnimationFrame()
を呼び出して取得されるため、これは少し混乱します。これにより、window.requestAnimationFrame()
との互換性が確保されます。
理解を深めるために、コードをいくつか紹介します。次のサンプルは、レンダリング ループの開始と維持方法を示しています。フレームという単語の二重使用に注目してください。また、requestAnimationFrame()
への再帰呼び出しにも注意してください。この関数は 1 秒間に 60 回呼び出されます。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
// Process the frame.
xrFrame.session.requestAnimationFrame(onFrame);
}
});
ポーズ
画面に何かを描画する前に、ディスプレイ デバイスが指している場所を把握し、画面にアクセスする必要があります。一般に、AR/VR 内の物体の位置と向きをポーズと呼びます。視聴者と入力デバイスの両方にポーズがあります。(入力デバイスについては後で説明します)。ビューアと入力デバイスの両方の姿勢は、列優先の順序で Float32Array
に保存される 4×4 行列として定義されます。現在のアニメーション フレーム オブジェクトで XRFrame.getDevicePose()
を呼び出すと、ビューアのポーズを取得できます。ポーズが返されたかどうかは必ずテストしてください。問題が発生した場合に、画面に描画しないようにします。
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
// Draw something to the screen.
}
視聴回数
ポーズを確認したら、描画を始めましょう。描画するオブジェクトはビュー(XRView
)と呼ばれます。ここでセッション タイプが重要になります。ビューは、XRFrame
オブジェクトから配列として取得されます。没入型以外のセッションの場合、配列には 1 つのビューがあります。没入型セッション中は、アレイは左右の目に 1 つずつ、2 つあります。
for (let view of xrFrame.views) {
// Draw something to the screen.
}
これは、WebXR と他の没入型システムの重要な違いです。1 つのビューを反復処理するのは無意味に思えるかもしれませんが、これにより、さまざまなデバイスで単一のレンダリング パスを使用できます。
レンダリング ループ全体
これらをすべてまとめると、以下のコードになります。入力デバイスのプレースホルダを残しています。これは後のセクションで説明します。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
for (let view of xrFrame.views) {
// Draw something to the screen.
}
}
// Input device code will go here.
frame.session.requestAnimationFrame(onFrame);
}
}
XR セッションを終了する
XR セッションは、XRSession.end()
の呼び出しによる独自のコードによる終了など、さまざまな理由で終了することがあります。ヘッドセットが切断されている、または別のアプリがヘッドセットを制御している場合も、この問題が発生することがあります。そのため、適切に動作するアプリケーションでは、終了イベントをモニタリングし、発生したときにセッション オブジェクトとレンダラ オブジェクトを破棄する必要があります。終了した XR セッションは再開できません。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('end', onSessionEnd);
});
// Restore the page to normal after immersive access has been released.
function onSessionEnd() {
xrSession = null;
// Ending the session stops executing callbacks passed to the XRSession's
// requestAnimationFrame(). To continue rendering, use the window's
// requestAnimationFrame() function.
window.requestAnimationFrame(onDrawFrame);
}
インタラクションの仕組み
アプリケーションの存続期間と同様に、AR または VR でオブジェクトを操作する方法について説明します。
WebXR Device API は、ユーザー入力に「ポイント&クリック」のアプローチを採用しています。このアプローチでは、すべての入力ソースに、入力デバイスが指している場所を示すポインタ レイと、何かが選択されたことを示すイベントが定義されています。アプリはポインタ レイを描画し、ポインタが指している場所を示します。ユーザーが入力デバイスをクリックすると、select
、selectStart
、selectEnd
などのイベントが発生します。アプリは、何がクリックされたかを判断し、適切に応答します。
入力デバイスとポインタ レイ
ユーザーには、ポインタ レイはコントローラとユーザーが指している対象物との間の薄い線にしか見えません。ただし、アプリで描画する必要があります。つまり、入力デバイスのポーズを取得し、その位置から AR/VR 空間内のオブジェクトに線を引くということです。このプロセスは、おおまかに次のようになります。
let inputSources = xrSession.getInputSources();
for (let xrInputSource of inputSources) {
let inputPose = frame.getInputPose(inputSource, xrFrameOfRef);
if (!inputPose) {
continue;
}
if (inputPose.gripMatrix) {
// Render a virtual version of the input device
// at the correct position and orientation.
}
if (inputPose.pointerMatrix) {
// Draw a ray from the gripMatrix to the pointerMatrix.
}
}
これは、没入型ウェブ コミュニティ グループの入力トラッキング サンプルの簡素化版です。フレーム レンダリングと同様に、ポインタ レイとデバイスの描画は任意です。前述のとおり、このコードはレンダリング ループの一部として実行する必要があります。
仮想空間でアイテムを選択する
AR や VR で物体を指すだけでは、あまり意味がありません。何か有用なことをするには、ユーザーが選択できる必要があります。WebXR Device API には、ユーザー操作に応答するための 3 つのイベント(select
、selectStart
、selectEnd
)が用意されています。想定外の癖があり、入力デバイスがクリックされたことを示すだけです。環境内のどのアイテムがクリックされたかについてはわかりません。イベント ハンドラは XRSession
オブジェクトに追加され、利用可能になり次第追加する必要があります。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('selectstart', onSelectStart);
xrSession.addEventListener('selectend', onSelectEnd);
xrSession.addEventListener('select', onSelect);
});
このコードは入力選択の例に基づいています。詳細なコンテキストが必要な場合は、そちらをご覧ください。
何がクリックされたかを把握するには、ポーズを使用します。(驚きましたか?)詳細は、アプリまたは使用しているフレームワークに固有のものであるため、この記事の範囲外です。Cottontail のアプローチは、入力選択の例で説明しています。
function onSelect(ev) {
let inputPose = ev.frame.getInputPose(ev.inputSource, xrFrameOfRef);
if (!inputPose) {
return;
}
if (inputPose.pointerMatrix) {
// Figure out what was clicked and respond.
}
}
まとめ: 今後の展望
前述のとおり、拡張現実は Chrome 69(2018 年 6 月の Canary)でリリースされる予定です。それでも、これまでにリリースされた機能を試してみることをおすすめします。改善に役立てるため、フィードバックをお寄せください。ChromeStatus.com の WebXR ヒットテストで進捗状況を確認してください。WebXR アンカーに沿ってポーズ トラッキングを改善することもできます。