🐯

Blender + AR.js + A-FrameでWebARを作る方法

2022/02/12に公開
4

はじめに

この記事ではBlenderでモデリングした自作の3Dモデルを使ってWebARを実装する手順について解説しています。

WebARとは

Webブラウザ上で動くAR(拡張現実)です。
ブラウザ上でカメラを起動させてARマーカーを読み取ると、3Dモデルなどのコンテンツが表示されます。

実際に見てみよう

スマホで下のQRコードを読み取ってみてください。

ARマーカーが合体したQRコード

URLから飛んだ先でカメラを許可してQRコードを写すと、下の画像のように表示されると思います。

AR表示後の画像

これがWebARです。

この一連の仕組みはAR.jsというJavaScriptのライブラリと、A-Frameというフレームワークで作ることができます。[1]

実装に必要なもの

  • 3Dモデルのデータ(glTF形式)
  • QRコードの画像
  • ARマーカー(.patt.pngファイル)
  • レンタルサーバー
  • テキストエディタ

今回ネックになるのがWebARアプリのURLへ遷移させる方法なのですが、今回はQRコードの画像をARマーカー化することで、画像1枚でURLへのアクセスとARマーカーの読み取りができるようにします。

作成手順

1.プロジェクトフォルダの作成

まず、データを保存しておくプロジェクトフォルダを作っておきます。

今回はwebarというフォルダの中にindex.htmlというHTMLファイルとassetsというフォルダを作りました。

3Dモデルの.glbファイルとARマーカーの.pattファイルはassetsフォルダの中に格納するようにします。

2.3Dモデルの用意

Blenderで作った3DモデルをglTF形式で保存します。

マテリアルがプリンシプルBSDFになっているか確認

マテリアルのサーフェスの種類が「ディフューズBSDF」など、プリンシプルBSDF以外になっているとマテリアルが反映されません。
「マテリアル」タブを開いて「プリンシプルBSDF」に変更します。

全てのオブジェクトをメッシュに変換

カーブ、テキスト、ヘアーパーティクルなどのオブジェクトは全てメッシュに変換します。

オブジェクトを選択した状態で、「オブジェクト」>「変換」>「メッシュ」

ヘアーパーティクルのメッシュ変換は下記リンクを参考にしました。
https://3dgraph.me/modeling/convert-hair-particle-to-mesh/

モディファイアーを全て適用

サブディビジョンサーフェスなどのモディファイアーを全て適用させます。

自動でやる場合

エクスポート画面でできます。後述のエクスポートの章を見てください。

手動でやる場合

自動でやったら適用されていない箇所がある。表示がおかしい。そういった場合は手動で行います。

「モディファイアー」タブを開き、「適用」ボタンを押します。

オブジェクトがリンク複製されている場合は、モディファイアーの適用ボタンが押せません

その場合は、該当のオブジェクトを選択した状態で、「オブジェクト」>「関係」>「シングルユーザー化」>「オブジェクトとデータ」を選択すると、適用ボタンが押せるようになります。

モディファイアーが未適用のままだと、モディファイアーを設定する前の状態で書き出されてしまいますので注意です。

エクスポート

オブジェクトを選択した状態で「ファイル」>「エクスポート」>「glTF 2.0」を選択

↓出力オプションのダイアログが表示されます。

フォーマットは「glTFバイナリ(.glb)」を選択します。

「ジオメトリ」>「モディファイアーを適用」にチェックを入れると、一括で適用してくれます。

テクスチャを設定している場合は、「UV」にチェックを入れると、オブジェクトと一緒にテクスチャも書き出してくれます。

エクスポートしたファイルをglTF Viewerで見てみます。

glTF Viewerにアップロードした3Dモデル

特に問題なく表示されていたらOKです。

3.QRコードを作成

QRコード作成ジェネレーターのQR Code GeneratorでQRコードを作ります。
今回は https://ドメイン名/webar/ で作りました。

4.ARマーカーを作成

ARマーカー作成ジェネレーターのAR.js Marker TrainingでARマーカーを作ります。

UPLOADボタンを押して先ほど作ったQRコードの画像をアップします。

Pattern Ratioの値は黒い枠の太さです。
スライドバーを動かすことで変更が可能で、値を小さくすると枠が太く、値を大きくすると枠が細くなります。
しかし枠の太さを変更するとコードを書き加える必要があるので、一旦今回は変更しないことにします。

QRコードの画像をアップできたら、画面真ん中のDOWNLOAD MAKERDOWNLOAD IMAGEボタンを押して、.patt.pngファイルを入手します。

補足

本記事の実際に見てみようで載せたマーカーの画像のように、ダウンロードした.pngファイルの画像をPhotoshopなどで加工してもOKです。

ただし、黒い枠は改変しないようにします。
また、黒い枠の周りの余白も狭くしない方が良いです。

理由は、枠のサイズがジェネレーターで作ったものと違っていると、マーカーが認識されないからです。
また、枠の周りに余白が無いと、マーカーの認識精度が低下してしまいます。

マーカーの縦横の比率を保持したまま拡大縮小するのはOKです(テスト済み)。

5.コードを書く

index.htmlにコードを書いていきます。

書き方はAR.jsの公式ドキュメントA-Frameの公式ドキュメントを参考にしています。

まず<head>要素の中に<script>要素でA-FrameとAR.jsを読み込みます。

index.html
<head>
  <!-- A-Frameを読み込む -->
  <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
  <!-- AR.jsを読み込む -->
  <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
</head>

<body>要素にstyle属性を追加します。
カメラ起動時のブラウザ画面は全画面表示なので、余白やスクロールが出ないようにするためです。

<body style="margin: 0; overflow: hidden">
</body>

次に<body>の中に<a-scene>要素を追加します。

<!-- A-FrameにAR.jsを紐づけ、VRボタン非表示、深度バッファ追加 -->
<a-scene embedded arjs vr-mode-ui="enabled: false;" renderer="logarithmicDepthBuffer: true;">
</a-scene>

<a-scene>はオブジェクトを配置するための舞台です。
ここにembeddedarjs属性を追加し、A-FrameとAR.jsを紐づけます。

このとき任意でvr-mode-ui="enabled: false;"も追加することで、ブラウザ起動時に右下に表示されるVRのボタンを非表示にできます。

また、renderer="logarithmicDepthBuffer: true;"も追加して、対数深度バッファを設定します。
深度バッファを設定しないと、3Dモデルの表示にノイズが走ったり、チカチカ明滅したりすることがあります。

門松の表示がジャギっている

これは同じ場所に複数のポリゴンがある場合、奥行きの計算がうまく出来ず、レンダリングの順番が競合するために起こる現象で、「Z-fighting(Zファイティング)」と呼ばれます。

対数深度バッファというのは、同じ場所にあるポリゴンの前後関係を明確にして、正しい順番でレンダリングするための方法です。
深度バッファを設定すると、下の写真のように滑らかな表示になります。

次に<a-scene>の中には、以下のタグを追加します。

<!-- 3Dモデルを読み込む -->
<a-assets>
  <a-asset-item id="tiger" src="./assets/tiger.glb"></a-asset-item>
</a-assets>

<!-- マーカーの.pattファイルを読み込む  -->
<a-marker type="pattern" url="./asset/pattern-tiger.patt">
  <!-- 3Dモデルを呼び出す -->
  <a-entity gltf-model="#tiger"></a-entity>
</a-marker>

<!-- カメラを追加 -->
<a-entity camera></a-entity>

まず、<a-asset-item>要素で3Dモデルを読み込みます。
<a-marker>type属性は3種類ありますが[2]、ジェネレーターで作ったパターンマーカーを使うので、ここではpatternにします。

次に読み込んだ3Dモデルを<a-entity>gltf-model属性で呼び出します。

最後にカメラを追加して、完了です!

↓全体のコードです。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- A-Frame を読み込む -->
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <!-- AR.js を読み込む -->
    <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
    <title>任意のページタイトル</title>
  </head>
  <body style="margin: 0; overflow: hidden">
	  
    <!-- A-FrameにAR.jsを紐づけ、VRボタン非表示、深度バッファ追加 -->
    <a-scene embedded arjs vr-mode-ui="enabled: false;" renderer="logarithmicDepthBuffer: true;">
	    
      <!-- 3DCGモデルを読み込む -->
      <a-assets>
        <a-asset-item id="tiger" src="./asset/tiger.glb"></a-asset-item>
      </a-assets>

      <!-- マーカーの.pattファイルを読み込む -->
      <a-marker type="pattern" url="./asset/pattern-tiger.patt">
        <!-- 3Dモデルを呼び出す -->
        <a-entity gltf-model="#tiger"></a-entity>
      </a-marker>

      <!-- カメラを追加 -->
      <a-entity camera></a-entity>
    </a-scene>
  </body>
</html>

6.サーバーにアップ

プロジェクトフォルダをサーバーにアップロードします。
そして作ったARマーカーをパソコンのディスプレイに映したり、印刷して、スマホで読み取れるように準備します。
スマホのQRコードリーダーでQRコードを読み取るとカメラが起動するので、そのままQRコードを読み取り続けると3Dモデルが表示されます。
表示されたら成功です!

Tips

サンプルコードをアレンジする方法をまとめました。

ARマーカーの黒枠を細くする

Pattern Ratioの値を変更し、DOWNLOADボタンを押して.pattファイルと.pngファイルをダウンロードします。
Pattern Ratioの値は覚えておきます。

次に、<a-scene>arjs属性にプロパティを追加します。

index.html
<a-scene embedded arjs="patternRatio: 設定した値">

例:Pattern Ratioを0.9に設定した場合

index.html
<a-scene embedded arjs="patternRatio: 0.9">

3Dモデルの向きを変える

これは、Blender側で変更することもできますし、コードを書き加える方法でもできます。

<a-entity>rotation属性を追加します。

index.html
<a-entity gltf-model="#3Dモデルのid" rotation="X軸 Y軸 Z軸"></a-entity>

X軸、Y軸、Z軸を基準に、回転させる角度を入力します。

X軸は奥から手前へ回転します。

Y軸は時計回りに回転します。

Z軸は反時計回りに回転します。

反対向きに回転させたいときは、マイナスを付けます。

余談になりますが、通常、ARマーカーは座布団のように3Dモデルの真下に来ます。

イメージ図

パソコンのディスプレイなどにARマーカーを表示してスマホで読み取る場合は、ディスプレイが地面に対して垂直になっているので、3Dモデルの頭しか見えない、という事態が起きます。

下の画像のように3DモデルをX軸方向へマイナス90度傾けて寝かせてあげることで、ARマーカーと並行に表示させることができます。

3Dモデルに回転アニメーションを付ける

下記Twitterの投稿のように、3Dモデルを回転させることができます。

<a-entity>animation属性を追加します。

index.html
<a-entity gltf-model="#3Dモデルのid" animation="property: rotation; to: X軸 Y軸 Z軸; loop: true; dur: 10000"></a-entity>

to: X軸 Y軸 Z軸は、それぞれの軸に対して何度回転させるかを0-360度の間で入力します。

例えば、Z軸方向に1回転させたい場合は、to: 0 0 360と入力します。

loop: trueにすると、無限に回転します。

durというのは「duration(間隔)」のことで、アニメーションの1サイクルの長さです。
単位は「millisecond」なので、1秒は1000ミリ秒になります。
デフォルトではdur: 1000で1秒と設定されています。ここでは10秒になるように変更しています。
お好きな秒数に設定してください。

A-Frameの基礎知識

サンプルコード以外のものを実装しようとするとA-Frameの知識が必要になる場合が多いので、A-Frameについても参考程度に書き留めておきたいと思います。

Entity Component System

A-Frameのコード記法は、3Dやゲーム開発でよく使われる Entity Component System という設計方法に基づいています。
これは文字通り、Entity、Component、Systemの3つの要素で構成されます。

Entity(エンティティ) とは、その世界に存在する物体を指します。ゲームで例えると、キャラクター、武器、乗り物などです。
エンティティだけでは機能せず、後述のコンポーネントで肉付けをしていくことによって実体化します。

A-Frameでは<a-entity>要素で表現されます。

Component(コンポーネント) とは、物体に付随しているデータのことです。
ゲームで例えると、キャラクターの攻撃力や防御力、体力など、そのキャラクターを形作る要素に当たります。

A-Frameでは<a-entity>のHTML属性で表現されます。


EntityとComponentの相関

System(システム) とは、その世界の設定のことです。
ゲームで例えると、キャラクターと敵の衝突時に体力を減らす(体力をマイナスして更新する)というシステム設定などがこれに当たります。

A-Frameでは<a-scene>のHTML属性で表現されます。

まとめると、

  • <a-scene>のHTML属性で全体に関わる設定を書き、
  • <a-entity>でオブジェクトを追加し、
  • <a-entity>のHTML属性でオブジェクトに肉付けをする

というのが、A-Frameの基本的な書き方です。

Asset Management System

5.コードを書くの章において、3Dモデルを表示させるコードを書きました。
これはモデルを一旦読み込んでから呼び出すという手順を踏んでいます。これはなぜなのでしょうか?

A-Frameには、Asset Management System というシステムが存在します。
オブジェクト(アセット)の読み込みと表示を分けることで、読み込み時間の短縮やレンダリングパフォーマンスを向上させることができるというものです。

まず、<a-assets>の中に<a-asset-item>で3Dモデルを読み込みます。

呼び出すときは、<a-entity>gltf-model属性で呼び出します。

<a-scene>
  <a-assets>
    <a-asset-item id="3Dモデル" src="3Dモデル.glb"></a-asset-item>
  </a-assets>

  <a-entity gltf-model="#3Dモデル"></a-entity>
</a-scene>

このように、3Dモデルなどのアセットを事前に読み込んでおくことで、遅延なくARコンテンツを表示させることができます。

おわりに

BlenderとAR.jsとA-Frameを使ってWebARを作成する方法を解説してみました。
JSを書く必要がなくHTMLだけで完結するため、すごく簡単に作ることができるのでおすすめです。
みなさんも良かったら作ってみてください!

参考リンク

公式ドキュメント

Tips

A-Frameの基礎知識

Blender

Zファイティング(Z-fighting)

更新履歴

2023年2月9日

  • A-Frameのバージョンを最新版に書き換え
  • Zファイティング、深度バッファの記述を追加
  • 参考リンクにZファイティングの項目を追加

2022年2月12日

  • 記事を投稿
脚注
  1. 他にも方法はあります。AR.js + Three.jsやA-Frameのwebxrコンポーネント、MindARなど。 ↩︎

  2. デフォルトマーカーのhiro、行列計算から自動生成されるbarcode(ここで作成できる)、本記事のジェネレーターで作成したpatternの3種類。 ↩︎

Discussion

iidaaaiidaaa

はじめまして。
Akaneさんのこの投稿を拝見しながら、勉強している初心者です。
とてもわかりやすい内容で、順序通り行った際にしっかり表示させることが出来ました。
ありがとうございます!

ひとつ質問させてください。
私がBlenderで作成したモデリングデータなのですが、記載通りに出力した後、glTF Viewerにドラッグ&ドロップすると正確に表示されるのですが、実装して、スマホ上で表示させるとオブジェクトによってジャギが走ってしまう現象(チラついてしっかりと表示されていない)になってしまいます。
AkaneさんがあげていらっしゃるQRコードをスマホで読み込むと門松あたりが同様のジャギが走った表示になるのですが、原因はわかりますでしょうか?
スマホによる性能の違い?オブジェクトの厚さがないから?

もし改善できるとしたら対処法も知りたい次第です。
何卒よろしくお願いいたします!

AkaneAkane

iidaaaさん、初めまして。嬉しいお言葉ありがとうございます!

ジャギが走ってしまう現象ですが、わたしのスマホ(Android)でもiidaaaさんと同様の表示になりました。↓

調べた結果、この現象は「Zファイティング」と呼ばれていることがわかりました。
同じ場所に複数のポリゴンがある場合、奥行きの計算がうまく出来ずレンダリングの順番が競合してしまうため、そのポリゴンの表示がジャギったり、チカチカ明滅することがあるそうです。

対処法は、<a-scene>renderer="logarithmicDepthBuffer: true;"を追加してください。

index.html
<a-scene embedded arjs renderer="logarithmicDepthBuffer: true;">

これで下の写真のように滑らかに表示されると思います。↓

※このコードは、対数深度バッファ(logarithmicDepthBuffer)を追加する=ポリゴンを正しい前後関係でレンダリングする、という意味になります。

ジャギるのは手ブレのせいかなと思っていて、深く追求していなかったのですが、iidaaaさんのおかげでさらに綺麗に表示させることができました。ありがとうございます!
この内容は、記事にも追記しておきます。
コードを試していただいても改善されない場合は、またご返信いただけますと幸いです。
よろしくお願いいたします。

iidaaaiidaaa

Akaneさん
丁寧に解説いただき、また、解決方法までご享受いただきありがとうございました!
挙げていただいたコードを入力したらZファイティングがない状態で表示されました。

大変助かりました!

余談なのですが、
別の方が提示していたコードとも組み合わせてBlender上でアニメーションをつけたデータをUPして試したのですが、こちらだと一筋縄では改善されなかったです。(表示されるがZファイティングが改善ならず)
A-Frameのバージョンの違い、A-FrameとAR.jsを読み込む際のhttps以降のアドレスが違う、等少々コード記述が違う部分がある
みたいなので、そこが関係しているのかなと思います。少し自分でも解決策を模索してみます。

もし埒が明かない場合はまたご相談させていただきたく思います。

何卒よろしくお願いします!

AkaneAkane

iidaaaさん、お返事が遅くなってしまいすみません。
ご提案させていただいたコードで正常に表示ができたとのことで、ほっとしております。
Blenderでアニメーションをつけた3Dモデルでは、まだ不具合があるということですね。
こちらは解決策が思い浮かばずすみません。
また私の方でも試してみて、何かわかりましたらこちらに書き込みいたします!