Away3Dはオープンソースのリアルタイム3Dエンジンだ。もともとはFlashプラットフォーム向けに開発された。そのAway3Dエンジンが「Away3D TypeScript」というJavaScriptライブラリに移植されはじめている。現在アルファリリースが公開されている。本連載では、このAway3D TypeScriptによる3次元表現を解説していきたい。
その初めてのお題は、つぎのサンプル1のような床に置いたボールだ[1]。ドラッグすると、カメラ位置を回すことができる。今回を含めた都合3回で仕上げたい。
Away3D TypeScriptライブラリを使う
まずは、ライブラリのダウンロードと読込みから説明しよう。「Away3D TypeScript」のサイトには、作例やソースファイルのリンクなどがある。最新のライブラリは、「Source Files」の欄に示された「GitHub: AwayJS Core TypeScript」と「GitHub: StageGL Core TypeScript」および「GitHub: StageGL Extentions」の3つのリンクからダウンロードすすればよい(図1)。また、サンプルコードとして「GitHub: StageGL Examples」も掲げられている。
サンプルコードなどAway3D TypeScriptを試してみるには、ソースファイルの3つのリンクからダウンロードしたファイルの中の3つのライブラリ「awayjs-core.next.min.js」と「stagegl-core.next.min.js」および「stagegl-extensions.next.min.js」を使う(図2)。これらのJavaScript(JS)ファイルを、これから書くコードのライブラリ用に定めたフォルダ(本連載では「lib」とする)に配置する。
HTMLドキュメントには、まずscript要素にAway3Dの3つのJSファイルawayjs-core.next.min.jsとstagegl-core.next.min.jsおよびstagegl-extensions.next.min.jsを読み込む。つぎに、JavaScriptコードには初めに呼び出す関数(initialize())を設け、body要素のonload属性にその呼出しを加える。これで準備が整った。Away3Dのコードを書いていこう。
Away3Dの3次元表現で用意しなければならないのは、つぎの3つだ。第1に、舞台となる3次元空間をつくる。第2に、その舞台に照明を備える。そして第3に、役者となる物体を舞台に登場させる。項を改めて、順に説明しよう。
- Away3Dで用意しなければならないもの
- 舞台:Viewオブジェクトで3次元空間の表示領域を定める
- 照明:光源のオブジェクトで3次元空間を照らす
- 役者:物体のひながたからインスタンスをつくって3次元空間に加える
Viewクラスで3次元空間の表示領域を定める
第1につくらなければならないのは、3次元空間の表示領域を定めるViewオブジェクトだ。3次元の座標空間や、それを表示領域に映すカメラもオブジェクトの中にもつ。View()コンストラクタの引数はレンダラだ。ここでは、DefaultRendererオブジェクトを渡すことにする。
Viewオブジェクトには、プロパティで設定が加えられる(表1)。もっとも、プロパティの数は多いものの、ほとんどはデフォルト値が定められている。今回は、ごく基本的なプロパティのみ定めることにしよう。
表1 Viewクラスに備わる基本的なプロパティ
Viewクラスのプロパティ | プロパティの値 |
width height | 表示される領域の幅と高さ。 |
backgroundColor | 画面の背景色。デフォルト値は黒(0x000000)。 |
camera | 表示領域を描くために用いられるCamera3Dオブジェクト。 |
scene | 表示領域を描くもととなる3次元空間のSceneオブジェクト。 |
Viewオブジェクトをつくって返す関数(createView())は新たに設ける。引数には、表示領域の幅と高さ、および背景色を渡す。
関数(createView())の中身は、つぎの抜書きのように定めた。そして、初期設定の関数(initialize())から呼び出す。できあがりのJavaScriptは、後にコード1としてまとめてある。
DirectionalLightクラスで平行光源を定める
第2に、光源を定める。3次元空間をつくっても、光がなければ何も見えない。神と同じく「光りあれ」と、光をつくらなければならないのだ。3次元表現で使われる光源はいくつかある。その中でも基本となるのは「平行光源」(directional light)だ。太陽の光のように同じ向き(平行)に進み、距離によって強さが変わらない(図3右上)。
平行光源は、DirectionalLight()コンストラクタで定める。引数はなくて構わない。この光で、後からつくる物体の表面を照らす。
もっとも、太陽光だけでは、月のように光の当たっていない部分は暗闇になってしまう。地球上では「環境光」(ambient light)があることで、陰にも光が及ぶ。そこで、DirectionalLightオブジェクトをつくって返す関数(createDirectionalLight())は、環境光の強さとその光の色を引数に与えてつぎのように定める。
DirectionalLightクラスは、LightBaseを継承する(表2)。そして、DirectionalLightオブジェクトのLightBase.ambientプロパティに強さの数値を与えれば、環境光が加わる。また、LightBase.colorプロパティで光の色を定める。さらに、DirectionalLight.directionプロパティには、Vector3Dオブジェクトの3次元ベクトルで光源の位置を与える。
表2 DirectionalLightおよびLightBaseクラスに備わる基本的なプロパティ
DirectionalLightオブジェクトのプロパティ | プロパティの値 |
ambient | 環境光の強さを示す0以上1以下の数値。デフォルト値は0。 |
color | 光のカラー値。デフォルト値は白(0xFFFFFF)。 |
direction | 光源の位置を示す3次元ベクトルのVector3Dオブジェクト。デフォルト値は(0, -1, 1)。 |
DirectionalLightオブジェクトをつくる関数(createDirectionalLight())は、インスタンスをつくった後、LightBase.ambientおよびLightBase.colorプロパティに引数値を与えて返した。DirectionalLight.directionプロパティはデフォルト値のまま用いる。
PrimitiveSpherePrefabクラスから球体のオブジェクトをつくって3次元空間に加える
舞台と照明が整ったので、ようやく第3の、役者となる物体のインスタンスを登場させる。そのためには、まずPrefabBaseのサブクラスから物体のひながた(プレハブ)をつくる。そして、そのひながたの参照に対してPrefabBase.getNewObject()メソッドを呼び出すと、物体のDisplayObjectインスタンスが得られる。
今回は球体をつくるので、ひながたにはPrimitiveSpherePrefabクラスを用いる。コンストラクタの引数は、球の半径、および曲面の滑らかさを決める水平と垂直の分割数だ。デフォルト値は、半径が50、分割数は水平16で垂直12だ。続けて、PrefabBase.getNewObject()メソッドは引数なしに呼び出せる。
ひながたからPrefabBase.getNewObject()メソッドでつくる物体は、DisplayObjectのサブクラスのMeshオブジェクトだ。その表面素材となるテクスチャをMesh.materialプロパティに定める。今回は、DefaultMaterialManager.getDefaultTexture()で得られるデフォルトのテクスチャを使った。また、表面素材にはそれを照らす光源を、MaterialBase.lightPickerプロパティにStaticLightPickerオブジェクトで与える。StaticLightPicker()コンストラクタに渡す引数は、光源の配列だ。
球体のMeshオブジェクトをつくって返す関数(createSphere())は、以下のように定めた。引数は球の半径(radius)、および水平と垂直の分割数(segmentsHとsegmentsV)、そして素材を照らす光源(light)だ。この関数を初期設定の関数(initialize())から呼出した。返される球体のMeshオブジェクトは、View.sceneプロパティで得られる3次元空間のSceneオブジェクト(前掲表1)にScene.addChild()メソッドで加える。
ここまで解説したJavaScriptコードをまとめれば、3次元空間に球体をひとつ置くにはあと一歩だ。何が足りないのかというと、このまま試したのでは物体が表れない。Away3D TypeScriptは内部的につくったCanvasに描くため、必ず描画のメソッドView.render()を呼び出さなければならないのだ。
View.render()メソッドの呼出しを加えてまとめたのが以下のコード1になる。これで、3次元空間に球体がひとつ描かれる(図4)。注意したいのは、コード1がView.render()メソッドを2回呼び出していることだ。これは誤りではない。なぜか1回の呼出しだけでは、物体が描かれないために加えてある[2]。確認用に、コード1をjsdo.itにもサンプル2として掲げた[3]。
球体以外の基本的なかたちをつくる
Away3Dでは球体以外にも、基本的なかたちがつくれるひながたのクラスはいろいろある。3つほど紹介して、今回は締めよう。つぎの表3は、平面と直方体およびドーナッツ型をつくるひながたのクラスのコンストラクタだ。引数として渡す幾何学的な数値に違いがある。だが、ひながたオブジェクトにPrefabBase.getNewObject()メソッドを呼出してMeshオブジェクトが得られることは変わらない。つまり、コンストラクタを差し替えるだけで、これらのかたちの物体がつくれる。
表3 平面・直方体・ドーナッツ型のひながたをつくる
ひながたをつくるコンストラクタ | 引数の値 |
PrimitivePlanePrefab (幅, 高さ) | 平面のひながたをつくる。 幅 ー 平面のx軸方向の長さ。デフォルト値は100。
高さ ー 平面のz軸方向の奥行き。デフォルト値は100。 |
PrimitiveCubePrefab (幅, 高さ, 奥行き) | 直方体のひながたをつくる。 幅 ー 直方体のx軸方向の長さ。デフォルト値は100。 高さ ー 直方体のy軸方向の長さ。デフォルト値は100。 奥行き ー 直方体のz軸方向の長さ。デフォルト値は100。 |
PrimitiveTorusPrefab (半径, 太さの半径, 水平分割数, 垂直分割数) | ドーナッツ型のひながたをつくる。
半径 ー ドーナッツ型の大きさを示す半径。デフォルト値は50。
太さの半径 ー ドーナッツ型の太さを示す半径。デフォルト値は50。
水平分割数 ー 曲面の水平方向の分割数。デフォルト値は16。
垂直分割数 ー 曲面の垂直方向の分割数。デフォルト値は8。 |
ひとつ練習として、前掲コード1の球体をつくる関数(createSphere())を下敷きにして、ドーナッツ型をつくる関数に書き替えてみよう。この関数(createTorus())はつぎのように呼び出して、3次元空間にドーナッツ型を加えたい。呼出す関数を差し替えるだけで、前掲コード1の仕組みは変えない。
前述のとおり、基本的にはコンストラクタと引数だけ、つぎのように書き替えればよい。物体の変数名(torus)は関数名(createTorus())に合わせて変えただけだ。もうひとつ、かたちを真横から見たのでは、ドーナッツ型かどうかわからない。そこで、DisplayObject.rotationXプロパティにより角度を少し傾けた(前掲図5参照)。
さて次回は、3次元空間に加えた球体の表面にビットマップ、いわゆるテクスチャを貼ってみよう。そして、物体に回転のアニメーションを加えるつもりだ。