CCCMKホールディングス TECH LABの Tech Blog

TECH LABのエンジニアが技術情報を発信しています

ブログタイトル

A-FrameとFlaskでVR空間でデータを可視化する方法について調べてみました!

こんにちは、CCCMKホールディングス技術開発の三浦です。

野菜不足が気になっていたので、スムージーを作る習慣を始めました。料理でいろんな野菜を取ろうとすると結構大変ですが、スムージーだと簡単に取ることが出来ます。今度はこの野菜を使ってみようかな、とスーパーの野菜売り場に立ち寄るのが少し楽しくなってきました。

最近A-FrameというWebブラウザで動くVR(Virtual Reality)アプリを開発することが出来るWeb Frameworkが気になっており、使ってみたいな、と考えていました。自分が普段関わることの多い機械学習周辺と絡められると面白いな、と色々と活用案を考えていたところ、「VR空間でデータを見たら、2Dでは出会えない気づきに出会えるのでは」と考えるようになりました。

たとえばデータの関係性を見るのに2Dや3Dの散布図を描いたりしますが、あの散布図の中にもし自分が入ることが出来たら、全然違う気づきが得られるんじゃないかなぁと思うのです。データに溺れてみたい・・・そう思うようになりました。

VRアプリと聞くとすごく難しそうな印象があったのですが、A-Frameを使うとHTMLファイルの中にVR空間に追加したいオブジェクト(Entity)を簡単に追加していくことが出来ます。さらにサーバー側の処理はFlaskというWebフレームワークを使うことでPythonで作ることが出来ます。データの加工処理はPython、その結果をVRで表示、といったことが可能になります。

今回はFlaskとA-FrameでVR空間に仮想的な商品の売上数量を表す棒グラフを表示してみる、といったことにチャレンジしてみました。PCのブラウザで表示するとこんな感じです。

PCのブラウザで表示した様子

一方、Meta Quest2のブラウザで表示するとこんな感じになります。

Meta Quest2のブラウザで表示した様子

画像で見るとそれほど・・・ですが、VR空間で見ると大きな柱のような棒グラフにびっくりしました!データの大きさなどを身をもって体験してみたい/体験してもらいたい、なんていう時はすごく効果的なデータ可視化方法だと思いました。

A-Frame

A-FrameはHTMLでVR(Virtual Reality)を開発することが出来るWebフレームワークです。Three.jsというウエブブラウザで3Dグラフィックスを描画することが出来るJavascriptライブラリをベースに作られています。

aframe.io

A-Frameは色々なVRヘッドセットに対応していて、Meta Quest2にも対応しています。特別な設定をしなくても、今回作ってみたアプリにMeta Quest2のブラウザでアクセスするとVR表示(360度表示)にすることが出来ました。

A-Frameではエンティティ・コンポーネント・システム(ECS)という3Dゲーム開発で利用される設計パターンが採用されています。空間の中に配置されるオブジェクトはA-FrameではEntityとして扱われます。Entity、たとえば車のオブジェクトを考えた場合、それを構成するタイヤや外装といった部品が紐づきますが、これらの部品をA-FrameではComponentとして扱います。1つのEntityが複数のComponentの組み合わせで構成されます。そしてこのComponentを交換したり使いまわしたりして効率的にEntityを作り出せる点がECSのメリットの1つのようです。

立方体や球体、円柱などのよく使う形状のEntity-Componentの組み合わせはPrimitivesとしてシンプルなコードで追加することが出来るようになっています。また別のソフトウェアで作成した3Dモデルも取り込むことが出来ます。たとえばglTFという3Dモデルのファイル形式で出力し、アプリ起動時に読ませるようにすると、VRアプリで表示することが出来ます。A-FrameではAsset Management Systemというシステムにより、アプリに必要になる3Dモデルやテクスチャ画像などをAssetとして扱います。Assetはアプリ起動時にロードされ、VRアプリ内で必要に応じて描画されます。

A-Frameで提供される便利な機能にA-Frame Inspectorというビジュアルツールがあります。ブラウザでVRアプリを表示中に、Windowsでは<ctrl>+<alt>+<i>キーを押すと起動することが出来ます。Inspectorではオブジェクトの配置やサイズの変更などを画面上で行い、その時の値を確認することが出来ます。

A-Frame Inspector

それでは今回Flask側、A-Frame側でどんな作業をしたのか説明していきます。プロジェクトディレクトリ内に以下のような構成で必要なファイルを配置しました。

.
├── app.py
├── templates
│   ├── index.html
│   └── models
│       └── bottle.gltf

Flaskでやること

Flaskではグラフ描画用のデータ(products)を用意し、サーバにリクエストが来たらA-Frameを組み込んだHTMLファイルにデータをレンダリングしクライアントに返す、という動作をPythonで記述しました。

'''
app.py
'''

from flask import Flask, render_template

# 画像や3Dモデルなどの静的ファイルを扱うためにstatic_folderの指定が必要
app = Flask(__name__,static_folder='./templates/models')

@app.route("/")
def index():
    # 商品リスト
    products = [
    {
        'name':'Powerful Drink',
        'number':2.0,
        'color':'blue'
    },
    {
        'name':'Relax Tea',
        'number':5.0,
        'color':'green'
    },
    {
        'name':'Orange Juice',
        'number':4.0,
        'color':'orange'
    }
    ]
    
    return render_template('index.html',products=products)

A-Frameでやること

A-Frameでは<a-scean>タグの中に必要なEntityなどをタグで追加していきます。<a-assets>タグを使うとこのアプリで使用するAssetを登録できます。ここでは自作のドリンクボトル3Dモデルを登録しています。そしてこのAssetを<a-entity>タグの中に指定したgltf-modelComponentでロードしています。棒グラフになるのがPrimitiveの中で円柱の形状を扱う<a-cylinder>で、これのheightにFlaskから渡された仮想の商品売上数量をセットして棒グラフのように扱っています。

<!DOCTYPE html>
<html>
  <head>
    <script src="https://aframe.io/releases/1.4.1/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-assets>
        <a-asset-item id="bottle" src="/models/bottle.gltf"></a-asset-item>
      </a-assets>
      <a-text
        value="Drink sales comparison" 
        position="-2.0 5.0 -2.0"
        scale="1.5, 1.5, 1.5">
      </a-text>
      <a-entity 
        gltf-model="#bottle" 
        scale="1.5 1.5 1.5"
        position="-2.8 4.6 -1.9"
        rotation="0 0 -30"
      >
      </a-entity>
      {% for product in products %}
      <a-cylinder 
        position="{{ -1.5 + 1.5 * (loop.index - 1) }} {{ product.number / 2.0 }} -4" 
        radius="0.5" 
        height="{{ product.number }}" 
        color="{{ product.color }}"
      >
      </a-cylinder>
      <a-text position="{{ -1.5 + 1.5 * (loop.index - 1) }} {{ product.number / 2.0 }} -3.5" 
        value="{{ product.name }}">
      </a-text>
      {% endfor %}
      <a-plane 
        position="0 0 -4" 
        rotation="-90 0 0" 
        width="6" 
        height="6" 
        color="#6e6e6e">
      </a-plane>
      <a-sky color="#000000"></a-sky>
      <a-camera position="0 3 3.5">
      </a-camera>
    </a-scene>
  </body>
</html>

3Dモデルについて

グラフのタイトルの横に表示しているドリンクボトルの3DモデルはBlockbenchというローポリゴンモデル制作ツールを使用して作りました。

www.blockbench.net

立方体を積み上げて3Dモデルを作ることが出来ます。作ったモデルは色々な形式でエクスポートすることが出来、今回はglTF形式でエクスポートしました。

Blockbenchでモデルを作っている様子

まとめ

ということで、今回は最近気になっていたA-FrameというVRアプリがHTMLで開発出来るWebフレームワークについて、調べて試してみた話をご紹介しました。もっと色々調べてみて、面白いVRアプリが作れるようになりたいと思いました。Flaskと組み合わせると機械学習関連の機能とも連携しやすそうなので面白いアプリが作れそうで、とても楽しみです!