A-Frame で WebXR hit-test を利用する方法

すぎどん
2021-03-11
すぎどん
2021-03-11

A-Frame v1.1.0 でDom-Overlay、hit-testが使えるようになったのは、以前記事にしました。

A-Frame v1.1.0 から Hit-test と Dom-overlay の実装が入りました

今回は hit-test について書いていきます。
これで現実空間の垂直面/水平面を取得することができます。

WebXR の設定

前回も書きましたが、A-FrameにWebXRを利用することを知らせる必要があります。
そのため、A-Frameにはwebxrコンポーネントが用意されています。

実装は簡単で、a-sceneタグにwebxrコンポーネントを設定するだけ

 <a-scene webxr>

hit-test の設定

これだけでは hit-test を利用することはできません。
以下の4ステップの設定が必要です。

 - webxr コンポーネントの設定
 - xrHitTestSource の取得
 - hitTestResult の取得
 - 位置情報・回転情報の反映

それぞれ見ていきましょう。

webxr コンポーネントの設定

先ほど設定したwebxrコンポーネントの引数として、

 - optionalFeatures: hit-test

を設定します。

 <a-scene webxr="optionalFeatures: hit-test">

xrHitTestSource の取得

xrHitTestSource とはhit-testの結果を取得するための事前準備・インターフェースと考えてください。
詳しくは以下を参考に。

https://immersive-web.github.io/hit-test/#hit-test-source-interface

具体的にどのように取得するかは、

 - a-scene から renderer を取得
 - renderer から session を取得
 - session からxrRefereneceSpaceを取得します。
 - 取得したxrReferenceSpace を引数にxrHitTestSourceを取得します。

let scene = document.querySelector('a-scene');
let renderer = scene.renderer;
let session = renderer.xr.getSession();
let viewerSpace = await session.requestReferenceSpace("viewer");
let xrHitTestSource = await session.requestHitTestSource({
    space: viewerSpace
});

 xrReferenceSpace についてどこかで別途説明を書こうと思います。

https://immersive-web.github.io/webxr/#xrreferencespace

hitTestResult の取得

取得した xrHitTestSource を使って、hit-testを実行して結果(hitTestResult)を取得します。

 - AnimationFrameを取得する。
 - xrReferenceSpaceを取得する。
 - AnimationFrameからhitTestResultを取得する

let frame = scene.frame;
let refSpace = renderer.xr.getReferenceSpace();
let hitTestResults = frame.getHitTestResults(refSpace);

位置情報・回転情報の反映

取得したhitTestResultを解析して、位置・回転情報を取得する。

 - hitTestResultからpose情報を取得する
 - pose情報から位置、回転情報を取得する
 - それぞれを対象に反映する

const pose = hitTestResults[0].getPose(refSpace);
target.setAttribute("position", pose.transform.position);
target.object3D.quaternion.copy(pose.transfrom.orientation);

コンポーネント化する

実際に使うことを考えてコンポーネント化しておくと便利になります。
hit-test結果を自分自身に反映するコンポーネントを作成します。

ARセッションスタート時にxrHitTestSourceを取得して、
フレーム毎にhitTestResultを取得して、自分自身の位置、回転情報を更新します。

 

AFRAME.registerComponent("ar-hit-test", {
init: function() {
// session start
this.el.sceneEl.renderer.xr.addEventListener("sessionstart", async () => {
if (this.el.sceneEl.is("ar-mode")) {
this.renderer = this.el.sceneEl.renderer;
let session = this.renderer.xr.getSession();
let viewerSpace = await session.requestReferenceSpace("viewer");
this.xrHitTestSource = await session.requestHitTestSource({
space: viewerSpace
});
}
});

// session end
this.el.sceneEl.renderer.xr.addEventListener("sessionend", async () => {
this.xrHitTestSource = null;
});
},
tick: function() {

const frame = this.el.sceneEl.frame;
if (!frame) return;

// hit-test in real world
const xrHitTestSource = this.xrHitTestSource;
if (xrHitTestSource) {
const refSpace = this.renderer.xr.getReferenceSpace();
const hitTestResults = frame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
const pose = hitTestResults[0].getPose(refSpace);
this.el.setAttribute("position", pose.transform.position);
this.el.object3D.quaternion.copy(pose.transform.orientation);
}
}
}
});