WebフレームワークでVR体験、A-Frameの実践テクニック

カバー

[!] この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

近年の VR 技術の拡がり

みなさんは"VR"、体験したことありますか。

頭をすっぽり覆う形状のデバイスをかぶって映像を見る、そんな技術のことです。USJ(ユニバーサル・スタジオ・ジャパン)のようなアミューズメント施設に限らず、いまは自宅でも利用できます。すごい時代になりました。視界いっぱいに映像が広がる様は"見る"というよりも"体験"という表現がぴったりです。私はとある観光地の映像を"体験"したことがあり、後日その場所を訪れたときに既視感を覚えたことに感動したものです。このように VR は従来のただ映像を見るだけと比べると記憶の定着率が高いといわれ、教育の分野で近年注目され、企業でも研修などに導入する動きが広まっています。

VR を体験するための専用デバイス(以降 HMD =ヘッド・マウント・ディスプレイの略)はいくつか課題があります。まずは高価であること。市販価格の安いものでも 5 万円はくだらず、エンタープライズ版となると 15 万円以上の高価格帯です。また GPU やバッテリーを内蔵しているため本体重量があります(約 0.5kg ~ 0.8kg)。HMD の性質上、映像を間近に見るので本体重量と相まって長時間使用は目や首に負担になります。実際の利用にはややハードルが高い VR ですが、実は Web ブラウザーでも利用することが可能です。さすがに HMD のような臨場感はありませんが、空間の把握という点では十分です。ブラウザーでの利用はコンサート会場の座席確認や不動産業の内見、大学の施設紹介など場所や空間の紹介に活用されています。

この記事では Web ブラウザーで VR を実現するためのフレームワーク「A-Frame」を紹介します。

Web ブラウザー上で VR を実現する A-Frame

A-Frame は three.js をベースとする WebGL(Web Graphics Library)の一種です。WebGL はいまや多くのブラウザーでサポートされています。使いこなせば家庭用の 3D ゲームに匹敵する表現が可能です。 そんな A-Frame を利用するために必要なものは Web ブラウザーと HTML が書けるエディターだけです。以下のように head 部に CDN でホストされた A-Frame の .js ファイルを記載します。

<html>
  <head>
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene></a-scene>
  </body>
</html>

<a-scene>タグのなかに専用のタグをつかって 3D オブジェクト、背景画像を指定します。A-Frame 標準のタグでいくつかの 3D モデル(立方体や球体)を利用できるので専用のモデルは不要です。以下は 3D の球体を表示する簡単なサンプルコードです。<a-sphere>が 3D 球体、<a-sky>には 360 度カメラで撮影した画像を指定しています。表示だけならたったこれだけです。サンプルの写真部分をドラッグ・アンド・ドロップしてみてください。自由にアングルを動かせます。

<body>
  <a-scene>
    <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
    <a-sky src="./sample.jpg" radius="30"></a-sky>
  </a-scene>
</body>

ベースが HTML なので、A-Frame 専用タグの要素に対しても DOM(Document Object Model)の操作が可能です。DOM の操作は JavaScript に限らず、Web フロントエンドフレームワークである Angular や Vue などからも可能なのでこれらを組み合わせることで VR な Web アプリケーションの開発ができるのです。次章以降では Web アプリケーションに必須なインタラクティブ操作の実現方法について紹介していきます。

オブジェクトのクリック判定を取得する

Web アプリケーションのインタラクティブな操作の一歩としてオブジェクトのクリック判定を取得する方法を紹介します。よくある DOM 操作のように、要素に対してイベントハンドラーを設定します。イベントハンドラーの登録は AddEventListener ではなく A-Frame 専用の方法になります。以下はオブジェクトをクリックしたときに色が変わるサンプルです。同時に開発者コンソールにログを出すようにしています。オブジェクトの色変更は クリックされた要素を取得して、属性値に setAttribute するという JavaScript ではよくある方法で簡単に実現できます。

<script>
  AFRAME.registerComponent("clicktest", {
    init: function () {
      this.el.addEventListener("click", function (evt) {
        console.log("clicked!");
        var sphere = document.getElementById(this.id);
        var color = sphere.getAttribute("color");
        if (color.match("red")) {
          sphere.setAttribute("color", "yellow");
        } else if (color.match("yellow")) {
          sphere.setAttribute("color", "red");
        }
      });
    },
  });
</script>
<body>
  <a-scene>
    <a-light type="directional" color="#FFF" intensity="1" position="0 2 1"></a-light>
    <a-sphere clicktest id="sphere" class="collidable" position="0 1.25 -5" radius="1.25" color="red"></a-sphere>
    <a-sky clicktest src="./sample.jpg" radius="30"></a-sky>
    <a-camera position="0 0 0">
      <a-cursor cursor="rayOrigin: mouse" visible="true" raycaster="objects: .collidable"></a-cursor>
    </a-camera>
  </a-scene>
</body>

コードを詳しく見ていきます。まず<script> タグの AFRAME.registerComponent でイベントの名称を設定します。ここで設定した名称は HTML の 各種タグに設定する属性 のような形で<a-sphere>などの要素に設定します。サンプルの<a-sphere>の最初に書かれている"clicktest"のような形ですね。A-Frame の仕様で名称は"clickTest"のようなキャメルケースが使えない点に注意してください。サンプルコードの clicktest をキャメルケースにするだけで動作しなくなります。

次は A-Frame における接触判定です。マウスクリックしたポイントの先にあるオブジェクトに、当たり判定を持たせないとクリックが検知できません。当たり判定は<a-cursor>中の"raycaster"で設定されています。"objects: .collidable"という値が指定されていますね。これは"collidable"クラスを持つオブジェクトに対する当たり判定をチェックするという意味になります(collide はぶつかるという意味です)。サンプルでは背景の<a-sky>にも clicktest が指定されていますが、"collidable"が指定されていないため、マウスクリックしてもログ出力されません。

データロードを検知してログを出す

360 度パノラマの 4K 写真となるとファイルサイズが大きくなるので画像データのロードに時間がかかります(高精度な画像だと 30MB を超えます)。ここでは背景となる写真の読込が完了したかどうかを検知する方法を紹介します。ロード完了検知は単独の HTML では効果を実感しにくいですが、Web フロントエンドフレームワークの画面描画タイミングを検知するときに特に効果を実感できると思います。

以下は(背景画像の画像ロード完了) ≒ (画面に背景画像が映ったタイミング)として開発者コンソールにログを出すサンプルです(≒ としているのは画面にでるまで少しだけラグがあるためです)。 <a-sky>の src には<img>タグに付けた id を指定することができます。なので(<img>タグのロード完了イベント)=(<a-sky>のロード完了)と見なすことができるのです。<a-sky>は静止画背景の場合に用いるもので、動画を背景としたい場合には<a-videosphere>を用います。<a-videosphere>のsrcに<video>タグのidを指定することで、動画の場合にもロード完了イベントを検知することができます。

<body>
  <a-scene>
    <a-sphere clicktest class="collidable" position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
    <img id="image01" src="./sample.jpg" />
    <a-sky src="#image01" radius="30"></a-sky>
    <a-camera position="0 0 0">
      <a-cursor cursor="rayOrigin: mouse" visible="true" raycaster="objects: .collidable"></a-cursor>
    </a-camera>
  </a-scene>
</body>

<script>
  var imgElement = document.getElementById("image01");
  imgElement.addEventListener("load", function () {
    console.log("loaded!");
  });
</script>

コードを詳しく見ていきます。<img>タグに id を設定して、その id を<a-sky>の src として設定しています。<a-sky>の src は今回のような id 以外にファイルパスも指定できます。<script> タグでは JavaScript おなじみの方法で DOM 要素にイベントを設定しています。addEventListener の第1引数"load"は DOM 要素が発生させるイベントを指定します。<video>の場合だと"canplay"や"loadeddata"でロード完了イベントを設定できるので試してみてください。

日本語の文字列を表示する

A-Frame は標準では日本語表示に対応していません。でも標準では、というだけで方法はちゃんと用意されています。ずばり、Multi-channel Signed Distance Field(以降、msdf)という画像データを用いた表示方法です。この msdf は有志により作成された msdf ジェネレーターを用いることで自由に生成することができます。

まず、npm で公開されている msdf ジェネレーターをインストールします。Node.js をインストールして npm が利用できる環境を用意してください。用意できたら以下ページを参考に msdf ジェネレーターをインストールします。

msdf-bmfont-xml

npm install msdf-bmfont-xml -g

msdf ジェネレーターのインストールが完了したら 2 つのファイルを用意します。1 つは生成対象の文字列が書かれたテキストファイル、もう 1 つは msdf な画像生成の元になるフォントファイル(ttf 形式)です。フォントファイルは ttf 形式のみ利用可能で ttc 形式のフォントファイルは利用できません。この ttc 形式とは ttf 形式のフォントファイルの集合体のことです。ttc から 特定の ttf を抽出することもできるので目的に応じた ttf 形式のフォントファイルを用意してください。

次に生成対象の文字列が書かれたテキストファイルです。今回の例では charset.txt というファイル名で用意します。中身は以下のような文字列が羅列されたテキストファイルです。ここで記載したすべての文字が画像データとして出力され、後述の<a-text>で指定した文字を画面に表示することができるようになります。このため、charset.txt に含めなかった文字は、<a-text>で指定しても表示されないことに注意してください。テキストファイルの文字コードは UTF-8N、改行コードは LF である点に注意してください。日本語に完全に対応しようとすると漢字を大量に記載する必要があるので、第1水準、第2水準漢字などを目的に応じて記載してください。日常的に使う第1水準漢字(3000 文字弱)で日本語は記述できる、とされています。

あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん
がぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽゃゅょっ
アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン
ガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポァィゥェォャュョッ
アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨワヲン
ガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポァィゥェォャュョッ
1234567890
1234567890
!"#$%&'()=~{`+*}_?><-^\[@:];./\,
!”#$%&’()=~|‘{}*+_?><・。、」:;「@¥^ー
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrsuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
亜唖娃阿…
(以下略)

2 つのファイルが用意できたら以下のコマンドで msdf を生成します。ちょっとした注意点として ttf ファイルは実行ユーザーがアクセス権限を持つファイルパスを指定してください。Windows 標準のフォントファイル配置場所はユーザー権限によってはアクセスできません。環境と生成する文字数にもよりますが、私の環境(CPU Intel(R) Core(TM) i7-7700T + メモリ 16GB)では約 3000 文字の生成が 1 ~ 2 分程度で完了しました。

msdf-bmfont -s 42 -f json -t msdf -m 2560,2560 -i .\charset.txt c:\Users\public\xxxxxx.ttf

コマンドが成功すると、xxxxxx.png と xxxxxx.json という 2 つのファイルが生成されます。xxxxxx.png は charset.txt に記載された文字が変換された画像データです。xxxxxx.json は画像中のどの座標がどの文字に対応しているかが書かれています。以下の画像は xxxxxx.png の一部を抜粋したものです。白い丸内がカタカナの"サ"に対応した画像データです。

xxxxxx.json のカタカナの"サ"に対応した情報は以下のようになっています。x と y に xxxxxx.png 中のどの座標が"サ"のデータか記載されています。

    "id": 12469,
    "index": 2927,
    "char": "サ",
    "width": 40,
    "height": 38,
    "xoffset": 1,
    "yoffset": 5,
    "xadvance": 42,
    "chnl": 15,
    "x": 1903,
    "y": 1913,

msdf 形式の画像出力時に、フォントファイルの中に変換したい文字がないことがあります(特殊な記号や半角文字、常用外漢字など)。その場合は変換実行時のエラー表示で確認することができます。以下は生成元の ttf ファイルに半角カタカナがないために表示された例です。

Warning: no bitmap for character 'ア' (65393), adding to font as empty
Warning: no bitmap for character 'イ' (65394), adding to font as empty
Warning: no bitmap for character 'エ' (65396), adding to font as empty
Warning: no bitmap for character 'ウ' (65395), adding to font as empty
Warning: no bitmap for character 'ス' (65405), adding to font as empty

あとは生成されたデータを<a-text>のソースに指定することで日本語が表示できるようになります(該当箇所のみ抜粋)。value に指定された文字について xxxxxx.json の情報をもとに、xxxxxx.png のどの座標に描かれているかを判断して画面中に描画することで文字列が表示されます。

<a-text
  id="text"
  rotation="0 0 0"
  position="0 0 -1"
  value="サンプルテキスト"
  font="./xxxxxx.json"
  font-image="./xxxxxx.png"
  negate="false"
  color="white"
  align="center">
</a-text>

まとめ

A-Frame の簡単な使用例について紹介しました。HMD は今後もどんどん普及していきそうですが、値段を考えるとまだ少し時間がかかりそうにも感じます。臨場感こそないものの、Web ブラウザーではすぐに、手軽に"VR"を感じることができます。今回紹介した A-Frame は特殊な環境を用意することなく、Web ブラウザーとテキストエディターさえあれば簡単に VR 空間を自作することができます。この記事がみなさんの VR 体験の一助になれば幸いです。

参考サイト

https://aframe.io/


TOP
アルファロゴ 株式会社アルファシステムズは、ITサービス事業を展開しています。このブログでは、技術的な取り組みを紹介しています。X(旧Twitter)で更新通知をしています。