8th WallのLightship VPSについての解説/コードからVPSの仕組みを理解する

※この度、8th WallのLightship VPSについて寄稿いただきました。全3回に渡り解説をお送りします。今回は3回目となります。前回の記事はこちらです!

8th WallのVPSのサンプルコードを見ていきながら、実装してみます(2022/10/10現在の情報です)。

今回は、公式が出しているLightship VPS Bespokeのコードを見ていきましょう。

※gifはサンプルページより引用

どういう仕組みなのか

Bespokeのサンプルでは、木にぶつかった風船が凹んで、そのままフワッと飛んでいっているのですが、実はこの風船の動きの部分は、コードで制御しているのではなく、3D側で制御しています

前回書いたように、Geospatial browserのページで、Wayspot空間のglbデータをダウンロードできるようになっています。

8th WallのWayspotのプロジェクト登録画面
開いてみるとこんな感じ

この空間のglbメッシュの原点が、VPS体験の原点となるので、

このglbにピッタリと合うように配置した3Dモデルを作る事で、実際の空間に重なるようにうまく表示してくれるのです。

ということで、VPSは、いかにリッチな3Dモデルを作って見せられるか、がめちゃくちゃ肝になってくるのではないかな、と思います。


前回の記事では、Wayspotをプロジェクトに登録する時に、綺麗なメッシュのglbをプロジェクトに入れるといいよ、というお話をしました。

その理由は、この空間glbに合わせて、3Dモデルのデータを作成するので、その位置合わせをやりやすくするため、なんですね。

コードを見ていこう

body.html(体験の元になる部分)

さて、ざっくりと仕組みが大体わかったところで、コードを見ていきます。
まずはbody.htmlを見ていきます。公式のコードから、重要そうな部分を抜粋したのがこちらです。

<a-scene
  landing-page
  vps-coaching-overlay
  background="color: #303030"
  renderer="colorManagement: true"
  gltf-model="dracoDecoderPath: https://cdn.8thwall.com/web/aframe/draco-decoder/"
  xrextras-runtime-error
  xrextras-loading
  xrweb="enableVps: true">

<a-assets>
    <a-asset-item id="vps-mesh" src="./assets/california-p.glb"></a-asset-item>
    <a-asset-item id="vps-anim" src="./assets/california-p-anim.glb"></a-asset-item>
 </a-assets>

<!-- 中略 -->

<a-entity named-wayspot="name: california-p">
    <!--hider material-->
    <a-entity
      gltf-model="#vps-mesh"
      xrextras-hider-material>
    </a-entity>
    <!--shadow material-->
    <a-entity
      gltf-model="#vps-mesh"
      shadow-shader
      shadow="cast: false">
    </a-entity>
    <!--vps animation-->
    <a-entity
      gltf-model="#vps-anim"
      cubemap-realtime
      play-vps-animation
      shadow="receive: false">
    </a-entity>
  </a-entity>

<!-- 後略 -->

a-sceneの中で、xrweb=”enableVps: true”を指定することで、VPSをすぐプロジェクトで使い始められます。

3行目のvps-coaching-overlayを入れておくと、VPS登録地点に近づいた時に、端末をかざすように指示するUIが表示されます。

プロジェクトに登録されていない地点でも、近くに登録地点があると表示される仕様のようです。


18行目、named-wayspot=”name: california-p”という部分で、Wayspotを登録しています。
このnameは、以下の部分にあたります。以下のように日本語でもOKです。
private scanされた地点の場合、12文字の英数字になるはずです。


19-23行目、xrextras-hider-materialというコンポーネントを指定しています。
#vps-meshというモデルには、空間glbが指定されています(12行目)。このglbを、実空間に合わせて配置して、そこを透明なメッシュにしてくれます。この透明メッシュにめり込むようにモデルを置くと、めり込んだ部分が隠れます
以下は床面にのみ透明メッシュが置いてある状況ですが、これが空間の凹凸に合わせて置かれている、と考えるとわかりやすいかなと思います。


24-29行目で影を指定しています。
30-36行目で3Dモデルのアニメーションについて指定しています。play-vps-animationを指定しておくと、vps-coaching-overlayが消えるのを待ってから、アニメーションを開始できます。このコンポーネントは、named-wayspot.jsの中で定義されています。

components/realtime-cubemap.js(モデルに環境を反射させる)

realtime-cubemapは、環境に置いてあるものをモデルに反射させる仕組みです。

詳細は、こちらのサンプルから。

Realtime Reflectionsのサンプルページより抜粋

このGIFを見るとわかると思いますが、みかんやオブジェの色や、ろうそくの光などが、リアルタイムでくるくる回る球体に映り込んでいるのがわかりますよね。

components/shadow-shader.js(影を落とす)

shadow-shaderは文字通り、モデルの影を落とすための実装です。

components/named-wayspot.js(Wayspotでのモデル表示制御)

named-wayspotの中で、モデルの表示・非表示を制御しています。

このコードの、namedWayspotComponentがVPSの肝になってくる部分ですので、抜粋して見てみましょう。

const namedWayspotComponent = {
  schema: {
    name: {type: 'string'},
  },
  init() {
    const {object3D} = this.el
    const {name} = this.data
    object3D.visible = false
    const foundWayspot = ({detail}) => {
      if (name !== detail.name) {
        return
      }
      object3D.position.copy(detail.position)
      object3D.quaternion.copy(detail.rotation)
      object3D.visible = true
    }
    const updateWayspot = ({detail}) => {
      if (name !== detail.name) {
        return
      }
      object3D.position.copy(detail.position)
      object3D.quaternion.copy(detail.rotation)
    }
    const lostWayspot = ({detail}) => {
      if (name !== detail.name) {
        return
      }
      object3D.visible = false
    }
    this.el.sceneEl.addEventListener('xrprojectwayspotfound', foundWayspot)
    this.el.sceneEl.addEventListener('xrprojectwayspotupdated', updateWayspot)
    this.el.sceneEl.addEventListener('xrprojectwayspotlost', lostWayspot)
  },
}

やっていることは比較的シンプルで、xrprojectwayspotfound, xrprojectwayspotupdated, xrprojectwayspotupdated, というイベントがそれぞれ走った時に、モデルを出したり、引っ込めたり、表示をアップデートしたりしています。

実装する時のポイント

基本的に、bespokeのサンプルを元に実装して、モデルの差し替えをするだけで、簡単に自分の好きな場所でモデルを出せます。

モデル作成のコツ

プロジェクトから空間glbをダウンロードして、Blenderなどのソフトを使って、そこに合わせてモデルを作成・配置しましょう。
この時、空間glbの原点を動かさないように注意してください。動かしてしまうと、AR表示位置が狂ってしまいます。

複数地点で出し分けるには

複数のWayspotを検知させることも可能です。

その場合は、body.htmlのa-asset-itemに追加して、それぞれに違うidを振っておき、
named-wayspotごとにa-entityを追加していけばOKです。
それぞれのgltf-modelのidを対応させることをお忘れなく)コードにすると、こんな感じになると思います。

<a-scene
  landing-page
  vps-coaching-overlay
  background="color: #303030"
  renderer="colorManagement: true"
  gltf-model="dracoDecoderPath: https://cdn.8thwall.com/web/aframe/draco-decoder/"
  xrextras-runtime-error
  xrextras-loading
  xrweb="enableVps: true">

<a-assets>
    <a-asset-item id="vps-mesh1" src="./assets/spot1.glb"></a-asset-item>
    <a-asset-item id="vps-anim1" src="./assets/spot1-anim.glb"></a-asset-item>

    <a-asset-item id="vps-mesh2" src="./assets/spot2.glb"></a-asset-item>
    <a-asset-item id="vps-anim2" src="./assets/spot2-anim.glb"></a-asset-item>
 </a-assets>

<!-- 中略 -->

<a-entity named-wayspot="name: ここにspot1の名前">
    <!--hider material-->
    <a-entity
      gltf-model="#vps-mesh1"
      xrextras-hider-material>
    </a-entity>
    <!--shadow material-->
    <a-entity
      gltf-model="#vps-mesh1"
      shadow-shader
      shadow="cast: false">
    </a-entity>
    <!--vps animation-->
    <a-entity
      gltf-model="#vps-anim1"
      cubemap-realtime
      play-vps-animation
      shadow="receive: false">
    </a-entity>
</a-entity>

<a-entity named-wayspot="name: ここにspot2の名前">
  <!--hider material-->
  <a-entity
    gltf-model="#vps-mesh2"
    xrextras-hider-material>
  </a-entity>
  <!--shadow material-->
  <a-entity
    gltf-model="#vps-mesh2"
    shadow-shader
    shadow="cast: false">
  </a-entity>
  <!--vps animation-->
  <a-entity
    gltf-model="#vps-anim2"
    cubemap-realtime
    play-vps-animation
    shadow="receive: false">
  </a-entity>
</a-entity>

<!-- 後略 -->

まとめ

ということで、bespokeを例に取りながら、VPSの仕組みについてまとめてみました。
こうして見ていくと、シンプルなコードで、かなり柔軟で面白いAR表現を追求できそうな仕組みだなと思いました。
ますますリッチなWebAR体験ができそうで、ワクワクしますね!


執筆者  影織(@kageori_ar
ARと切り絵を組み合わせた作品を中心に、様々な制作をしています。https://kageori.pythonanywhere.com/