没入型ウェブとは、ブラウザでホストされる仮想世界体験のことです。このバーチャル リアリティ体験全体が、ブラウザや VR 対応ヘッドセットに現れます。
没入型ウェブとは、ブラウザでホストされる仮想世界体験のことです。これには、ブラウザや VR 対応ヘッドセット(Google の Daydream、Oculus Rift、Samsung Gear VR、HTC Vive、Windows Mixed Reality Headsets など)で表示されるバーチャル リアリティ(VR)エクスペリエンスと、AR 対応モバイル デバイス用に開発された拡張現実(AR)エクスペリエンスが含まれます。
ここでは、没入体験を 2 つの用語で表現しますが、さまざまなレベルの AR が存在する、完全な現実から完全に没入感のある VR 環境まで、さまざまなレベルで考える必要があります。
臨場感あふれるエクスペリエンスの例:
- 没入感のある 360° 動画
- 臨場感あふれる環境で表示される従来の 2D(3D)動画
- データの可視化
- ホーム ショッピング
- アート
- まだ誰も考えていないような魅力的な機能
そこにはどうやって行けばよいですか?
没入型ウェブは、約 1 年前から初期の状態で利用されてきました。 これは、Chrome 62 以降のオリジン トライアルで利用できる WebVR 1.1 API を使用して行われました。この API は、Firefox、Edge、および Safari 用のポリフィルでもサポートされています。
でも、次に進みましょう。
オリジン トライアルは 2018 年 7 月 24 日に終了し、この仕様は WebXR Device API と新しいオリジン トライアルに置き換えられました。
WebVR 1.1 はどうなったのですか?
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 は、臨場感あふれるエクスペリエンスの範囲のあらゆるものを表す一種の代数変数として意図されています。これは、前述のオリジン トライアルとpolyfillで利用できます。
この記事が Chrome 67 のベータ版に公開されたときは、VR 機能のみが有効でした。Chrome 69 で拡張現実が導入されました。詳しくは、ウェブ用の拡張現実をご覧ください。
この新しい API については、これ以外の記事でも詳しく説明しています。WebXR サンプルを理解するために、ここで十分役立つ情報を紹介します。詳細については、元の説明とImmersive Web Early Adopters Guide をご覧ください。オリジン トライアルの進行に伴い、後者を拡張します。ぜひ、Issue をオープンしたり、pull リクエストを送信したりしてください。
この記事では、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 回繰り返します。
プレゼンテーション フレームをリクエストする
「フレーム」という言葉には、Web XR の文脈ではいくつかの意味があります。1 つ目は参照フレームです。これは、座標系の原点の計算元と、デバイスが動くと原点がどうなるかを定義します。(ユーザーが移動してもビューは同じままですか?それとも実際のようにシフトしますか?)
2 つ目のタイプのフレームは、XRFrame
オブジェクトで表されるプレゼンテーション フレームです。このオブジェクトには、AR/VR シーンの単一フレームをデバイスにレンダリングするために必要な情報が含まれています。プレゼンテーション フレームは requestAnimationFrame()
を呼び出して取得するため、やや混乱します。これにより、window.requestAnimationFrame()
との互換性が確保されます。
詳細をご説明する前に、いくつかコードをご紹介します。以下のサンプルは、レンダリング ループを開始して維持する方法を示しています。単語フレームが 2 つ使用されていることに注目してください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 における物の位置と向きのことをポーズと呼びます。ビューアと入力デバイスの両方にポーズがあります。(入力デバイスについては後ほど説明します)。ビューアと入力デバイスのポーズは、どちらも 4 行 4 列の行列として定義され、列優先の順序で Float32Array
に格納されます。現在のアニメーション フレーム オブジェクトで 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 つのビューを反復処理するのは無意味に思えるかもしれませんが、そうすることで、さまざまなデバイスに対して 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.
}
}
これは、Immersive Web Community Group の入力トラッキング サンプルを省略したバージョンです。フレーム レンダリングと同様に、ポインタレイとデバイスの描画は任意です。前述のように、このコードはレンダリング ループの一部として実行する必要があります。
仮想空間でアイテムを選択する
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(Canary は 2018 年 6 月時点)では拡張現実が期待されています。とにかく、これまでの方法を試すことをおすすめします。改善するにはフィードバックが必要です。ChromeStatus.com の WebXR ヒットテストで進行状況を確認します。また、WebXR アンカーに従うと、ポーズの追跡が改善されます。