AS3.0 で 3D プログラミングを1から勉強する (1)
3D の原理をあまり知らなかったので、ActionScript 3.0 で1から勉強してみた。1からなのでフレームワークは使わず、自力で実装していく。
Web 上には色んな資料があってありがたいだけど、玉石混交な上に、有用なものでも一本道で幅の狭いものが多い。前提知識のない自分にとっては、資料間の関連性を理解するのが大変だった。
なので、なるべく簡単なところからスタートしつつ、広く浅く体験していくことを目標としてみる。まずは、四面体をワイヤーフレームで表示するところからスタートしよう。
四面体を定義する
まずは、3次元上の点を表現する Point3D クラスを作る。
class Point3D { public var x:Number; public var y:Number; public var z:Number; public function Point3D(_x:Number = 0, _y:Number = 0, _z:Number = 0){ x = _x; y = _y; z = _z; } }
四面体を作るために、メインのクラスに4つの点を定義した。
public class Study3d1 extends Sprite{ private var p1:Point3D = new Point3D( 0, 0, 100); private var p2:Point3D = new Point3D(100, 0, 0); private var p3:Point3D = new Point3D( 0, 100, 0); private var p4:Point3D = new Point3D(-50, -50, -50);
2次元空間に表示する
3次元の点を2次元空間に描画する方法を考える。
とはいえ、一番最初のサンプルなので、真上(もしくは真下)から見たところを表示する。つまり、Z の値を無視して表示する。3次元空間上の(0, 0, 100) という点は、(0, 0) に表示する。
プロット先の座標を求めて(といっても、Zの値を無視するだけだけど)、線で結んでやれば四面体を真上から見た図ができた。
回転!
これだとあまりにも退屈なので、回転させてみよう。
カメラが回転するのは難しいので、四面体に回転してもらうことにする。こっちはちょー簡単。
ある点 p を原点周りに rad ラジアンだけ回転させるには
p.x = Math.cos(rad) * x + Math.sin(rad) * y; p.y = -Math.sin(rad) * x + Math.cos(rad) * y;
とする。(詳しく知りたい人は「回転行列」あたりのキーワードで検索すべし)
x と y を y と z に置き換えれば X 軸周りの回転になるし、z と x に置き換えれば Y 軸周りの回転になる。
マウスの位置に応じて Y 軸周りと Z 軸の周りに回転させるようにしてみた。回転後の座標を線で繋げば回転する四面体の完成だ。
ソースはたった86行。ローテクだけど、回転し続けていれば立体に見えなくもない。
第2回に続く。
今回のソースは以下に(86行)。
package { import flash.display.*; import flash.geom.Point; [SWF(backgroundColor="0x000000")] public class Study3d1 extends Sprite{ private var p1:Point3D = new Point3D( 0, 0, 100); private var p2:Point3D = new Point3D(100, 0, 0); private var p3:Point3D = new Point3D( 0, 100, 0); private var p4:Point3D = new Point3D(-50, -50, -50); private var canvas:Sprite = new Sprite(); public function Study3d1(){ stage.scaleMode = "noScale"; stage.align = "TL"; addChild(canvas); canvas.x = 150; canvas.y = 150; changeHandler(null); addEventListener("enterFrame", changeHandler); } private function changeHandler(event:Object):void { var pp1:Point3D = rotate(p1); var pp2:Point3D = rotate(p2); var pp3:Point3D = rotate(p3); var pp4:Point3D = rotate(p4); canvas.graphics.clear(); drawPoint(pp1); drawPoint(pp2); drawPoint(pp3); drawPoint(pp4); drawLine(pp1, pp2); drawLine(pp1, pp3); drawLine(pp1, pp4); drawLine(pp2, pp3); drawLine(pp2, pp4); drawLine(pp3, pp4); } private function drawPoint(p:Point3D):void { canvas.graphics.beginFill(0xffffff); canvas.graphics.drawCircle(p.x, p.y, 10); canvas.graphics.endFill(); } private function drawLine(p1:Point3D, p2:Point3D):void { canvas.graphics.lineStyle(3, 0xffffff); canvas.graphics.moveTo(p1.x, p1.y); canvas.graphics.lineTo(p2.x, p2.y); canvas.graphics.lineStyle(); } private function rotate(_p:Point3D):Point3D { var ret:Point3D = new Point3D(_p.x, _p.y, _p.z); var p:Point; // y rotate p = rotate2d(ret.z, ret.x, stage.mouseX / 180 * Math.PI); ret.z = p.x; ret.x = p.y; // z rotate p = rotate2d(ret.x, ret.y, -stage.mouseY / 180 * Math.PI); ret.x = p.x; ret.y = p.y; return ret; } private function rotate2d(x:Number, y:Number, rad:Number):Point { var p:Point = new Point(); p.x = Math.cos(rad) * x + Math.sin(rad) * y; p.y = -Math.sin(rad) * x + Math.cos(rad) * y; return p; } } } class Point3D { public var x:Number; public var y:Number; public var z:Number; public function Point3D(_x:Number = 0, _y:Number = 0, _z:Number = 0){ x = _x; y = _y; z = _z; } }