JavaScript: AR.jsで年賀状を作ってみる

こんにちは。10月中途入社のgengenです。
年賀状差し出しのデッドラインが迫ってきていますが、皆さん準備はお済みでしょうか?
今回はAR.jsというライブラリを使ってAR年賀状を作ってみようと思います。

AR.jsとは

AR.jsはthree.jsをベースにしたWebブラウザでAR(拡張現実)実現するためのJavaScriptライブラリです。

iOS(11以上)Safari、Android、WindowsPhoneに対応。ARマーカーの処理にはEmscriptenでJavaScriptにコンパイルしたARToolKitが使われています。

ARマーカーを準備

こちらのジェネレーターにマーカーにしたい画像をアップロードして、アプリで読み込むパターンファイル(.patt)と印刷用のpng画像をダウンロードします。
今回はQRコードをそのままARマーカーにしました。

とりあえず動かす

<!doctype HTML>
<html>
<!-- A-Frameを読み込み -->
<script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<!-- AR.jsを読み込み -->
<script src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.6.2/aframe/build/aframe-ar.js"></script>

<body style="margin : 0px; overflow: hidden">
  <!-- AR.jsのデバックとA-FrameのVRボタンを非表示 -->
  <a-scene embedded arjs="debugUIEnabled: false" vr-mode-ui="enabled: false">
    <!-- a-markerのtypeにpatternを設定, 作ったpattファイルをurlにを指定 -->
    <a-marker type="pattern" url="pattern-marker.patt">
      <!-- 赤い立方体を表示 -->
      <a-box position="0 0.5 0" width="0.5" height="0.5" depth="0.5" color="red"></a-box>
    </a-marker>
    <a-entity camera></a-entity>
  </a-scene>
</body>

</html>

a-marker内にA-Frameで立方体を設置しています。
a-markerがカメラ画像上のARマーカーに重なるように表示されますので、画面にかけたい効果やUI以外は基本的にa-marker内に設置していくことになります。
作ったページを表示してマーカーにカメラを向ければこのように立方体が表示されるはずです。

A-Frameとは?

急にA-Frameがなるものが登場しました。A-Frameはタグ打ちだけで3D描画ができるthree.jsベースのWebVR向けのフレームワークです。
AR.jsはthree.jsからの使用もサポートしていますが、今回は特に複雑でもないのでA-Frameを使っていきます。

画像を表示、回転させる

それではARアプリを作っていきます。
謹賀新年の文字を出して、干支の動物を走らせていれば年賀状っぽいかと思うのでそういう感じでやっていきます。

まず謹賀新年の文字を画像で表示します。

<!-- 略 -->
<a-scene embedded arjs="debugUIEnabled: false" vr-mode-ui="enabled: false">
  <a-assets>
    <!-- 画像をプリロード -->
    <img id="kinga_sinnen" src="images/kinga_sinnen.png">
  </a-assets>
  <!-- a-markerのtypeにpatternを設定, 作ったpattファイルをurlにを指定 -->
  <a-marker type="pattern" url="pattern-marker.patt">
    <!-- 赤い立方体を表示 -->
    <a-box position="0 0.5 0" width="0.5" height="0.5" depth="0.5" color="red"></a-box>
    <a-entity>
      <!-- idを指定して画像を表示、x軸回りで-90度回転 -->
      <a-image src="#kinga_sinnen" width="2" height="2" rotation="-90 0 0"></a-image>
      <!-- a-imageを内包するa-entityをy軸周りで360度回転するアニメーション -->
      <!-- dur(間隔):4000(ms), repeat(繰り返し):止めない限り無限, easing(イージング):線形(イージングしない)-->
      <a-animation attribute="rotation" to="0 360 0" dur="4000" repeat="indefinite" easing="linear"></a-animation>
    </a-entity>
  </a-marker>
  <a-entity camera></a-entity>
</a-scene>
<!-- 略 -->


うまくいっています。
A-Frameにはa-imagea-animationの他にも様々なAPIがあります。公式ドキュメントに詳しいです(当たり前)。

glTF:2.0形式の3Dモデルを表示

来年は亥年なのでウリ坊のモデルを表示してみます。
A-FrameはglTFCOLLADA、JSON形式のモデルをサポートしています。
どうやらglTF形式がナウいようなので、ここではblenderで作成したモデルをglb(glTFのバイナリ形式)として書き出して表示してみます。
blenderでモデルをglTF形式にエクスポートするにはアドオンが必要になりますのでglTF-Blender-Exporterをダウンロードしておきます。

アドオンを追加

  1. glTF-Blender-Exporter-master/scripts/addons/io_scene_gltf2ディレクトリを
    blenderのscripts/addonsディレクトリへコピー。
    (macOS版v2.79bの場合、
    ~/Applications/blender/blender.app/Contents/Resources/2.79/scripts/addons)
  2. infoメニューのFile>User Preferencesを開き、
    Add-onsタブからImport-Export: glTf 2.0 formatのadd-onを有効化。

書き出し

上記の設定が終わったらinfoメニューのFile>Export>glTF2.0(.glb)でモデルを書き出します。
glbファイルが作成されます。

glTFLoaderで表示

<!-- 略 -->
<head>
  <!-- A-Frameを読み込み -->
  <script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
  <!-- AR.jsを読み込み -->
  <script src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.6.2/aframe/build/aframe-ar.js"></script>
  <!-- glTFに含まれたアニメーションを再生するためにaframe-extrasをロード -->
  <script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v5.0.0/dist/aframe-extras.min.js"></script>
</head>
<!-- 略 -->
<a-scene embedded arjs="debugUIEnabled: false" vr-mode-ui="enabled: false">
  <a-assets>
    <!-- 画像をプリロード -->
    <img id="kinga_sinnen" src="images/kinga_sinnen.png">
    <!-- 3Dモデルをプリロード -->
    <a-asset-item id="uribo" src="assets/uribo.glb"></a-asset-item>
  </a-assets>
  <!-- a-markerのtypeにpatternを設定, 作ったpattファイルをurlにを指定 -->
  <a-marker type="pattern" url="pattern-marker.patt">
    <!-- idを指定して3Dモデルを表示 -->
    <a-entity gltf-model="#uribo" scale="0.1 0.1 0.1" animation-mixer></a-entity>
    <a-entity>
      <!-- idを指定して画像を表示、x軸回りで-90度回転 -->
      <a-image src="#kinga_sinnen" width="2" height="2" rotation="-90 0 0"></a-image>
      <!-- a-imageを内包するa-entityをy軸周りで360度回転するアニメーション -->
      <!-- dur(間隔):4000(ms), repeat(繰り返し):止めない限り無限, イージング:線形(イージングしない)-->
      <a-animation attribute="rotation" to="0 360 0" dur="4000" repeat="indefinite" easing="linear"></a-animation>
    </a-entity>
  </a-marker>
  <a-entity camera></a-entity>
</a-scene>
<!-- 略 -->

やけに暗くレンダリングされちゃってますね。
どうやらglTFはリニア色空間で色を扱っているため、glTFを使う場合、基本的にgammaOutputtrueに設定して、ガンマ補正をかける必要があるようです。
以下のようにa-sceneから設定できます。

<a-scene embedded arjs="debugUIEnabled:false;" renderer="gammaOutput: true;" vr-mode-ui="enabled: false">


やりましたね。
ちょっと絵面が寂しいですがこれで完成ということにします。

余談

glTF形式の場合ガンマ補正をかけなければいけないと書きましたが、gammaOutput=falseの状態で意図した明るさに描画されているobj形式等のモデルがすでにある場合、ガンマ補正をかけると輝度が過剰になります。

WebGLRenderer.gammaInput = trueでテクスチャがガンマ補正されてることを明示するとthree.jsが補正してくれます。

a-sceneタグのrenderer属性のgammaInput: trueをA-Frameが拾ってくれないので、以下だと動かないようです。

<a-scene embedded arjs="debugUIEnabled:false;" renderer="gammaOutput: true; gammaInput: true;" vr-mode-ui="enabled: false">

a-scene.rendererがWebGLRendererらしいのでここから設定してみます。

<!-- 略 -->
  <script>
    document.querySelector("a-scene").renderer.gammaInput = true;
  </script>
</body>

補正が入ったので元と同じにはなっていませんが、obj形式モデルの輝度がましになりました。

さいごに

今年初めあたりにはそこそこシェアがあったiOS11未満も絶滅危惧種ですので、気兼ねなくAR年賀状を出せますね。
ここまで読んでいただき、ありがとうございます。良いお年を。

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

gengen

gengenの書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ

BPSアドベントカレンダー