A-Frame WebXRを使って床面にBOXを置いてみる
前のblogでhit-test、dom-overlayについて紹介しました。
dom-overlay
hit-test
今回は実際に動くアプリを作成する過程で、実装方法を説明していきます。
作成するもの
床面を認識してカーソルを表示し、ボタンを押すことでカーソル上にBOXを配置します。
以下リンクをARCoreに対応したAndroidスマートフォン上のChromeブラウザでアクセスし、右下の「AR」ボタンを押してください。
A-Frame WebXRで床面にBOXを置くデモ
ARCoreに対応したAndroidで、ブラウザはChromeのみで動作します。iPhoneでは動作しませんのでご注意ください。
ARCore対応端末の確認は以下をご覧ください。
https://developers.google.com/ar/discover/supported-devices
最近のメジャーな端末は大体対応しているかと思いますが、特殊な端末だと非対応だったりします。
例えば、私の持っている「Rakuten Hand」は非対応でした。
てっきり対応と思っていたのですが、確認して購入すればよかった・・・
実装方法
実装方法を順を追って説明していきます。
A-Frameのベースソースの取得
以下からA-Frameのベースとなるソースを取得します。
https://glitch.com/~aframe
「View Source」ボタンを押して、index.htmlのソースをコピーします。
glitch.com上のソースのため、glitch.comを使っている方はRemixするだけで自分のプロジェクトへ取り込むことができます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, WebVR! • A-Frame</title>
<meta name="description" content="Hello, WebVR! • A-Frame">
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
</head>
<body>
<a-scene background="color: #ECECEC">
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
</a-scene>
</body>
</html>
WebXRの設定
a-sceneタブにWebXRコンポーネントを設定し、hit-test、dom-overlayの設定を追加します。
background設定は削除します。
<a-scene webxr="optionalFeatures: hit-test, dom-overlay; overlayElement:#overlay;">
Hit-Test
床面に沿って丸いカーソルが表示されるようになります。
カーソルの作成
a-sceneタグ内のオブジェクト実装を行います。
まず、既存のオブジェクトは不要のため削除します。
次に平面を沿うように動くカーソルオブジェクトを追加します。
カーソルはhit-test結果を反映するa-entityオブジェクトの子要素として追加します。
<!-- コメントアウト。削除しても大丈夫です。
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
-->
<!-- hit-testを反映するa-entityオブジェクトを作成し、その子要素としてカーソルを表現するa-circleを追加します。 -->
<a-entity id="hitTest" ar-hit-test>
<a-circle scale="0.1 0.1 0.1" rotation="-90 0 0"></a-circle>
</a-entity>
hit-testの設定
追加したa-entityにhit-test結果を反映する「ar-hit-test」コンポーネントを作成します。
コンポーネントはheadタグ内に<script>〜</script>の形で追加します。
<script>
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);
}
}
}
});
</script>
Dom-Overlay
ボタンを押したらBOXを置けるようになります。
ボタンdomの作成
ARモードでも表示するDOMオブジェクトを設定します。
webxrに設定した
overlayElement:#overlay;
とIDを揃えるように実装します。
<div id="overlay">
<input id="button" type="button" style="position:absolute; bottom: 20px; left: 20px; width:150px; height: 50px; z-index:100;" value="ボタン">
</div>
BOXの配置ロジック
配置ボタンのclickイベントに、BOXを配置するロジックを追加します。
hit-test結果を反映しているa-entityオブジェクトからposition、rotation情報を取得してBOXに反映しています。
positionをそのままBOXに反映すると半分埋まってしまいます。床面から0.05だけ上げてpositionを反映しています。
<script>
var scene = document.querySelector("a-scene");
var hitTest = document.querySelector("#hitTest");
var button = document.querySelector("#button");
button.addEventListener("click", function() {
let box = document.createElement("a-box");
box.setAttribute("color", "cyan" );
box.setAttribute("position", new THREE.Vector3(0, 0.05, 0).add(hitTest.object3D.position));
box.setAttribute("rotation", hitTest.getAttribute("rotation"));
box.setAttribute("scale", "0.1 0.1 0.1");
scene.appendChild(box);
});
</script>
まとめ
ソースの全容は以下を確認ください。
https://glitch.com/edit/#!/statialab-cegblog-webxr-001?path=index.html
WebARはマーカーARが主流でしたが、hit-testとdom-overlayが使えば、マーカーに縛られないARを実現することができます。
アイデア次第で面白いサービスが作れると思っています。