雪が降るAR年賀状をつくってみた（8th wall 編）

2023年のAR年賀状として、雪が降る年賀状をつくりたいと考えました。

「年賀状の部分にだけ雪が降っていたら面白いのでは？」という発想です。（ちなみに、ジョジョの奇妙な冒険のウェザー・リポートが好きで、一エリアにだけ特殊な天気を発生させる、というのをやってみたかったというのもあります。マニアックな話ですいません。）

[前回](https://qiita.com/Namy/items/5354fd38cf42c5ad3037)は、model viewerを使ったAR年賀状のつくりかたを解説しましたが、今回は、model viewerだと実装が難しく、8th wallというARをつくるツールを使いました。

完成形はこちら。

![square.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420008/aa4ef95c-1629-6c47-f1db-bf1c44bac64b.png)

![snowcardmovie.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420008/7bba4450-5f05-8f3b-c76d-a7cdc3aba123.gif)

こちらで体験できます。
https://namikawasusumu.net/nengajo2023/
※スマートフォンのブラウザでご覧ください。

自分の備忘録も兼ねつつ、このAR年賀状のつくりかたを解説します。
スマホのウェブブラウザで動く高性能なARを作ってみたい人の参考になればと思います。

また、プログラムは、githubにも置いておきます。プログラムの使用、改変は非営利/営利ともに自由です。ただし、3Dモデルの流用/販売はご遠慮ください。
https://github.com/Namy731/arsnowcard

開発環境は、
・Macbook Pro
・OSは、Catalina 10.15.7
・Blender2.82
です。

## 8th wallとは？

WebAR（Webブラウザで体験できるAR）を簡単かつ高性能につくれる有料ツールです。

8th wall
https://www.8thwall.com/

同じくWebブラウザで体験できるARのライブラリであるA-frameやAR.js（無料）でできることを強化してくれる、というイメージです。感覚値ですが、AR用のネイティブアプリを開発すればできることを10とし、ウェブブラウザ上でA-frameとAR.jsでできることを3とすると、7ぐらいのことができるイメージです。
だったら、ネイティブアプリを開発するのが一番いいじゃん、と思う人もいるかもしれませんが、ARを体験するためにアプリをダウンロードする、というのは結構ハードルがありますよね。手軽だけど本格的なARを提供したいという方には、8th wallは有力な選択肢だと思います。

今回は、AR.jsよりも平面検出の精度を上げたくて、8th wallを使用しました。

## つくりかた
8th wallでの開発の手順は、
+ ウェブブラウザ上の8th wallエディターで確認しつつ、開発。
↓
+ 8th wallのサーバーにアップする
あるいは
+ 自分のウェブサーバーにアップする
です。
今回は、自分のドメインにしたかったので、後者を選びました。

## コーディング

8thwallのコーディングは、まず、ウェブブラウザ上で使えるエディターで行っていきます。このエディター、かなり便利です。プレビュー機能やデバッグ機能もあります。

基本は、AR.js、A-frameと、コードの書き方は同じです。いくつか便利で強力な8th wall独自のライブラリもあります。

![editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420008/925f05fb-8058-b93b-9cda-d2595da64174.png)

## 3Dモデルの制作と取り込み

降ってくる雪以外の3Dモデルは、Blenderで制作し、Embedded GLTFでアニメーション込みで出力しました。このあたりは、[前回の記事](https://qiita.com/Namy/items/5354fd38cf42c5ad3037)も参考にしてみてください。制作した3Dモデルを、8th wallエディターにドラッグ&ドロップして取り込みます。

![modeling.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420008/eebed3bb-d442-5605-a171-c7e6945c70ac.png)

## headerの編集
ここには、使いたいライブラリを書いておきます。アニメーションを使う場合（アニメーションするgltfモデルを使うときなど）は、aframe-extrasを加えておきます。

```
<meta name="8thwall:renderer" content="aframe:1.2.0">
<meta name="8thwall:package" content="@8thwall.xrextras">
<meta name="8thwall:package" content="@8thwall.landing-page">
<script src="//cdn.8thwall.com/web/aframe/aframe-extras-6.1.1.min.js">
```

## bodyの編集
A-Frameではいろんな物体を配置することができます。
a-sceneタグで囲まれた中に、いろいろなa-◯◯タグで物体を置いていくルールです。

```diff_html
<a-scene
  xrextras-gesture-detector
  landing-page
  xrextras-loading
  xrextras-runtime-error
  renderer="colorManagement: true"
  xrweb="allowedDevices: any">

// この間にさまざまな物体を配置する

</a-scene>
```

a-cameraは、スマホのカメラの位置の設定です。
センターから少しずらした位置にカメラを設定しておくと、中心である"0　0　0"にモデルを配置したときに、いい感じの位置に出現するようになります。

```diff_html
  <a-camera
    id="camera"
    position="0 8 8" // カメラ位置
    cursor="fuse: false; rayOrigin: mouse;">
  </a-camera>

```
ちなみに、A-Frameでの座標は、"y座標,z座標（縦）,x座標"となります。

また、GLTFの3Dモデルを配置するには、a-assetsでファイル名を指定し、a-entityタグを使って配置します。複数配置することもできます。animation-mixerを設定すると、アニメーションが動きます。

```diff_html
  <a-assets>
  <a-asset-item id="snowcard" src="snowcard.gltf"></a-asset-item>  // ファイル名とidを指定
  </a-assets>
```

```diff_html
  <a-entity
    gltf-model="#snowcard" 　// a-assetsで指定したidを入れる
    position="0 0.05 0"
    scale="1 1 1" 
    shadow="receive: true"
    animation-mixer="loop: repeat"　// アニメーションの指定 今回はリピートも指定
    snowbase>
  </a-entity>
```

## 雪の発生
雪の発生が、今回一番苦労したところです。

+ 一定の間隔で、白い球体を新たに生成。
+ サイズと出現するxy座標は決められた範囲でランダムに。
+ 球体にはアニメーションを設定し、次々落下させていく。

という設計にしました。

![setsumei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420008/f2b4e4e8-2b18-109d-9ef3-39f26b0a19fb.png)

Javascriptのパートは、app.jsに書きます。8th wallの制作例を見ると、app.jsには呼び出しだけ書いて、別途jsファイルをつくるのが一般的なようですが、面倒なので、僕はapp.jsにガンガン書いていきます。

```diff_javascript
AFRAME.registerComponent('snowbase', {
  init() {

  setInterval(() => {

  const x = Math.random()*4-2  // x位置をランダムに設定
  const y = Math.random()*5.8-2.9　// y位置をランダムに設定
  const ra = Math.random()*0.06+0.03 // 半径をランダムに設定
  const rasize = ''+ra
  const fposition = ''+x+' 10 '+y　//スタート位置
  const eposition = "property: position; to: "+x+" -1 "+y+"; dur: 6000; easing: linear; loop: false;"　//終了位置とアニメーション
  const newElement = document.createElement('a-sphere') //球体をつくる
  newElement.setAttribute('position',fposition)
  newElement.setAttribute('radius',rasize)
  newElement.setAttribute('color','#FFF')
  newElement.setAttribute('animation__position',eposition)
  this.el.sceneEl.appendChild(newElement)　//A-Frameのシーンに加える

  }, 80)
  
  }
});

```
## 自分のウェブブラウザにアップロードする
8th wallにファイルを一括で出力できる機能が見当たらなかったので（本当はあるのかもしれない）、エディターから手動でコピペして、アップロード用のindex.htmlファイルを作っていきました。

また、8th wallのPROJECT DASHBOARDで、HOSTINGのコーナーにあるSet Up Domainsで、最終的にアップロードするドメインを設定し、APIキーを発行しておきます。

index.htmlの構造は下記の通りです。

```diff_html
<!doctype html>
<html>

<head>
<title>AR snow card</title>

<!--meta情報をここに入れる-->

<!--head.htmlのコードをここに入れる-->

</script>
<script async src="//apps.8thwall.com/xrweb?appKey=<apiキーを入れる＞"></script>

</head>

<script type="text/javascript">

// app.jsのコードを入れる

</script>

<body>

<!--body.htmlのコードを入れる-->

</body>
</html>

```

## 振り返り

Javascriptを使ったWeb ARには、さまざまな可能性があると思います。
クリックなどを使ったインタラクティブな表現も考えられます。

高性能でクリエイティビティに富んだWeb ARにチャレンジする人にとって、この記事がなにかのヒントになれば嬉しいです。

8th wallについて言えば、2022年3月には、あのポケモンGOのNianticが8th wallを買収したことでも話題になりました。8th wallにも、VPS（ビジュアルポジショニングシステム）が実装されました。次回は、VPSを実験した記事を書きたいと思っています。