Away3D TypeScriptではじめる3次元表現

第8回 オブジェクトの位置と大きさをランダムにする

この記事を読むのに必要な時間:およそ 10.5 分

前回の第7回回り込むカメラの真ん中にオブジェクトを捉えるでは,真ん中のオブジェクトが中心となる円周上に,等しい間隔で複数のオブジェクトを並べ,カメラはその外の円軌道から回り込ませた第7回コード3)⁠今回は,オブジェクトの位置や大きさをランダムにしてみよう。

Matrix3Dクラスで座標を回転する

オブジェクトをランダムに変換する前に,第7回コード3で円周上の座標を求めた関数(getPolarPosition())は書き替える。座標の回転や伸縮,さらに移動まで一手に引受けるMatrix3Dクラスを使ってみたい。"matrix"というのは数学の「行列」を意味する映画を思い浮かべてはいけない)⁠難しげに聞こえるかもしれないが,つまるところ数値をパラメータで変換する仕組みだ※1)⁠

たとえば,Photoshop CCやFireworks CS6のフィルタやカラー調整は,内部的に行列を用いて行われている図1)⁠これらの機能は,画像のピクセルごとのカラー値を変換する。

図1 Fireworks CS6の「色相・彩度」フィルタ

図1 Fireworks CS6の「色相・彩度」フィルタ

Away3DのMatrix3Dクラスが変換するのは3次元座標だ。パラメータを変えることにより,回転や伸縮,あるいは移動ができる。あまり難しく考えすぎずに,つぎの図2のようなパネルを思い浮かべてもらえばよい。ただし,スライダではなく,もちろんJavaScriptコードを書いて変換しなければならない。

図2 Matrix3Dクラスは座標を回転・伸縮・移動する

図2 Matrix3Dクラスは座標を回転・伸縮・移動する

Matrix3Dクラスに備わる回転・伸縮・移動の座標変換メソッドは,つぎの表1のとおりだ。円周上の座標を求めるには,回転のappendRotation()メソッドを使う。引数には度数の角度と回転軸を与える。y軸で回したいので,回転軸はVector3D.Y_AXISとする。なお,今のところ前変換(prepend)と後変換(append)が何かは気にしなくてよい※2)⁠まずは,Matrix3Dクラスに慣れることを心がけよう。

appendRotation(角度, 回転軸)

表1 Matrix3Dクラスの回転・伸縮・移動の座標変換メソッド

座標変換前変換後変換
回転 prependRotation(degrees, axis)appendRotation(degrees, axis)
伸縮prependScale(xScale, yScale, zScale)appendScale(xScale, yScale, zScale)
移動prependTranslation(x, y, z)appendTranslation(x, y, z)

Matrix3Dクラスで,距離と角度から円周上の座標をどう求めるか。x軸上の点(距離, 0, 0)をその角度回せばよい。ただし,Matrix3Dオブジェクトは変換行列を表し,3次元座標ではない。Matrix3D.transformVector()メソッドに座標のVector3Dオブジェクトを渡せば,変換されたVector3Dオブジェクトが得られる。距離と角度から円周上の座標を返す関数(getPolarPosition())は,以下のようにほぼすべて書き替えた※3)⁠

Matrix3Dオブジェクト.transformVector(Vector3Dオブジェクト)
var Matrix3D = require("awayjs-core/lib/geom/Matrix3D");

function getPolarPosition(distance, rotationY) {
  // var vector = new Vector3D();
  var vector = new Vector3D(distance, 0, 0);
  // vector.x = distance * Math.cos(-rotationY * Math.PI / 180);
  // vector.z = distance * Math.sin(-rotationY * Math.PI / 180);
  var matrix = new Matrix3D();
  matrix.appendRotation(rotationY, Vector3D.Y_AXIS);
  // return vector;
  return matrix.transformVector(vector);
}

これで,円軌道上に置くオブジェクトは,Matrix3Dオブジェクトの行列変換により位置が決まる。書き替えたスクリプト全体は,つぎのコード1のとおりだ。もちろん,動きの見た目は第7回コード3と変わらない。

コード1 円軌道上に複数のオブジェクトを行列変換により配置してカメラで回り込む

var LoaderEvent = require("awayjs-core/lib/events/LoaderEvent");
var Vector3D = require("awayjs-core/lib/geom/Vector3D");
var Matrix3D = require("awayjs-core/lib/geom/Matrix3D");
var AssetLibrary = require("awayjs-core/lib/library/AssetLibrary");
var URLRequest = require("awayjs-core/lib/net/URLRequest");
var RequestAnimationFrame = require("awayjs-core/lib/utils/RequestAnimationFrame");
var View = require("awayjs-display/lib/containers/View");
var DirectionalLight = require("awayjs-display/lib/entities/DirectionalLight");
var StaticLightPicker = require("awayjs-display/lib/materials/lightpickers/StaticLightPicker");
var PrimitiveCubePrefab = require("awayjs-display/lib/prefabs/PrimitiveCubePrefab");
var DefaultRenderer = require("awayjs-renderergl/lib/DefaultRenderer");
var TriangleMethodMaterial = require("awayjs-methodmaterials/lib/TriangleMethodMaterial");
var view;
var cube;
var imageDiffuse = "assets/trinket_diffuse.jpg";
var timer;
var ORIGIN = new Vector3D();
var angle = -Math.PI / 2;
var distance = 1500;
var stageWidth = 240;
var stageHeight = 180;
function initialize() {
  var directionalLight = createDirectionalLight(0.5, 0xFFFFFF);
  view = createView(stageWidth, stageHeight, 0x0);
  cube = createCube(400, 400, 400, directionalLight);
  setCamera(view.camera, distance, angle);
  view.scene.addChild(cube);
  cloneMesh(cube, 5);
  AssetLibrary.addEventListener(LoaderEvent.RESOURCE_COMPLETE, onResourceComplete);
  AssetLibrary.load(new URLRequest(imageDiffuse));
  timer = new RequestAnimationFrame(rotate);
  timer.start();
  view.render();
}
function createView(width, height, backgroundColor) {
  var defaultRenderer = new DefaultRenderer();
  var view = new View(defaultRenderer);
  view.width = width;
  view.height = height;
  view.backgroundColor = backgroundColor;
  return view;
}
function createCube(width, height, depth, light) {
  var material = new TriangleMethodMaterial();
  var cube = new PrimitiveCubePrefab(width, height, depth, 1, 1, 1, false)
  .getNewObject();
  cube.material = material;
  material.lightPicker = new StaticLightPicker([light]);
  return cube;
}
function cloneMesh(mesh, count) {
  var scene = view.scene;
  var distance = 1000;
  var degrees = 360 / count;
  for (var i = 0; i < count; i++) {
    var clone = mesh.clone();
    var rotationY = degrees * i;
    var position = getPolarPosition(distance, rotationY);
    clone.x = position.x;
    clone.y = position.y;
    clone.z = position.z;
    clone.rotationY = rotationY;
    scene.addChild(clone);
  }
}
function createDirectionalLight(ambient, color) {
  var light = new DirectionalLight();
  light.ambient = ambient;
  light.color = color;
  return light;
}
function onResourceComplete(eventObject) {
  var assets = eventObject.assets;
  var material = cube.material;
  material.texture = assets[0];
  view.render();
}
function rotate(timeStamp) {
  var camera = view.camera;
  angle += timeStamp / 2000;
  setCamera(camera, distance, angle);
  view.render();
}
function setCamera(camera, distance, angle) {
  camera.x = Math.cos(angle) * distance;
  camera.z = Math.sin(angle) * distance;
  camera.lookAt(ORIGIN);
}
function getPolarPosition(distance, rotationY) {
  var vector = new Vector3D(distance, 0, 0);
  var matrix = new Matrix3D();
  matrix.appendRotation(rotationY, Vector3D.Y_AXIS);
  return matrix.transformVector(vector);
}
※1
行列についてもう少し詳しくは,行列?なにそれ?おいしいの?参照。15分間のビデオでもご覧いただける。
※2
一応説明しておくと,行列による座標変換は掛け算で行われる。しかし,実数の乗算と異なり,行列は掛ける順序が変わると答えが違ってしまう(交換法則が成立たない)⁠これは,フィルタでモザイクを掛けてからぼかすのと,ぼかしてからモザイクを掛けたのでは画像が変わることからもわかるだろう。そこで,掛け算の左右(前後)どちらに置くのか,メソッドが分けて用意されているのだ。
※3
前回xz平面におけるy軸回りの回転が数学とは反対方向だということで,角度(rotationY)を正負逆にした(再掲第7回図6)⁠しかし,もちろんAway3DのMatrix3Dクラスはコンピュータグラフィックス向けにつくられているので,そのような考慮はしなくてよい。

第7回図6 xz平面でy軸周りの角度は時計回りが正

第7回図6 xz平面でy軸周りの角度は時計回りが正

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入