ブラウザだけでARを実現するAR.jsを使ってAR時計を作ってみよう

AR (Augmented Reality, 拡張現実) といえば、昨年大流行したポケモンGOでも使われていたり、最新のiOSリリースでAR機能が強化されたりと、最近では話題に事欠きません。

今回は、そのAR機能をブラウザだけで実現できるJavaScriptライブラリのAR.jsを使って簡単なARを作ってみましょう。

AR.jsとは

AR.jsは、WebGLやWebRTCなどのWeb標準の機能のうえに実現されたライブラリで、アプリをインストールすることなくWebブラウザだけでAR機能を実現することができます。

技術的には、ネイティブ向けのARライブラリであるARToolkitをEmscriptenを使ってJavaScriptに変換するというなかなか豪快な作りになっていますが、使う分にはいたって普通のJavaScriptライブラリとして使用することができます。スマホのブラウザ上でも問題ないパフォーマンスで動作するので、いろいろな応用ができそうな可能性を秘めています。

ARには、画像認識を助ける2次元パターンの「ARマーカー」を使う通常のARと、ARマーカーを使わないマーカーレスARがありますが、現在AR.jsではマーカーレスでない通常のARのみをサポートしているようです。

AR.jsを使ってみよう

今回はそのAR.jsを使って以下のようにARマーカーにデジタル時計をオーバーレイ表示する機能を作ってみます。

AR時計完成イメージ

 

なお、ここでは以降の実装にClojureScriptを使いますが、JavaScriptやその他のAltJSの言語でも同じように作ることができます。

AR.jsはJavaScriptから使えるいくつかの3DあるいはVR向けのライブラリ、フレームワークと連携させて使うことができるようになっています。ここではそのなかで、JavaScriptから使える3Dライブラリとして知名度の高いThree.jsと合わせて使う方法を見てみます。

AR.jsをThree.jsを合わせて使う場合、機能分担は次のようになります:

  • AR.jsはWebRTCからの入力を基にARマーカーの位置を特定し、マーカーの中心を原点とする座標系を用意してくれます。この座標系は、カメラの位置や角度が変わってもマーカーを追従するように随時更新されるため、ここにオブジェクトを配置することであたかもそのオブジェクトがマーカーに固定されているように表示することができます。
  • AR.jsで用意した座標系を基に、Three.jsで任意のオブジェクトを配置します。一旦マーカーの座標が特定できればあとはThree.jsで自由にグラフィックを組み立てていくことができます。

AR.jsで提供されている主要なAPIは次の3つです:

1. THREEx.ArToolkitSource

   (js/THREEx.ArToolkitSource. #:js{:sourceType <source type>}) 

  ARで使用する入力ソースを指定します。WebRTC経由でウェブカメラを使うことができるほか、画像や動画も入力ソースとして指定することができます。

2. THREEx.ArToolkitContext

   (js/THREEx.ArToolkitContext. <opts>

  入力ソースからマーカーを特定する処理をするコンテキストを作ります。<opts>ではキャンバスのサイズやカメラパラメータ等の各種設定を指定します。詳しくは 公式ドキュメント を参照して下さい。

3. THREEx.ArMarkerControls

   (js/THREEx.ArMarkerControls. <context> <marker> <opts>

  2.で作ったコンテキストを<context>に渡すことで、<marker>に指定したオブジェクトを常にマーカーの位置に追従させることができます。<opts>により追従するマーカーの情報をします。

 

さて、ここまで見たところでこの3つのAPIを組み合わせてシンプルなARを作ってみましょう。ARのサンプルとしてよくある、マーカー位置に立方体を表示する例を作ってみます。

コードはこちらを参照下さい: 立方体を表示するサンプルコード

これを実行すると、次のようにマーカー位置に立方体が表示されます:

 

AR立方体サンプル

 

ARで時計を表示してみよう

ここまでで、AR.jsの基本的な部分は押さえられました。先の例では単純な立方体を表示するだけでしたが、Three.jsを使ってより複雑なARを実現することができます。

ARでデジタル時計を実現するためには何が必要でしょうか?やり方はいろいろ考えられますが、ここでは次のように実現してみます:

  1. 現在時刻を表すテキストオブジェクトを配置する
  2. 配置したテキストオブジェクトを定期的に更新する

現在時刻を表すテキストオブジェクトを配置する

Three.jsでテキストオブジェクトを扱うには TextGeometry を使いますが、TextGeometryを使うにはフォントをあらかじめロードしておく必要があります。

フォントのロードには、FontLoaderを使います。以下のようなコードを書くことで、<path> にフォントデータをロードすることができます(なお、フォントデータはフォントごとにJSON形式で用意しておく必要がありますが、いつくかのフォントに関してはThree.jsの Examples の中にすでに用意されています。facetype.js を使って自前で作ることもできます)。

■ フォントをロードする処理

(.load (js/THREE.FontLoader.) <path>
  (fn [font]
    ... <ロードしたfontを使った処理> ...))

 

ロードしたフォントをオプションで指定してTextGeometryを作り、それをThree.jsのメッシュでくるんでやるとテキストオブジェクトを生成できます:

■ テキストオブジェクトの生成処理

(let [geo (js/THREE.TextGeometry. <text> #js{:font font ...})
      mat (js/THREE.MeshNormalMaterial. <mat opts>)]
  (js/THREE.Mesh. geo mat))

配置したテキストオブジェクトを定期的に更新する

TextGeometryで表示するテキストの内容はコンストラクタの第一引数に文字列として指定できるため、現在時刻を文字列にして渡してやれば時刻をテキストとして表現できます。しかし、一度生成したTextGeometryのテキストを後から変更することはできないので、刻一刻と時刻が変化していくのを素直に実現することができません。

そこで、定期的にテキストの内容を更新するごとに新たなテキストオブジェクトを生成し、元のテキストオブジェクトと差し替えることでテキスト内容の更新を実現します:

■ テキストオブジェクトの定期的な更新

(defn update-clock [clock target]
  (when clock (.remove target clock))
  (let [clock' <現在時刻のテキストオブジェクトの生成処理>]
    (.add target clock')
    (js/setTimeout #(update-clock clock' target) 200)))

 

これらを組み合わせると、以下のように1秒ごと(実際には200ミリ秒ごと)に更新されるデジタル時計として動くことが確認できます:

 

AR時計アニメーション

 

まとめ

今回はブラウザだけでARを実現できるJavaScriptライブラリであるAR.jsを使って、ARでデジタル時計を作ってみました。

この試みによって以下のようなことが分かりました:

  • AR.jsを使って非常に簡単にブラウザ上にAR機能を実現できる
  • スマホ上でも(ネイティブアプリでなくても)実用的なパフォーマンスでARを動かすことができる
  • Three.js等のライブラリと組み合わせて使えば、好みに合わせて自由にグラフィックを拡張していける
  • ただし、裏を返すとThree.js等の使い方が分からないと好きなように表示させるのはやや難しいかも

また、今回はよくある単なるARのグラフィックを表示するだけのサンプルプログラムではなく、時々刻々と変化する内容を伴った表示をARで実現するサンプルを試すことで、ユーザへ情報提示をする表現方法としてのARの可能性も改めて確認することができました。

今回作ったサンプルプログラムは以下のデモページから実際に確認することができます。その際は、下のARマーカーをあらかじめ印刷しておくか、別の端末で表示しておくように下さい。

ARマーカー

HaLakeイベント情報

上記のバナーをクリックいただくと、HaLakeで行われる、勉強会、ワークショップ、もくもく会などのイベント情報をご覧いただけます。

お問い合わせ

コワーキングスペースの利用や、プログラミング教室に関して、お問い合わせください。
また、セミナー、ワークショップ、イベント等の開催についてもお気軽にどうぞ。

HaLakeとは、埼玉県越谷市レイクタウン駅から南に徒歩数十秒のビルの4Fにある、コワーキングスペースです。

この内容で送信します。よろしいですか?