まずは基本的なA-Frameのシーンを作ることから始めましょう。このためには、HTMLの基本的な理解が必要です。ここでは、次のことを学びます。

  • プリミティブで3Dエンティティ(つまり、オブジェクト)を追加する。
  • 3D空間のエンティティを位置、回転、スケールで変形させるトランスフォーム
  • 環境の追加
  • テクスチャーの追加
  • アニメーションとイベントによる基本的なインタラクティブ機能の追加
  • テキストを追加する

Glitchで基本的なシーンをRemixする (opens new window).

# HTMLの作成

まずは最小限のHTML構造から始めます。

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

A-Frameをscriptタグとして<head>に記述して、CDNでホストされているA-Frameのビルドを指すようにします。これは、A-FrameがカスタムHTML要素を登録するためで、<a-scene>と打ち込む前に定義しておかないと、<a-scene>は何もしません。

次に、<a-scene><body> 内に記述します。<a-scene> にはシーン内のすべてのエンティティが含まれます。WebGL の設定、キャンバス、カメラ、ライト、レンダラー、レンダリング ループ、および HTC Vive、Oculus Rift、Samsung GearVR、スマートフォン(Google Cardboard)などのプラットフォームですぐに使える WebVR サポートなど、3D に必要なすべての設定を <a-scene> が処理します。<a-scene> だけで、私たちの負担はかなり軽減されます。

# エンティティを追加する

<a-scene>の中では、A-Frameの標準プリミティブの一つである<a-box>を使って3Dエンティティを貼り付けます。<a-box>は通常のHTML要素と同様に使用でき、タグを定義し、HTML属性を使ってカスタマイズすることができます。A-Frameに付属するプリミティブの他の例としては、<a-cylinder><a-plane><a-sphere>などがあります。

ここでは、<a-box>の色を定義しています。その他の属性(例:幅、高さ、深さ)については、<a-box>のドキュメントを参照してください。

boximage Image by Ruben Mueller from vrjump.de

<a-scene>
  <a-box color="red"></a-box>
</a-scene>

余談ですが、プリミティブとは、A-Frameの使いやすいHTML要素で、基本となるエンティティ・コンポーネントの集合体を包むものです。便利なものですが、 <a-box>の下には、ジオメトリgeometryとマテリアルmaterialのコンポーネントを持つ<a-entity>があります。

<a-entity id="box" geometry="primitive: box" material="color: red"></a-entity>

しかし、デフォルトのカメラとボックスは0 0 0地点に配置されているため、ボックスを動かさない限り、そのボックスを見ることはできません。 これを行うには、位置コンポーネントを使用して、3D 空間でボックスを変換します。

# 3Dでのエンティティの変形

まず、3次元空間について説明します。A-Frameは右手座標系を採用しています。デフォルトのカメラ方向では、正のX軸は右に、正のY軸は上に、正のZ軸は画面の外側からこちらに向かって伸びています。

righthandimage Image from what-when-how.com

A-Frameの距離の単位がメートルなのは、WebVR API (opens new window)がポーズデータをメートル単位で返すからです。 VR 用にシーンをデザインする場合、作成するエンティティの現実世界でのスケールを考慮することが重要です。height="10" のボックスは、 コンピュータの画面では普通に見えますが、VR では巨大に見えるでしょう。

A-Frameの回転単位はですが、three.jsに渡すと内部でラジアンに変換されます。回転の正方向を決めるには、右手の法則 (opens new window)を使います。親指を正の軸の方向に向け、指を丸めた方向が回転の正の方向です。

ボックスの平行移動回転拡大縮小を行うには、位置、回転、拡大の各要素を変更します。まず、回転とスケールの部品を適用してみましょう。

<a-scene>
  <a-box color="red" rotation="0 45 45" scale="2 2 2"></a-box>
</a-scene>

ボックスが斜めに回転し、サイズが2倍になります。

# 親と子のトランスフォーム

A-Frame HTMLは、3Dのシーングラフを表現しています。シーングラフでは、エンティティは1つの親と複数の子を持つことができます。子エンティティは、親エンティティから変換(位置、回転、スケール)を継承します。

例えば、ボックスの子として球を持つことができます。

<a-scene>
  <a-box position="0 2 0" rotation="0 45 45" scale="2 4 2">
    <a-sphere position="1 0 3"></a-sphere>
  </a-box>
</a-scene>

球体のワールドポジションを計算すると、*1 2 3* となり、球体の親のポジションと球体自身のポジションを合成することで達成される。同様に、回転とスケールについても、球体はボックスの回転とスケールを継承する。球体も親ボックスと同じように回転、伸縮します。Box が位置、回転、スケールを変更すると、即座に球体に適用され、影響を受けます。

球体の子要素として円柱を追加すると、円柱のトランスフォームは球体とボックスの両方のトランスフォームの影響を受けることになります。 three.js の内部では、これは変換行列を掛け合わせることで行われます。幸いなことに、私たちはそのようなことを考える必要はありません。

# 箱をカメラの前に配置する

では、最初からボックスがカメラに見えるように戻しましょう。位置コンポーネントを使用して、負の Z 軸上でボックスを 5 メートル後方に移動できます。また、ボックスをスケーリングし、スケーリングは中心から行われるため、ボックスが地面と交差しないように、正の Y 軸で 2 メートル上に移動させる必要があります。

<a-scene>
  <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>
</a-scene>

これで箱を見ることができました!

boximage2

# 初期のコントロール

平面ディスプレイ(ノートパソコンやデスクトップ)の場合、デフォルトの操作方式では、マウスのクリック・ドラッグで周囲を見渡し、WASDキーや矢印キーで移動することができます。モバイルの場合は、携帯電話をパンすることでカメラを回転させることができます。A-FrameはWebVR向けに作られていますが、このデフォルトの操作体系により、ヘッドセット無しでもシーンを見ることができます。

webvrimage

Web VRの世界を設定し、入るために Mozilla VR ホームページを御覧ください (opens new window).

VRヘッドセット(Oculus Rift、HTC Viveなど)を接続した状態でゴーグルのアイコンをクリックしてVRの世界に入る (opens new window)と、没入感のあるVRでシーンを体験することができる。ルームスケールが可能であれば、物理的にそのシーンの周りを歩くことも可能です

# 環境を追加する

A-Frameは、開発者が再利用可能なコンポーネントを作成し、他の人が簡単に使えるように共有することができます。@feiss環境コンポーネント (opens new window)は、1行のHTMLで様々な環境全体をひとまとまりに生成してくれます。環境コンポーネントは、VRアプリケーションを視覚的に起動するための簡単で素晴らしい方法で、多数のパラメータを持つ十数個の環境を提供してくれます。

まず、A-Frameの後にscriptタグを使用して環境コンポーネントをインクルードします。

<head>
  <script2 src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  <script2 src="https://unpkg.com/aframe-environment-component/dist/aframe-environment-component.min.js"></script>
</head>

そして、シーン内に、環境コンポーネントを付けたエンティティを追加します。プリセット(例:森)を指定し、他の多くのパラメータ(例:200本の木)とともに指定できます。

<a-scene>
  <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>

  <!-- Out of the box environment! -->
  <a-entity environment="preset: forest; dressingAmount: 500"></a-entity>
</a-scene>

# Applying an Image Texture

テクスチャを適切にロードするためにローカルサーバーを使ってHTMLを用意してください

通常の<img>要素と同じように、src属性を使って画像、動画、<canvas>で画像テクスチャをボックスに適用することができるのです。また、設定したcolor="red"を削除して、テクスチャに色が混ざらないようにしましょう。デフォルトのマテリアルカラーは白なので、color属性を削除すれば十分です。

<a-scene>
  <a-box src="https://i.imgur.com/mYmmbrp.jpg" position="0 2 -5" rotation="0 45 45"
    scale="2 2 2"></a-box>

  <a-sky color="#222"></a-sky>
</a-scene>

これでテクスチャが適切についた箱を見ることができるはずです。

boxtextureimage

# アセット管理システムを使う

パフォーマンスを考えると、アセット管理システムを使用することをお勧めします。アセット管理システムを使うと、ブラウザが資産(画像、動画、モデルなど)をキャッシュしやすくなり、A-Frameはレンダリング前にすべての資産を取得するようにします。

アセット管理スステムの中で<img>を定義すれば、three.jsは内部で<img>を生成する必要がありません。 <img>を自分たちで生成するのはより多くのコントロールを与え、復数のエンティティに対してテクスチャを流用することを可能にします。 A-Frameは、必要に応じてクロスオリジンなどを設定することも可能なスマートさを持っています。

画像テクスチャの資産管理システムを利用するためには

  • <a-asset>をシーンに追加します
  • <a-assets>の下に<img>をテクスチャとして定義します
  • <img>にHTMLのIDを付与する(例 id="boxTexture")
  • DOMセレクターの書式の中で、IDを用いてアセットを参照する(src="#boxTexture")
<a-scene>
  <a-assets>
    <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg">
  </a-assets>

  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>

  <a-sky color="#222"></a-sky>
</a-scene>

# 独自に環境を構築する

Previously we had the environment component generate the environment. Though it's good to know a bit on creating a basic environment for learning purposes.

前の項では、環境コンポーネントが環境を生成してくれました。しかし、学習のために基本的な環境の作成について少し知っておくのは良いことです。

# シーンに背景を追加する

シーンを取り囲む背景を<a-sky>で追加することができます。<a-sky>は大きな球体の内側に適用されるマテリアルで、フラットカラー、360°画像、360°動画のいずれかを使用することができます。例えば、濃いグレーの背景なら

<a-scene>
  <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>

  <a-sky color="#222"></a-sky>
</a-scene>

もしくはテクスチャ画像を360度の背景として使うこともできます。

<a-scene>
  <a-assets>
    <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg">
    <img id="skyTexture"
      src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg">
  </a-assets>

  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>

  <a-sky src="#skyTexture"></a-sky>
</a-scene>

# 地面を追加する

地面を追加するには、<a-plane>を使用します。デフォルトでは、平面はXY軸に平行に配向しています。地面と平行にするには、XZ軸に沿った向きにする必要があります。これは、平面をX軸上で90度回転させることで可能です。

<a-plane rotation="-90 0 0"></a-plane>

We'll want the ground to be large, so we can increase the width and height. Let's make it 30-meters in each direction: 地面を大きく確保したいので、幅と高さを大きくしてみます。各方向30mにしてみます。

<a-plane rotation="-90 0 0" width="30" height="30"></a-plane>

そしてテクスチャを適用します。

  <a-assets>
    <!-- ... -->
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
    <!-- ... -->
  </a-assets>

  <!-- ... -->
  <a-plane src="#groundTexture" rotation="-90 0 0" width="30" height="30"></a-plane>
  <!-- ... -->

テクスチャを繰り返して貼るためにrepeat属性を使うことができます。X方向に何回繰り返すか、Y方向に何回繰り返すか(テクスチャでは一般にUとVと呼ばれます)の2つの数値を使います。

<a-plane src="#groundTexture" rotation="-90 0 0" width="30" height="30"
  repeat="10 10"></a-plane>

# 照明の調整

<a-light>を使用することで、シーンの照明方法を変更することができます。デフォルトでは、何もライトを指定しないと、A-Frameは環境光と指向性光を追加します。もしA-Frameがライトを追加してくれなければ、シーンは真っ暗なままです。しかし、独自に照明を追加すると、デフォルトの照明設定は削除され、独自に設定した照明に置き換わります。

ここでは、空と同じ青緑色をしたアンビエントライトを追加します。アンビエントライトは、シーン内のすべてのエンティティに適用されます(少なくともデフォルトのマテリアルが適用されている場合)。

また、スポットライトも追加します。スポットライトは電球のようなもので、シーンの周りに配置することができ、エンティティに対するスポットライトの効果は、エンティティとの距離によって決まります。

<a-scene>
  <a-assets>
    <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg">
    <img id="skyTexture"
      src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg">
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
  </a-assets>

  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box>

  <a-sky src="#skyTexture"></a-sky>

  <a-light type="ambient" color="#445451"></a-light>
  <a-light type="point" intensity="2" position="2 4 4"></a-light>
</a-scene>

# アニメーションを追加する

A-Frameに内蔵されているアニメーションシステムを使って、ボックスにアニメーションを追加することができます。アニメーションは、時間の経過とともに値を補間したり、トゥイーンさせたりします。エンティティにアニメーションコンポーネントを設定することができます。Box を上下にホバーさせて、シーンに動きをつけてみましょう。

<a-scene>
  <a-assets>
    <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg">
  </a-assets>

  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"
         animation="property: object3D.position.y; to: 2.2; dir: alternate; dur: 2000; loop: true"></a-box>
</a-scene>

アニメーションコンポーネントで以下の指示を出しました。

  • エンティティobject3Dの位置のY軸をアニメートする。
  • 20センチメートル高い2.2までアニメートする。
  • アニメーションを繰り返すたびに、アニメーションの方向(dir)を交互に変えて、行ったり来たりさせる。
  • 各サイクルで2000ミリ秒のdur(継続時間)を設定する。
  • アニメーションをループさせ、永遠に繰り返す。

animationgif

# Adding Interaction

箱を見ると箱のサイズが大きくなり、箱を「クリック」すると箱が回転する、というインタラクションを追加してみましょう。

現在、多くの開発者はコントローラー付きの適切な VR ハードウェアを持っていないため、このセクションでは、内蔵のカーソル コンポーネントを使用して基本的なモバイルおよびデスクトップ入力を使用することに焦点を当てます。カーソルコンポーネントはデフォルトで、モバイルではエンティティを凝視したり見つめたりすることでエンティティを「クリック」する機能を提供し、デスクトップではエンティティを見てマウスをクリックすることでエンティティをクリックしたとみなします。 しかし、カーソルコンポーネントはインタラクションを追加するための一つの手段に過ぎず、実際のコントローラにアクセスできるようになれば、物事は大きく進展します。

可視化されているカーソルを[カメラ]に固定するためには、親子変換で説明したように、カーソルをカメラの子要素として配置します。

特にカメラを定義していないため、A-Frameにはデフォルトのカメラが含まれています。しかし、カメラの子としてカーソルを追加する必要があるため、<a-camera><a-cursor>を含むように定義する必要があります。

<a-scene>
  <a-assets>
    <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg">
  </a-assets>

  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"
         animation="property: object3D.position.y; to: 2.2; dir: alternate; dur: 2000; loop: true"></a-box>

  <a-camera>
    <a-cursor></a-cursor>
  </a-camera>
</a-scene>

<a-cursor> がラップしている[カーソルコンポーネントのドキュメント][cursor component] を確認すると、クリックだけでなく mouseentermouseleave などのホバーイベントを発生することがわかります。

# イベントリスナーコンポーネント(中級)

カーソルイベントを手動で処理する方法の1つは、通常のDOM要素と同じように、JavaScriptでイベントリスナーを追加する (opens new window) ことです。もし、あなたがJavaScriptに慣れていないなら、以下のイベントでのアニメーションまで読み飛ばしてもかまいません。

JavaScript では、querySelector (opens new window) でボックスを取得し、「addEventListener]addeventlistener (opens new window) を使用してから、setAttribute でボックスがホーバーされたときにスケールを大きくするようにしています。なお、A-Frameでは、setAttributeにマルチプロパティのコンポーネントを扱うための機能が追加されています。第2引数には、完全な {x, y, z} オブジェクトを渡すことができます。

<script>
  var boxEl = document.querySelector('a-box');
  boxEl.addEventListener('mouseenter', function () {
    boxEl.setAttribute('scale', {x: 2, y: 2, z: 2});
  });
</script>

しかし、もっと堅牢な方法は、このロジックをA-Frameコンポーネントにカプセル化することでしょう。この方法では、シーンの読み込みを待つ必要がなく、コンポーネントがコンテキストを与えてくれるため、クエリセレクタを実行する必要がありません。また、ページ上で制御不能なスクリプトを実行する代わりに、コンポーネントを再利用して設定することができます。さらに良い方法は、.setAttributeの呼び出しを省略して、this.el.object3D.scaleの値を直接設定するとパフォーマンスが向上します。

<script>
  AFRAME.registerComponent('scale-on-mouseenter', {
    schema: {
      to: {default: '2.5 2.5 2.5', type: 'vec3'}
    },

    init: function () {
      var data = this.data;
      var el = this.el;
      this.el.addEventListener('mouseenter', function () {
        el.object3D.scale.copy(data.to);
      });
    }
  });
</script>

このコンポーネントは、HTMLから直接ボックスに付与することができます。

<script>
  AFRAME.registerComponent('scale-on-mouseenter', {
    // ...
  });
</script>

<a-scene>
  <!-- ... -->
  <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"
         animation="property: object3D.position.y; to: 2.2; dir: alternate; dur: 2000; loop: true"
         scale-on-mouseenter></a-box>
  <!-- ... -->
</a-scene>

# イベントを利用したアニメーション

アニメーションコンポーネントには、エンティティがイベントを発信したときにアニメーションを開始する機能があります。これは、startEvents 属性にカンマで区切ったイベント名のリストを指定することで実現できます。

カーソルコンポーネントの mouseentermouseleave イベントでボックスのスケールを変更するアニメーションを2つ、クリック時にボックスをY軸周りに回転させるアニメーションを1つ追加することができます。エンティティは、属性名に __ を付けることで、複数のアニメーションを持つことができることに注意してください。

<a-box
  src="#boxTexture"
  position="0 2 -5"
  rotation="0 45 45"
  scale="2 2 2"
  animation__position="property: object3D.position.y; to: 2.2; dir: alternate; dur: 2000; loop: true"
  animation__mouseenter="property: scale; to: 2.3 2.3 2.3; dur: 300; startEvents: mouseenter"
  animation__mouseleave="property: scale; to: 2 2 2; dur: 300; startEvents: mouseleave"></a-box>

# 音声を追加する

VRに没入感や臨場感を与えるためには、音声が重要です。簡単なホワイトノイズを背景に加えるだけでも、大きな効果があります。 各シーンに音声を用意することをお勧めします。一つの方法は、HTMLに<audio>要素を追加して(できれば<a-assets>の下で)オーディオファイルを再生することです。

<a-scene>
  <a-assets>
    <audio src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay
      preload></audio>
  </a-assets>

  <!-- ... -->
</a-scene>

また、<a-sound>を使用して位置情報を持つ音声を追加することもできます。これにより、音は近づくと大きくなり、遠ざかると小さくなります。positionを使って、サウンドをシーンに配置することもできます。

<a-scene>
  <!-- ... -->
  <a-sound src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay="true"
    position="-3 1 -4"></a-sound>
  <!-- ... -->
</a-scene>

# テキストを追加する

A-Frameには、テキストコンポーネントが付属しています。テキストをレンダリングする方法はいくつかあり、それぞれにメリットとデメリットがあります。A-Frameには、比較的シャープでパフォーマンスの高いthree-bmfont-text (opens new window)を使ったSDFテキスト実装が付属しています。

<a-entity
  text="value: Hello, A-Frame!; color: #BBB"
  position="-0.9 0.2 -3"
  scale="1.5 1.5 1.5"></a-entity>

# さあ試してみましょう

基本的なサンプルをご覧いただけます。

GlitchでRemixしてみる (opens new window).

Glitchでサンプルを見る (opens new window).