2020年12月22日

AR.jsでLocation Based ARを作ってみる

目次

  1. はじめに
  2. Location Based ARとは
  3. 試してみる
  4. AR.jsのLocation Based ARの課題
  5. Location Based ARの体験を良くする工夫
  6. Location Based ARが向いてるケース
  7. まとめ

はじめに

この記事は 新しい技術にチャレンジし続けるpalanのアドベントカレンダー 22日目の記事です。 昨日は「デザイン制作・コーディングツール「Hadron」を使ってみた」という記事でした。
デザイン制作・コーディングツール「Hadron」を使ってみた
今回はAR.jsのLocation Based ARを作ってみたので、その作り方や特徴を紹介します。

Location Based ARとは

Location Based AR(ロケーションベース型AR)とはGPSなどの位置情報に基づいて、特定の位置にARコンテンツを表示させるARです。例えば地図アプリのナビゲーションで目的地の方向や位置を示すのに使われてたりします。
地図アプリでLocation Based ARを使っている例
地図アプリのナビゲーションでLocation Based ARを使っている例

AR.jsのLocation Based AR

AR.jsのバージョン3以降ではLocation Basedに対応しています。これを使うと、わずかなコードでLocation BasedのWebARを簡単に作成できます。 AR.jsのLocation Based ARはA-Frameバージョンとthree.jsバージョンの2つがありますが、本記事ではA-frameバージョンを使用しています。 AR.js Documentation
Location Based – AR.js Documentation
A-Frame – Make WebVR A-Frameに関しては以前の記事でも触れてるので良かったらこちらもご覧ください。
A-Frameで始めるクリエイティブ・コーディング入門

試してみる

まずは最小限のコードでLocation Based ARを作成して体験してみます。 A-FRAMEのblog記事にちょうどいいサンプルコードがありますので、こちらを試してみます。

  <html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
  </head>

  <body style="margin: 0; overflow: hidden;">
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <a-text
        value="This content will always face you."
        look-at="[gps-camera]"
        scale="50 50 50"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
      ></a-text>
      <a-camera gps-camera rotation-reader> </a-camera>
    </a-scene>
  </body>
</html>

[Image Tracking and Location-Based AR with A-Frame and AR.js 3 – A-Frameから引用

POINT!!

コードの一部を解説をします。

  <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
  <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>  

A-Frameと、AR.jsのA-FrameバージョンのJavaScriptを読み込みます。
また、後述するlook-atを使用するためのライブラリも読み込みます。

  <a-camera gps-camera rotation-reader></a-camera>

GPS情報を利用するためa-cameragps-cameraを追加します。
加えて、回転イベントを処理するためにrotation-readerも追加します。

<a-text
  value="This content will always face you."
  look-at="[gps-camera]"
  scale="50 50 50"
  gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
></a-text>

look-at属性に[gps-camera]を指定することで、ARコンテンツが常にカメラの方を向きます。テキストや画像などの2Dコンテンツでは基本的にこの指定を入れることをおすすめします。3Dはケースバイケースで指定してください。 ARコンテンツにはgps-entity-placeARコンテンツを配置したい座標の緯度経度を指定します。ソースコード内の、<add-your-latitude>に緯度<add-your-longitude>に経度を入れます。

例(東京タワーに表示したい場合)

gps-entity-place="latitude: 35.65861027555679; longitude: 139.74542998791907;"

TIPS

Google Mapで取得したい座標で右クリックすると一番上に緯度経度が表示されます。緯度経度部分をクリックするとクリップボードにコピーされます。
google mapで緯度経度取得

体験する

上記のソースコード内の<add-your-latitude><add-your-longitude>にARコンテンツを表示させたい緯度経度を指定したらサーバーにアップしてスマートフォンでアクセスします。 GPS情報取得とデバイスモーションの利用、カメラのアクセスを求められるので全て許可します。
スマートフォンの画面をARコンテンツを設置した方向に向けるとARコンテンツがその位置に表示されます。
テキストのみでちょっと味気ないですが、簡単にLocation Based ARを体験できました。

AR.jsのLocation Based ARの課題

このようにAR.jsを使うと簡単にLocation BasedのWebARを作成できます。
ただ、体験してもらうと分かりますが課題に感じる部分も幾つかあります。

オクルージョンされない

オクルージョンとは手前にある物体が後ろにある物体を隠して見えなくする状態のことです。
AR.jsのLocation Based ARでは、カメラとARコンテンツの間に物体がある場合でも、ARの体験画面では常に全体が表示されます。(正確にはLocation Basedに限った話ではないですが)
カメラとの距離によってスケールは縮小されて表示はされるのですが、体験する側としては遠くにあるコンテンツが縮小されているのか、小さいコンテンツが目の前にあるのか判断するのは難しいです。

位置取得の精度

カメラ位置の取得にはGeoLocation APIを使用しています。そのためデバイスのGPS取得の精度に依存します。
GPS取得の精度を良くするためLocation BasedのARでは屋外での使用を推奨されてますが、屋外であっても私が試してみた所感ですが、10m前後のブレはあります。
GPSの位置がブレることで画面内のARコンテンツの表示される位置も変わってきます。特にARコンテンツとの距離が近いほどそのズレは大きくなります。

Location Based ARの体験を良くする工夫

前述のような課題はありますが、設定を変えたり工夫次第で良い体験を生むことはできます。
幾つか例を紹介します。

ARコンテンツを表示する最大距離、最小距離を設定する


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
  </head>
  <body style="margin: 0; overflow: hidden;">
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <a-box
        material="color: red"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
        scale="30 30 30"
      ></a-box>
      <a-camera gps-camera="minDistance:30; maxDistance:100" rotation-reader> </a-camera>
    </a-scene>
  </body>
</html>

gps-cameraminDistancemaxDistanceプロパティを設定すると、ARコンテンツを表示する最大距離、最小距離を設定できます。単位はmです。 最大距離を設定した場合、その距離より離れている場合はARコンテンツを表示しません。
これにより、遠く離れた位置からでもARコンテンツが表示されてしまう問題を防げます。
また、最小距離を設定した場合は、その距離より近い場合はARコンテンツを非表示にします。
例えばARコンテンツの大きさが10mのように大きい場合、その中に入ってしまった場合にうまく表示されない問題を防ぎます。 これらの設定をすることで限られたエリアにいる場合のみARコンテンツを出現させることができます。
ARコンテンツまでの距離が100m以下になると表示される

位置情報の小さな変化を無視する


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
  </head>
  <body style="margin: 0; overflow: hidden;">
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <a-box
        material="color: red"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
        scale="30 30 30"
      ></a-box>
      <a-camera gps-camera="gpsMinDistance:10;" rotation-reader> </a-camera>
    </a-scene>
  </body>
</html>

gps-cameragpsMinDistanceプロパティを使用するとGPS更新イベントの発生をメートル単位で制御できます。上記のコードではGPSの変化が10m以下の場合は更新イベントを発生させません。 指定した値以下の位置情報の変化を無視することができるので、頻繁な位置情報の変化によるARコンテンツの「ジャンプ」を防ぐことができます。
デフォルトは5mに設定されており個人的には妥当な値だと思いますが、コンテンツのブレが気になる場合はこの値を調整すると良いでしょう。

ARコンテンツまでの距離を表示する


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
    <style>
      #debug {
        position: fixed;
        z-index: 10000;
        background-color: #fff;
        padding: 10px;
        top: 0;
        left: 0;
        display: block;
      }
    </style>
  </head>

  <body style="margin: 0; overflow: hidden;">
    <div id="debug"></div>
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <a-box
        material="color: red"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
        scale="15 15 15"
      ></a-box>
      <a-text
        id="text"
        value=""
        look-at="[gps-camera]"
        scale="30 30 30"
        position="0 55 0"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
      ></a-text>
      <a-camera gps-camera rotation-reader> </a-camera>
    </a-scene>
    <script>
      window.addEventListener('load', () => {
        const text = document.getElementById('text');
        text.addEventListener('gps-entity-place-update-positon', (event) => {
          document.getElementById('debug').textContent = `あと${event.detail.distance}m`;
          text.setAttribute('value', text.getAttribute('distanceMsg') + ' left');
        });
      });
    </script>
  </body>
</html>

gps-entity-place-update-positonイベントを使用すると、カメラからARコンテンツまでの距離を取得できます。
イベント名 呼ばれるタイミング Payload
gps-entity-place-update-positon gps-entity-placeの指定されているARコンテンツの位置が更新されたとき { detail: { distance: <Number> }}
この値を画面内で表示させることで体験者にARコンテンツまでどのくらい離れてるかを伝えることができます。 なお、このイベント発生のタイミングでARコンテンツの distancedistanceMsgカスタム属性にも値が渡されます。 distanceにはARコンテンツまでの距離、 distanceMsgには<整数の距離> meterが入ります。 そのため、下記の方法でも距離を取得できます。

const distanceMsg = document.querySelector('[gps-entity-place]').getAttribute('distanceMsg');
console.log(distanceMsg);   // "890 meters"

上記コードでは、画面上部と、a-textにARコンテンツまでの距離を表示させてます。
a-textにはa-box と被って文字が見えなくならないように、 positionプロパティで高さを指定しています。
公式ドキュメントでは positionの単位はmと記載されてるのですが、検証してみたところあまり高さは正確に反映されなそうです。 なお、a-textなどのARコンテンツに距離を表示させる場合は、距離が遠すぎるとサイズが縮小されて文字が読めなくなるので使われるシーンに応じてscaleの調整が必要です。

補足

a-textは標準で日本語には対応してません。日本語を表示させた場合は下記の記事を参考にしてください。
A-Frameの日本語フォントを用意する – Crieit
ARコンテンツまでの距離が変化するたびに、その距離が反映されます

その他のイベント

gps-entity-place-update-positonイベントの他、現在の位置を取得する gps-camera-update-positonイベントなども用意されてます。
location basedで使えるカスタムイベントは下記ページ「Custom Events」の「Feature」に「Location Based」と書いてあるものを参照してください。
UI and Events AR.js Documentation

距離に応じてARコンテンツを変化させる

A-FrameのARコンテンツはJavaScriptで動的にプロパティを変化させることができます。
この特性と前述で取得できる距離を使って、特定の距離より近づいた場合にARコンテンツの色を変えたり動きをつけたりなど変化をつけると、インタラクティブな体験ができます。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
  </head>
  <body style="margin: 0; overflow: hidden;">
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <a-box
        material="color: red"
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
        scale="30 30 30"
      ></a-box>
      <a-camera gps-camera rotation-reader> </a-camera>
    </a-scene>
    <script>
      window.addEventListener('load', () => {
        const el = document.querySelector('[gps-entity-place]');
        el.addEventListener('gps-entity-place-update-positon', (event) => {
          if(event.detail.distance < 100) {
            el.setAttribute('material','color: yellow');
          } else {
            el.setAttribute('material','color: red');
          }
        });
      });
    </script>
  </body>
</html>

event.detail.distancでARコンテンツまでの距離が取得できるので、条件分岐を使い100以下ではARコンテンツの色を yellowに、それ以外では redにします。
ARコンテンツまでの距離が100m以下になるとBOXの色が黄色に変わります

組み合わせてみる

これらの手法は組み合わせて使用することが可能です。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Location-based AR.js demo</title>
    <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js"></script>
    <!-- animation-mixtureを使うため -->
    <script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v4.2.0/dist/aframe-extras.min.js"></script>
    <style>
      body {
        font-family: sans-serif;
      }
      * {
        box-sizing: border-box;
      }
      #distance {
        position: fixed;
        z-index: 10000;
        background-color: rgba(0,0,0,.8);
        color: #fff;
        text-align: center;
        padding: 10px;
        top: 10px;
        left: 5%;
        width: 90%;
        display: block;
      }
      body {
        margin: 0;
      }
      #message {
        position: fixed;
        z-index: 10;
        left: 5%;
        bottom: 30px;
        width: 90%;
        padding: 5%;
        color: #fff;
        font-weight: bold;
        background-color: rgba(0,0,0,.8);
        border-radius: 20px;
        border: 3px solid #fff;
        display: none;
      }
    </style>
  </head>

  <body style="margin: 0; overflow: hidden;">
    <div id="distance"></div>
    <div id="message">
      大鎧ドラゴンがあらわれた!
    </div>
    <a-scene
      vr-mode-ui="enabled: false"
      embedded
      arjs="sourceType: webcam; debugUIEnabled: false;"
    >
      <!-- 3Dモデル(gltfファイル) -->
      <a-assets>
        <a-asset-items id="dragon" src="dragon.gltf" ></a-asset-items>
      </a-assets>
      <a-entity
        gps-entity-place="latitude: <add-your-latitude>; longitude: <add-your-longitude>;"
        gltf-model="#dragon"
        scale="7 7 7"
        rotation="0 0 0"
        pisition="0 -1 0"
        animation-mixer
      ></a-entity>
      <a-light
        id="directionLight"
        type="directional"
        color="#FFF"
        intensity="0"
        position="-1 1 2">
      </a-light>
      <a-light
        id="ambientLight"
        type="ambient"
        intensity="0"
        color="#FFF">
      </a-light>
      <a-camera gps-camera="minDistance:10; maxDistance:50" rotation-reader> </a-camera>
    </a-scene>
    <script>
      window.addEventListener('load', function () {
        const el = document.querySelector('[gps-entity-place]');
        const distanceMsg = document.getElementById('distance');
        const message = document.getElementById('message');
        const directionLight = document.getElementById('directionLight');
        const ambientLight = document.getElementById('ambientLight');
        el.addEventListener('gps-entity-place-update-positon', (event) => {
          distanceMsg.textContent = `あと${event.detail.distance}m`;
          if(event.detail.distance <= 30) {
            // 30m以下で明るくする、メッセージ表示、距離非表示
            directionLight.setAttribute('intensity',1);
            ambientLight.setAttribute('intensity',1);
            distanceMsg.style.display = 'none';
            message.style.display = 'block';
          } else if(event.detail.distance <= 50) {
            // 50m以下で近づくほど明るくする、メッセージ非表示、距離表示
            directionLight.setAttribute('intensity',1 - event.detail.distance / 50);
            ambientLight.setAttribute('intensity',1 - event.detail.distance / 50);
            distanceMsg.style.display = 'block';
            message.style.display = 'none';
          } else {
            // 50mより離れると、明るさ0、メッセージ非表示、距離表示
            directionLight.setAttribute('intensity',0);
            ambientLight.setAttribute('intensity',0);
            distanceMsg.style.display = 'block';
            message.style.display = 'none';
          }
        });
      });
    </script>
  </body>
</html>

組み合わせて少しリッチな体験となるようにしてみました。このコードの仕様は下記のようになってます。
  • ARコンテンツまでの距離を画面内に表示
  • ARコンテンツとの距離50m以下でARコンテンツを表示
  • 50m以下で近づくほどライトを強くする(明るくなる)
  • 20m以下でライトの強さMax、メッセージを表示
3Dモデル出現時に演出や音を付けたりするとより臨場感が出て面白そうです。

Location Based ARが向いてるケース

一通り、Location Based ARを試してみたところ、向いてるケース向かないケースは下記のようになるかと思います。

向いている

  • 建造物が少ない公園や開けた場所
  • 大きなサイズのARコンテンツ
  • ある程度の座標の位置ブレを許容できる
  • 表示される高さを厳密に求めない
  • 宝探しやアート系のAR

向いてない

  • 建造物が多い
  • 室内などGPSを正確に取得できない場所
  • 小さなサイズのARコンテンツ
  • 正確な座標に表示させたい
  • 表示される高さを厳密に求める
なお現在の仕様における所感なので、機能アップデートによりこの限りではなくなることもあります。
また向いてないケースであってもアイデア次第では面白い体験を生むことはできると思います。

まとめ

AR.jsのLocation Based ARを試してみましたが、ARコンテンツを表示させるだけでなく、距離が取得できたり、設定を細かくカスタマイズできたりと意外とできることが多いと思いました。
まだ発展途上の技術と感じる部分も多々ありますが、一方で限られた条件の中でどのように工夫して良い体験を生むのか考えるのも一つの面白さだと思います。 アイデア次第で面白い使い方もできると思いますので是非試してみてください。

WebAR/VRのお仕事に関するご相談

Bageleeの運営会社、palanではWebAR/VRに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。

無料相談フォームへ

1

0

AUTHOR

ogawa

ogawa

雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

簡単に自分で作れるWebAR

「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。

palanARへ
palanar

palanはWebARの開発を
行っています

弊社では企画からサービスの公開終了まで一緒に関わらせていただきます。 企画からシステム開発、3DCG、デザインまで一貫して承ります。

webar_waterpark

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRの企画・開発をやっています。森に住んでいます。

ゲスト bagelee

ゲスト bagelee

かっきー

かっきー

まりな

まりな

suzuki

suzuki

miyagi

ogawa

ogawa

雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。

いわもと

いわもと

デザイナーをしています。 好きな食べ物はラーメンです。

kobari

taishi kobari

フロントエンドの開発を主に担当してます。Blitz.js好きです。

shogokubota

kubota shogo

サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!

nishi tomoya

aihara

aihara

グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。

nagao

SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。

kainuma

Kainuma

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

sugimoto

sugimoto

asama

ando

iwasawa ayane

oshimo

oshimo

異業界からやってきたデザイナー。 palanARのUIをメインに担当してます。 これからたくさん吸収していきます!

CONTACT PAGE TOP