AS3で3D表現 – カメラの実装

Filed under 3D, AS3



AS3で自作3Dをやる際の、カメラの実装方法です。

以前自作3Dをやった時の、カメラだけ抜き出した版。

前は行列計算とかも自作してたけど、今回はビルトインのMatrix3DとVector3Dを使ってます。

とりあえず、こんな感じになります。カメラがY軸で円運動してます。

カメラを実装するときは、「ビュー変換」という考え方を使います。

これは、3D座標を「カメラの位置を中心として、カメラの視点方向をz軸とした座標」に変換することです。

よく3D関連の本にも載っていて、簡単な行列計算で実装できます。

今回の例では、MyCamera3Dという自作クラスの中のgetViewingTransformMatrix()メソッドがビュー変換行列を作る役割をしています。

以下、ソースです。

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Vector3D;

	[SWF(backgroundColor="#000000")]
	public class Main extends Sprite
	{
		private var vertices:Array;
		private var camera:MyCamera3D;
		private var theta:Number;

		public function Main()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.HIGH;
			stage.frameRate = 40;

			// 座標を計算・格納
			vertices = [];
			vertices.push(new Vector3D(100, 100, 0));
			vertices.push(new Vector3D(100, -100, 0));
			vertices.push(new Vector3D(-100, -100, 0));
			vertices.push(new Vector3D(-100, 100, 0));
			vertices.push(new Vector3D(200, 200, 0));

			// カメラの初期化
			camera = new MyCamera3D(0,0,400, 400);
			theta = 0;

			addEventListener(Event.ENTER_FRAME, onFrame);
		}

		private function onFrame(e:Event):void
		{
			// カメラの円運動
			camera.x = 400 * Math.sin(theta*Math.PI/180);
			camera.z = 400 * Math.cos(theta*Math.PI/180);
			theta ++;

			// 座標の変換
			var view:Matrix3D = camera.getViewingTransformMatrix();
			var newPoints:Array=[];
			var i:int;
			graphics.clear();
			for(i=0 ; i<5 ; i++)
			{
				var eyePoint:Vector3D = view.transformVector(vertices[i]);
				var newX:Number = eyePoint.x * (400 / eyePoint.length) + stage.stageWidth*0.5;
				var newY:Number = -eyePoint.y * (400 / eyePoint.length) + stage.stageHeight*0.5;

				// 変換後の座標点に円を描く
				newPoints.push(new Point(newX, newY));
				graphics.beginFill(0xffffff,1);
				graphics.drawCircle(newX, newY, 10);
				graphics.endFill();
			}

			// 図形を描写 (線とか)
			var sp:Point = newPoints[0] as Point;
			graphics.lineStyle(2, 0xffffff);
			graphics.moveTo(sp.x, sp.y);
			var p:Point;
			for(i=1 ; i<4 ; i++)
			{
				p = newPoints[i] as Point;
				graphics.lineTo(p.x, p.y);
			}
			graphics.lineTo(sp.x, sp.y);
			p = newPoints[2] as Point;
			graphics.moveTo(sp.x, sp.y);
			graphics.lineTo(p.x, p.y);
		}
	}
}

/*
 *  カメラを表すclass。
 *  getViewingTransformMatrix()メソッドで、ビュー変換行列を取得出来る。
 */
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
class MyCamera3D
{
	// 上と原点を示すベクトル
	private static const UP:Vector3D = new Vector3D(0, 1, 0);
	private static const CENTER:Vector3D = new Vector3D(0, 0, 0);
	private var _coord:Vector3D;

	public function MyCamera3D(x:Number, y:Number, z:Number, focus:Number)
	{
		_coord = new Vector3D(x, y, z, 0);
	}

	public function getViewingTransformMatrix():Matrix3D
	{
		// Eye - Center ベクトルを作成。Z軸となる。
		var n:Vector3D = _coord.subtract(CENTER);
		n.normalize();

		// UP×Nを作成。X軸となる。
		var u:Vector3D = UP.crossProduct(n);
		u.normalize();

		// N×Uを作成。Y軸となる。
		var v:Vector3D = n.crossProduct(u);
		v.normalize();

		// 内積の計算
		var dx:Number = -_coord.dotProduct(u);
		var dy:Number = -_coord.dotProduct(v);
		var dz:Number = -_coord.dotProduct(n);

		// ビュー変換行列の作成(Vectorには列ごとに値を収納)
		var vec:Vector.<Number> = Vector.<Number>([u.x, v.x, n.x, 0, u.y, v.y, n.y, 0, u.z, v.z, n.z, 0, dx, dy, dz, 1]);
		var mat:Matrix3D = new Matrix3D(vec);
		return mat;
	}

	// setter & getter
	public function set x(value:Number):void{_coord.x = value;}
	public function set y(value:Number):void{_coord.y = value;}
	public function set z(value:Number):void{_coord.z = value;}
	public function get x():Number{return _coord.x;}
	public function get y():Number{return _coord.y;}
	public function get z():Number{return _coord.z;}
}

Post a Comment

Your email is never published nor shared.