three.jsを用いた開発
three.js (opens new window)のうえに作られたフレームワークなので, A-Frameはthree.jsのAPIにフルアクセスできます。 ここでは基盤となるthree.jsのシーンやオブジェクト、そしてA=frameの下にあるAPIにアクセスする方法について説明します。
# A-Frameとthree.jsのシーングラフとの関係性
- A-Frameの
<a-scene>
はthree.jsのシーンと一致します。 - A-Frameの
<a-entity>
はthree.jsのオブジェクトと一致します。 - three.jsのオブジェクトは
.el
を通じてA-Frameのエンティティを参照することができます。これはA-Frameによってセットされます。
# 親子関係
A-Frame のエンティティが親子関係でネストされている場合、three.jsのエンティティもネストされます。 例えば、このA-Frameのシーンを見てみましょう。
<a-scene>
<a-box>
<a-sphere></a-sphere>
<a-light></a-light>
</a-box>
</a-scene>
three.jsのシーングラフは応答し、次のようになります。
THREE.Scene
THREE.Mesh
THREE.Mesh
THREE.Light
# three.js API にアクセスする
three.jsは ウインドウ内でグローバルオブジェクトとして利用できます。
console.log(THREE);
# three.jsのオブジェクトと一緒に動作させる
A-Frameはthree.jsの上で抽象化されていますが、three.jsの下で操作することが可能です。 A-Frameのエレメントはthree.jsのシーングラフに繋がるドアを持っています。
# three.jsのシーンにアクセスする
three.jsのシーン (opens new window)は.object3D
として <a-scene>
エレメントからアクセスすることが可能です。
document.querySelector('a-scene').object3D; // THREE.Scene
A-Frameのエンティティは全て.sceneEl
を通じて<a-scene>
を参照することができます。
document.querySelector('a-entity').sceneEl.object3D; // THREE.Scene
コンポーネント から、そのエンティティを通じてシーンにアクセスすることができます。
(this.el
):
AFRAME.registerComponent('foo', {
init: function () {
var scene = this.el.sceneEl.object3D; // THREE.Scene
}
});
# エンティティのthree.jsオブジェクトにアクセスする
A-Frameのエンティティ(例えば <a-entity>
)は全てTHREE.Object3D
(opens new window)を持っています。
もっと説明すると、 異なるタイプのObject3D
を内包するTHREE.Group
(opens new window)です。
根底となるエンティティのTHREE.Group
には.object3D
を通じてアクセスします。
document.querySelector('a-entity').object3D; // THREE.Group
エンティティは、複数のタイプの Object3D
から構成することができます。
例えばエンティティは、THREE.Mesh
とTHREE.Light
の両方を持つことで、ジオメトリコンポーネントとライトコンポーネント両方の性質を持つことができます。
<a-entity geometry light></a-entity>
Components add the mesh and light under the entity's root THREE.Group
.
References to the mesh and light are stored as different types of three.js
objects in the entity's .object3DMap
.
コンポーネントは、そのエンティティのルートである THREE.Group
の下に、メッシュとライトを追加します。
メッシュとライトへの参照は、エンティティの .object3DMap
オブジェクト内の、異なるタイプのthree.jsとして格納されます。
console.log(entityEl.object3DMap);
// {mesh: THREE.Mesh, light: THREE.Light}
But we can access them through the entity's .getObject3D(name)
method:
しかし、エンティティの .getObject3D(name)
メソッドを使うことでアクセスすることができます。
entityEl.getObject3D('mesh'); // THREE.Mesh
entityEl.getObject3D('light'); // THREE.Light
どのようにしてthree.jsオブジェクトが最初にセットされるのかを見てみましょう。
# Object3D
をエンティティの上に配置する。
Setting an Object3D
on an entity adds the Object3D
to the entity's Group
,
which makes the newly set Object3D
part of the three.js scene. We set the
Object3D
with the entity's .setObject3D(name)
method where the name
denotes the Object3D
s purpose.
For example, to set a point light from within a component:
エンティティに Object3D
を設定すると、その Object3D
がエンティティの Group
に追加されます。
これにより、新しく設定された Object3D
は three.js のシーンの一部となります。
ここではオブジェクト3D
をはエンティティの .setObject3D(name)
メソッドで指定します。
例えば、コンポーネントの中から点光源を設定してみます。
AFRAME.registerComponent('pointlight', {
init: function () {
this.el.setObject3D('light', new THREE.PointLight());
}
});
// <a-entity light></a-entity>
光源にlight
という名前をセットしました。後でアクセスししたときには先に書いたエンティティの.getObject3D(name)
メソッドを使うことができます
entityEl.getObject3D('light');
また、A-Frameのエンティティにthree.jsオブジェクトを設定すると、A-Frameは.el
を介してそのエンティティに、 three.js オブジェクトから A-Frame エンティティへの参照を提供します。
entityEl.getObject3D('light').el; // entityEl
# エンティティからObject3D
を削除する
エンティティからObject3D
を削除し、three.jsから削除するには、.removeObject3D(name)
を使うことができます。
光源のサンプルに戻ると、コンポーネントが外されると光源を削除することができます。
AFRAME.registerComponent('pointlight', {
init: function () {
this.el.setObject3D('light', new THREE.PointLight());
},
remove: function () {
// Remove Object3D.
this.el.removeObject3D('light');
}
});
# 作成した空間同志を変形させる
すべてのオブジェクトとシーン(世界)一般は、それぞれ独自の座標空間を持っています。 親オブジェクトの位置や回転スケールの変化は子オブジェクトの位置や回転やスケールにも適用されます。
こんなシーンを考えてみましょう。
<a-entity id="foo" position="1 2 3">
<a-entity id="bar" position="2 3 4"></a-entity>
</a-entity>
ワールドの基準点から、fooの位置は(1,2,3)であり、fooの変換はbarの変換の上に適用されるためbarの位置は(3, 5, 7)になります。 fooの基準点からは、foo の位置は (0, 0, 0)であり、bar の位置は (2, 3, 4)となります。 これらの基準点と座標の間で変換を行いたいことがよく起こります。 上記は簡単な例ですが、次のような操作をしたい場合があります。 barの位置に対してワールドスペースでの座標を求めたり、任意のの座標をfooの座標空間へ移動させるというようなことです。 3Dプログラミングでは、これらの演算はマトリクスを使って実現しますが、three.jsはそれを簡単にするためのヘルパーを提供します。
# Local to World Transforms
Normally, we'd need to call .updateMatrixWorld ()
on parent Object3D
s, but
three.js defaults Object3D.matrixAutoUpdate
to true
. We can use three.js's
.getWorldPosition (vector)
and .getWorldQuaternion (quaternion)
.
Object3D
の世界座標を入手するには
var worldPosition = new THREE.Vector3();
entityEl.object3D.getWorldPosition(worldPosition);
Object3D
の世界の回転角を入手するには
var worldQuaternion = new THREE.Quaternion();
entityEl.object3D.getWorldQuaternion(worldQuaternion);
three.jsの Object3D
は ローカル座標をワールド座標に変換する関数 (opens new window):を持っています。
.localToWorld (vector)
.getWorldDirection (vector)
.getWorldQuaternion (quaternion)
.getWorldScale (vector)
# 世界からローカルへの変換
世界空間からオブジェクトの局所空間への変換行列を得るには、オブジェクトのワールド行列の逆行列を行います。
var worldToLocal = new THREE.Matrix4().getInverse(object3D.matrixWorld)
これで変換したいworldToLocal
の行列を適用できます。
anotherObject3D.applyMatrix(worldToLocal);