サイトトップ

Director Flash 書籍 業務内容 プロフィール
 

HTML5 / Flash ActionScript講座
Webクリエイターのための
CreateJSスタイルブック
gihyo.jp連載
「HTML5のCanvasでつくる
ダイナミックな表現
―CreateJSを使う」
■Twitter: @FumioNonaka / Facebook Page: Away3D

Away3D Workshop
HTML5とJavaScriptではじめる3D表現

Away3D TypeScriptで立方体を回してみる

Away3D はオープンソースのリアルタイム3Dエンジンだ。もとはFlashプラットフォーム向けに開発された。そのAway3Dエンジンが、「 Away3D TypeScript 」としてJavaScriptライブラリへの移植が進んでおり、現在アルファリリースが公開されている。いよいよHTML5とJavaScriptを使って、本格的に3次元表現ができるようになる。そこで、3次元空間に描いた立方体を回してみるというお題で、Away3Dを使ったJavaScriptのコーディングについて解説する。

なお、このレジュメは2014年7月25日金曜日に催されたAway3D勉強会「HTML5とJavaScriptではじめる3D表現」用に書いた。勉強会のUSTREAMビデオが公開されたので、併せて掲げる。


[Vol. 1] Broadcast live streaming video on Ustream

[Vol. 2] Broadcast live streaming video on Ustream

[Vol. 3] Broadcast live streaming video on Ustream
 

01 Away3D TypeScriptライブラリを使う

Away3D TypeScript」サイトには、作例やソースファイルのリンクなどが掲げられている。最新のライブラリは、「Source Files」の欄に示されたGitHubのリンクからダウンロードできる(図001)。

図001■Away3D TypeScriptライブラリのソースファイル
図001

Away3D TypeScriptを試すには、ソースファイルのふたつのリンクからダウンロードしたファイルの中から3つのライブラリ「awayjs-core.next.min.js」と「stagegl-context.next.min.js」および「stagegl-renderer.next.min.js」を使う(図002)。

図002■Away3Dで使う3つのライブラリのJSファイル
図002

HTMLドキュメントには、まずscript要素にAway3Dの3つのJSファイルawayjs-core.next.min.jsとstagegl-context.next.min.jsおよびstagegl-renderer.next.min.jsを読込む。つぎに、JavaScriptコードには初めに呼出す関数(initialize())を設け、body要素のonload属性にその呼出しを加える。

<script src="lib/awayjs-core.next.min.js"></script>
<script src="lib/stagegl-context.next.min.js"></script>
<script src="lib/stagegl-renderer.next.min.js"></script>
<script>
function initialize() {
  // 初期設定
}
</script>


<body onload="initialize();">

Away3Dで3次元表現するには、つぎの3つを用意しなければならない。

    Away3Dで用意しなければならないもの
  1. 舞台   Viewオブジェクトで3次元空間の表示領域を定める
  2. 照明   光源のオブジェクトで3次元空間を照らす
  3. 役者   幾何情報と素材から物体のインスタンスをつくる

02 Viewクラスで3次元空間の表示領域を定める

第1につくらなければならないのは、3次元空間の表示領域を定めるViewオブジェクトだ。3次元の座標空間や、それを表示領域に映すカメラもオブジェクトの中にもつ。View()コンストラクタの引数にはレンダラを渡す。

new away.containers.View(new away.render.DefaultRenderer())

Viewオブジェクトには、プロパティで設定が加えられる(表001)。多くのプロパティには、デフォルト値が定められている。

表001■Viewクラスに備わる基本的なプロパティ
Viewクラスの
プロパティ
プロパティの値
width
height
表示される領域の幅と高さ。
backgroundColor 画面の背景色。デフォルト値は黒(0x000000)。
camera 表示領域を描くために用いられるCamera3Dオブジェクト。
scene 表示領域を描くもととなる3次元空間のScene3Dオブジェクト。

Viewオブジェクトをつくって返す関数(createView())を新たに定める(第16〜23行目)。引数に渡すのは、表示領域の幅と高さ、および背景色だ。そして、初期設定の関数(initialize())から呼出す(第10行目)。なお、抜書きの行番号は、後掲コード001にもとづく。

  1. var view;
  1. function initialize() {
  1.   view = createView(240, 180, 0x0);
  1. }
  2. function createView(width, height, backgroundColor) {
  3.   var defaultRenderer = new away.render.DefaultRenderer();
  4.   var view = new away.containers.View(defaultRenderer);
  5.   view.width = width;
  6.   view.height = height;
  7.   view.backgroundColor = backgroundColor;
  8.   return view;
  9. }

03 DirectionalLightクラスで平行光源を定める

第2に、光源を定める。3次元表現で使われる光源で基本となるのは「平行光源」(directional light)だ。太陽の光のように同じ向き(平行)に進み、距離によって強さが変わらない(図003右上)。

図003■3次元表現で使われる光源の種類
図003
*Tcpp's fileより引用

平行光源は、DirectionalLight()コンストラクタで定める。この光で、後からつくる物体の表面を照らす。

new away.entities.DirectionalLight()

地球上では「環境光」(ambient light)があることで、陰も見える。そこで、DirectionalLightオブジェクトをつくって返す関数(createDirectionalLight())は、環境光の強さとその光の色を引数に与えてつぎのように定めた。DirectionalLightオブジェクトのambientプロパティに強さの数値を与えると、環境光が加わる(第35行目)。また、colorプロパティで光の色を定める(第36行目)。なお、directionプロパティには、Vector3Dオブジェクトの3次元ベクトルで光源の位置を与える(第34行目)。

  1. function initialize() {
  2.   var directionalLight = createDirectionalLight(0.25, 0x00FFFF);
  1. }
  1. function createDirectionalLight(ambient, color) {
  2.   var light = new away.entities.DirectionalLight();
  3.   light.direction = new away.geom.Vector3D(0, -1, 1);
  4.   light.ambient = ambient;
  5.   light.color = color;
  6.   return light;
  7. }

04 PrimitiveCubePrefabクラスから立方体のオブジェクトをつくる

いよいよ第3の、役者となる物体のインスタンスを登場させる。そのためには、まず立方体のひながた(プレハブ)となるPrimitiveCubePrefabオブジェクトをつくる。コンストラクタに与える引数は立方体の幾何情報だ。つぎに、そのひながたの参照に対してgetNewObject()メソッドを呼出すと、物体のインスタンスが得られる。

new away.prefabs.PrimitiveCubePrefab(幅, 高さ, 奥行き).getNewObject()

表面素材は、デフォルトのテクスチャでつくることにする。静的メソッドDefaultMaterialManager.getDefaultTexture()で得られたオブジェクトを、TriangleMaterial()コンストラクタの引数に渡す。

new away.materials.TriangleMaterial(away.materials.DefaultMaterialManager.getDefaultTexture())

物体の表面を照らす光はStaticLightPickerオブジェクトで定め、素材のオブジェクトのlightPickerプロパティに加える。StaticLightPicker()コンストラクタには、光源のオブジェクトを配列に納めて渡す。

new away.materials.StaticLightPicker(光源の配列)

立方体のインスタンスをつくって返す関数(createCube())は、つぎのように初期設定の関数(initialize())から呼出して、できあがったインスタンスをScene3D.addChild()メソッドで3次元空間に加える(第11〜12行目)。引数には、立方体の幅と高さと奥行きに加えて、光源のオブジェクトを渡す。なお、3次元空間のScene3DオブジェクトはView.sceneプロパティで参照した(前掲表001)。関数は、PrimitiveCubePrefabオブジェクトから立方体のインスタンスをつくり、テクスチャと光源のオブジェクトを設定して返す(第24〜31行目)。

  1. var view;
  2. var mesh;
  1. function initialize() {
  2.   var directionalLight = createDirectionalLight(0.25, 0x00FFFF);
  3.   view = createView(240, 180, 0x0);
  4.   mesh = createCube(400, 400, 400, directionalLight);
  5.   view.scene.addChild(mesh);
  1. }
  1. function createCube(width, height, depth, light) {
  2.   var defaultTexture = away.materials.DefaultMaterialManager.getDefaultTexture();
  3.   var material = new away.materials.TriangleMaterial(defaultTexture);
  4.   var mesh = new away.prefabs.PrimitiveCubePrefab(width, height, depth).getNewObject();
  5.   mesh.material = material;
  6.   material.lightPicker = new away.materials.StaticLightPicker([light]);
  7.   return mesh;
  8. }

05 立方体のオブジェクトを水平および垂直の向きに回す

3次元空間を表示領域に描くには、必ずView.render()メソッドを呼出さなければならない。さらに、3次元空間の立方体を上下左右に回してみよう。アニメーションは、RequestAnimationFrameクラスで扱う。一定の時間間隔で行うアニメーションの処理を関数で定め、コンストラクタの引数として渡す。コールバックの呼出しは、RequestAnimationFrame.start()メソッドにより始まる。

new away.utils.RequestAnimationFrame(コールバック)

RequestAnimationFrameオブジェクト.start()

これらふたつのメソッドは、初期設定の関数(initialize())から呼出す(第13〜14行目)。RequestAnimationFrame()コンストラクタに渡したコールバック関数(rotate())は、立方体のプロパティrotationXrotationYに角度をそれぞれ1度ずつ加えて回した(第40〜41行目)。コールバックの最後には、View.render()メソッドを呼出す。

  1. var timer;
  2. function initialize() {
  1.   timer = new away.utils.RequestAnimationFrame(rotate);
  2.   timer.start();
  3. }
  1. function rotate(timeStamp) {
  2.   mesh.rotationX = (mesh.rotationX + 1) % 360;
  3.   mesh.rotationY = (mesh.rotationY + 1) % 360;
  4.   view.render();
  5. }

これで、3次元空間に置いた立方体をx軸とy軸で回すアニメーションが描ける(図004)。script要素全体を以下のコード001にまとめた。

図004■3次元空間に描かれた立方体が水平および垂直の向きに回る
図004

コード001■3次元空間に描いた立方体をx軸とy軸で回すアニメーション
  1. <script src="lib/awayjs-core.next.min.js"></script>
  2. <script src="lib/stagegl-context.next.min.js"></script>
  3. <script src="lib/stagegl-renderer.next.min.js"></script>
  4. <script>
  5. var view;
  6. var mesh;
  7. var timer;
  8. function initialize() {
  9.   var directionalLight = createDirectionalLight(0.25, 0x00FFFF);
  10.   view = createView(240, 180, 0x0);
  11.   mesh = createCube(400, 400, 400, directionalLight);
  12.   view.scene.addChild(mesh);
  13.   timer = new away.utils.RequestAnimationFrame(rotate);
  14.   timer.start();
  15. }
  16. function createView(width, height, backgroundColor) {
  17.   var defaultRenderer = new away.render.DefaultRenderer();
  18.   var view = new away.containers.View(defaultRenderer);
  19.   view.width = width;
  20.   view.height = height;
  21.   view.backgroundColor = backgroundColor;
  22.   return view;
  23. }
  24. function createCube(width, height, depth, light) {
  25.   var defaultTexture = away.materials.DefaultMaterialManager.getDefaultTexture();
  26.   var material = new away.materials.TriangleMaterial(defaultTexture);
  27.   var mesh = new away.prefabs.PrimitiveCubePrefab(width, height, depth).getNewObject();
  28.   mesh.material = material;
  29.   material.lightPicker = new away.materials.StaticLightPicker([light]);
  30.   return mesh;
  31. }
  32. function createDirectionalLight(ambient, color) {
  33.   var light = new away.entities.DirectionalLight();
  34.   light.direction = new away.geom.Vector3D(0, -1, 1);
  35.   light.ambient = ambient;
  36.   light.color = color;
  37.   return light;
  38. }
  39. function rotate(timeStamp) {
  40.   mesh.rotationX = (mesh.rotationX + 1) % 360;
  41.   mesh.rotationY = (mesh.rotationY + 1) % 360;
  42.   view.render();
  43. }
  44. </script>

サンプル001■Away3D 14/06/24 : Rotating a cube in the 3D space


06 映し出される立方体の角度をカメラの動きで変化させる

仕上げとして、立方体の向きをマウスでインタラクティブに変えよう。ただし、前掲コード001と違って、立方体そのものは動かさず、カメラの位置や向きによって立方体の見せ方をコントロールする。カメラワークには、表現が多彩なHoverControllerクラスのコントローラを用いることにする。HoverController()コンストラクタの第1引数には、カメラのオブジェクトを与える。

new away.controllers.HoverController(カメラ)

HoverControllerオブジェクトは、カメラをパンチルトできる(オリオン技研株式会社「パン・チルト・ロールとはなにですか」参照)。HoverControllerクラスのプロパティは、つぎの表002に掲げた。そして、プロパティHoverController.panAngleHoverController.tiltAngleでパンあるいはチルトを定めると、カメラは直ちにその角度に切替わるのではなく、ステップ(デフォルトは8)に分けてトゥイーンされる。

表002■HoverControllerクラスに備わる基本的なプロパティ
HoverController
クラスのプロパティ
プロパティの値
distance カメラと撮影対象との距離で、デフォルト値は1000。
maxTiltAngle
minTiltAngle
チルトできる角度の範囲の最大度数(デフォルト値90)と最小度数(デフォルト値-90)。
panAngle カメラがy軸を中心に回る度数の角度で、デフォルト値は0。
tiltAngle カメラの仰角を示す度数で、デフォルト値は90。

まず、HoverControllerクラスのオブジェクトでコントローラをつくるクラスは、つぎのような関数(setupCameraController())として定める。引数にはカメラオブジェクトのほか、前掲表002のHoverControllerクラスのプロパティに定める値を渡します。

setupCameraController(カメラ, 距離, チルト最小度数, チルト最大度数, パン度数, チルト度数)

でき上がったJavaScript全体は、後にコード002としてまとめた。HoverControllerオブジェクトのコントローラをつくる関数(setupCameraController())はつぎのとおりだ(第42〜50行目)。新たにつくったHoverControllerオブジェクトのプロパティに、引数の値を定めて返す。なお、前述のとおり、パンとチルトはトゥイーンするので、アニメーションの関数は名前を変えて(render())、View.render()メソッドの呼出しのみ行う(第16行目および第51〜53行目)。

  1. var cameraController;
  1. function initialize() {
  1.   cameraController = setupCameraController(view.camera, 1000, 0, 90, 45, 20);
      // timer = new away.utils.RequestAnimationFrame(rotate);
  1.   timer = new away.utils.RequestAnimationFrame(render);
  2.   timer.start();
  3. }
  1. function setupCameraController(camera, distance, minTiltAngle, maxTiltAngle, panAngle, tiltAngle) {
  2.   var cameraController = new away.controllers.HoverController(camera);
  3.   cameraController.distance = distance;
  4.   cameraController.minTiltAngle = minTiltAngle;
  5.   cameraController.maxTiltAngle = maxTiltAngle;
  6.   cameraController.panAngle = panAngle;
  7.   cameraController.tiltAngle = tiltAngle;
  8.   return cameraController;
  9. }
    // function rotate(timeStamp) {
  10. function render(timeStamp) {
      // mesh.rotationX = (mesh.rotationX + 1) % 360;
      // mesh.rotationY = (mesh.rotationY + 1) % 360;
  11.   view.render();
  12. }

この書替えだけでも、HoverController.panAngleHoverController.tiltAngleプロパティに定めたパンとチルトの位置にカメラはトゥイーンして、立方体の映る角度が変わる(図005)。この後さらに、マウスドラッグでパンとチルトをインタラクティブに動かそう。

図005■立方体を映すカメラのパンとチルトがトゥイーンする
図005左 図005右

ドラッグは、3つのマウスイベントで扱うのがお約束だ。

[1] マウスボタンを押す(onmousedownイベント)
ドラッグが始まる(第15行目)。コールバック関数(startDrag())は、マウスポインタの座標やカメラのパンとチルト角など、必要な値を変数にとる(第55〜58行目)。そして、マウスポインタを動かしたとき(onmousemoveイベント)のドラッグと、ボタンを放したとき(onmouseupイベント)の終了のイベントハンドラ(drag()とstopDrag())をそれぞれ定めた(第59〜60行目)。

[2] ドラッグする(onmousemoveイベント)
コールバック関数(drag())は、マウスポインタの水平な動きからパン(HoverController.panAngleプロパティ)を、垂直な動きでチルト(HoverController.tiltAngleプロパティ)を増減させる(第63〜64行目)。このとき、ドラッグ開始時(startDrag())にとっておいたマウス座標とパンおよびチルト角の変数値から差を求めている。

[3] マウスボタンを放す(onmouseupイベント)
ドラッグを終える。イベントハンドラ(stopDrag())は、onmousemoveonmouseupイベントにnullを代入してコールバック関数の定めは消す(第67〜68行目)。

  1. var lastMouseX;
  2. var lastMouseY;
  3. var lastPanAngle;
  4. var lastTiltAngle;
  5. function initialize() {
  1.   document.onmousedown = startDrag;
  1. }
  1. function startDrag(eventObject) {
  2.   lastMouseX = eventObject.clientX;
  3.   lastMouseY = eventObject.clientY;
  4.   lastPanAngle = cameraController.panAngle;
  5.   lastTiltAngle = cameraController.tiltAngle;
  6.   document.onmousemove = drag;
  7.   document.onmouseup = stopDrag;
  8. }
  9. function drag(eventObject) {
  10.   cameraController.panAngle = 0.5 * (eventObject.clientX - lastMouseX) + lastPanAngle;
  11.   cameraController.tiltAngle = 0.3 * (eventObject.clientY - lastMouseY) + lastTiltAngle;
  12. }
  13. function stopDrag(eventObject) {
  14.   document.onmousemove = null;
  15.   document.onmouseup = null;
  16. }

これで、マウスドラッグによりカメラが立方体の周りをパンあるいはチルトする。JavaScript全体はつぎのコード002にまとめた。また、jsdo.itにサンプルコードを掲げた(サンプル001)。

コード002■3次元空間でカメラをインタラクティブにパンおよびチルトさせる
  1. var view;
  2. var mesh;
  3. var timer;
  4. var cameraController;
  5. var lastMouseX;
  6. var lastMouseY;
  7. var lastPanAngle;
  8. var lastTiltAngle;
  9. function initialize() {
  10.   var directionalLight = createDirectionalLight(0.25, 0x00FFFF);
  11.   view = createView(240, 180, 0x0);
  12.   mesh = createCube(400, 400, 400, directionalLight);
  13.   view.scene.addChild(mesh);
  14.   cameraController = setupCameraController(view.camera, 1000, 0, 90, 45, 20);
  15.   document.onmousedown = startDrag;
  16.   timer = new away.utils.RequestAnimationFrame(render);
  17.   timer.start();
  18. }
  19. function createView(width, height, backgroundColor) {
  20.   var defaultRenderer = new away.render.DefaultRenderer();
  21.   var view = new away.containers.View(defaultRenderer);
  22.   view.width = width;
  23.   view.height = height;
  24.   view.backgroundColor = backgroundColor;
  25.   return view;
  26. }
  27. function createCube(width, height, depth, light) {
  28.   var defaultTexture = away.materials.DefaultMaterialManager.getDefaultTexture();
  29.   var material = new away.materials.TriangleMaterial(defaultTexture);
  30.   var mesh = new away.prefabs.PrimitiveCubePrefab(width, height, depth).getNewObject();
  31.   mesh.material = material;
  32.   material.lightPicker = new away.materials.StaticLightPicker([light]);
  33.   return mesh;
  34. }
  35. function createDirectionalLight(ambient, color) {
  36.   var light = new away.entities.DirectionalLight();
  37.   light.direction = new away.geom.Vector3D(0, -1, 1);
  38.   light.ambient = ambient;
  39.   light.color = color;
  40.   return light;
  41. }
  42. function setupCameraController(camera, distance, minTiltAngle, maxTiltAngle, panAngle, tiltAngle) {
  43.   var cameraController = new away.controllers.HoverController(camera);
  44.   cameraController.distance = distance;
  45.   cameraController.minTiltAngle = minTiltAngle;
  46.   cameraController.maxTiltAngle = maxTiltAngle;
  47.   cameraController.panAngle = panAngle;
  48.   cameraController.tiltAngle = tiltAngle;
  49.   return cameraController;
  50. }
  51. function render(timeStamp) {
  52.   view.render();
  53. }
  54. function startDrag(eventObject) {
  55.   lastMouseX = eventObject.clientX;
  56.   lastMouseY = eventObject.clientY;
  57.   lastPanAngle = cameraController.panAngle;
  58.   lastTiltAngle = cameraController.tiltAngle;
  59.   document.onmousemove = drag;
  60.   document.onmouseup = stopDrag;
  61. }
  62. function drag(eventObject) {
  63.   cameraController.panAngle = 0.5 * (eventObject.clientX - lastMouseX) + lastPanAngle;
  64.   cameraController.tiltAngle = 0.3 * (eventObject.clientY - lastMouseY) + lastTiltAngle;
  65. }
  66. function stopDrag(eventObject) {
  67.   document.onmousemove = null;
  68.   document.onmouseup = null;
  69. }

サンプル001■Away3D 14/06/24: Panning and tilting the camera in the 3D space


07 Away3D TypeScriptの解説

  1. Away3D: 立方体を回してみる
  2. Away3D: パーティクルのアニメーション
  3. Away3D: テクスチャの凹凸と反射 ー 法線マップとスペキュラマップ

サンプル002■Away3D 14/06/24: Animating particle fires on a floor with texture finished



作成者: 野中文雄
更新日: 2014年7月26日 USTREAMビデオを掲載。
作成日: 2014年7月25日


Copyright © 2001-2014 Fumio Nonaka.  All rights reserved.