[3D Tips #1] ベクトル

3D の基礎知識として絶対に欠かせない「ベクトル」や「行列」。今回はベクトルについて。

ベクトルは大きさと方向を持った量のこと。

2 次元のベクトルの場合は、<x, y> 2 つの成分を持ち(厳密には、どんな成分を持たせるかは実装者次第で、n 次元のベクトルは n 個の成分を持つというだけ)、 AS でいうところの Point クラスのようなもの。Point クラスでいえば、大きさは length メソッドで求まり、方向は x, y 成分の各数値になる。ただ、3D で扱うベクトルは 3 次元なので <x, y, z> 3 つの成分を持つ独自のクラスを作る必要がある。この時、持たせておくクラスメンバは下記の通り。


<必須になるクラスメンバ>

・x, y, z の各プロパティ

・length メソッド
ベクトルの大きさを求める。三平方の定理で求まる。
参考:deq notes さん 「ベクトルの基礎」

・normalize メソッド
ベクトルの大きさが 1 になるように <x, y, z> 各成分を変更する。各成分をベクトルの大きさで割ることで求まる。この時、各数値は 0 で割れず(割り算の基本)、1 の時は割る必要がない。こうして求まったベクトルを「単位ベクトル」といい、単位ベクトルを求めることを「正規化 (normalize) 」という。法線ベクトル (normal) とは関係無いので注意すること。
参考:deq notes さん 「ベクトルの利用」

・cross メソッド
ベクトル同士の外積を求める。2 つのベクトルに対し垂直なベクトルを求めるのに必要で、3D では法線ベクトルを求めるときなどに使用する。3 次元の場合、返り値はベクトルになる。数式で表すときは「P × Q」と表し、普通の乗算とは異なるので注意すること。
参考:deq notes さん 「内積と外積」

・dot メソッド
ベクトル同士の内積を求める。2 つのベクトルのなす角度を求めるのに必要で、3D では面の可視判定などに使用する。3 次元の場合、返り値は数値になる。数式で表すときは「P ・ Q」と表し、普通の乗算とは異なるので注意すること。
参考:deq notes さん 「内積と外積」
参考:Flashゲーム講座&ASサンプル集 さん 「交差判定について」の「内積とは?」の項目

<あると良いクラスメンバ>

・invert メソッド
ベクトルの <x, y, z> 各成分を反転する。

・add メソッド
ベクトルの <x, y, z> 各成分を加算する。数式で表すときは「P + Q」と表し、普通の加算とは異なるので注意すること。

・sub メソッド
ベクトルの <x, y, z> 各成分を減算する。数式で表すときは「P - Q」と表し、普通の減算とは異なるので注意すること。

以上を踏まえて定義した Vector3D クラスは下記のとおり。
なお、今後のために IVector3D インターフェースを実装する。

Vector3D クラス

package
{
    public class Vector3D implements IVector3D
    {
        private var _x:Number;
        private var _y:Number;
        private var _z:Number;

        /**
         * 新しい Vector3D インスタンスを作成します。
         *
         * @param x ベクトルの x 成分です。
         * @param y ベクトルの y 成分です。
         * @param z ベクトルの z 成分です。
         */
        public function Vector3D(x:Number = 0, y:Number = 0, z:Number = 0):void
        {
            _x = x;
            _y = y;
            _z = z;
        }

        /**
         * ベクトルの x 成分です。
         */
        public function get x():Number
        {
            return _x;
        }

        public function set x(value:Number):void
        {
            _x = value;
        }

        /**
         * ベクトルの y 成分です。
         */
        public function get y():Number
        {
            return _y;
        }

        public function set y(value:Number):void
        {
            _y = value;
        }

        /**
         * ベクトルの z 成分です。
         */
        public function get z():Number
        {
            return _z;
        }

        public function set z(value:Number):void
        {
            _z = value;
        }

        /**
         * ベクトルの大きさです。
         */
        public function get length():Number
        {
            return Math.sqrt(x * x + y * y + z * z);
        }

        /**
         * ベクトルを正規化して単位ベクトルにします。
         */
        public function normalize():void
        {
            var l:Number = length;

            if (l != 0 && l != 1)
            {
                x /= l;
                y /= l;
                z /= l;
            }
        }

        /**
         * 対象のベクトルとの外積を返します。
         *
         * @param v 対象のベクトルです。
         * @return 対象のベクトルとの外積。
         */
        public function cross(v:IVector3D):Vector3D
        {
            return new Vector3D(
                y * v.z - z * v.y,
                z * v.x - x * v.z,
                x * v.y - y * v.x
            );
        }

        /**
         * 対象のベクトルとの内積を返します。
         *
         * @param v 対象のベクトルです。
         * @return 対象のベクトルとの内積。
         */
        public function dot(v:IVector3D):Number
        {
            return x * v.x + y * v.y + z * v.z;
        }

        /**
         * ベクトルを反転します。
         */
        public function invert():void
        {
            x *= -1;
            y *= -1;
            z *= -1;
        }

        /**
         * 対象のベクトルで加算します。
         *
         * @param v 対象のベクトルです。
         */
        public function add(v:IVector3D):void
        {
            x += v.x;
            y += v.y;
            z += v.z;
        }

        /**
         * 対象のベクトルで減算します。
         *
         * @param v 対象のベクトルです。
         */
        public function sub(v:IVector3D):void
        {
            x -= v.x;
            y -= v.y;
            z -= v.z;
        }

        /**
         * ベクトルの複製を作成します。
         *
         * @return ベクトルの複製。
         */
        public function clone():Vector3D
        {
            return new Vector3D(x, y, z);
        }

        /**
         * このオブジェクトのストリング表現を返します。
         *
         * @return このオブジェクトのストリング表現。
         */
        public function toString():String
        {
            return "[object Vector3D {x:" + x + ", y:" + y + ", z:" + z + "}]";
        }
    }
}

IVector3D インターフェース

package
{
    public interface IVector3D
    {
        function get x():Number;
        function set x(value:Number):void;
        function get y():Number;
        function set y(value:Number):void;
        function get z():Number;
        function set z(value:Number):void;
    }
}

とりあえず今日はここまで。突っ込み大歓迎、答えられる範囲なら質問も大歓迎です。次は 3D 用の行列についてまとめます。

Leave a Reply