ベクトル
2次元ベクトル
グラフィックスプログラミングでは、空間の座標や距離などを表すために、複数の数値要素を組み合わせた値を用います。XNA Framework では 3DCG プログラミングに必要となる基本的な数学型を最初から提供しています。
物理学で大きさと向きを持った量のことをベクトルと呼び、この概念は数学や幾何学で広く応用されています。XNA Framework を含め、線形代数学を扱う 3DCG プログラミングではベクトルに相当するデータが必要になります。しかし、難しく考える必要はありません。プログラミングの世界では、ベクトル型とは複数の数値要素からなるデータ型であり、技術者にとっては配列と違いはありません。
XNA Framework が提供するベクトル型は2つの要素からなる2次元ベクトル、3つの要素からなる3次元ベクトル、そして4つの要素からなる4次元ベクトルの3種類です。基本的に、他の一般的な 3DCG グラフィックス API で使われるベクトル型と共通しているでしょう。
X 要素と Y 要素からなる2次元ベクトルは Microsoft.Xna.Framework.Vector2 構造体で荒業れます。
[TypeConverterAttribute("typeof(Microsoft.Xna.Framework.Design.Vector2Converter)")] [SerializableAttribute] public struct Vector2 : IEquatable<Vector2>
この構造体のコンストラクタはオーバーロードされており、各要素を初期化する値を指定できます。
public Vector2 (float value)
public Vector2 (float x, float y)
1 つの value パラメータ飲みを受け取るコンストラクタで生成された値は、2次元ベクトルの 2 つの要素がともに value パラメータに指定した値で初期化されます。x パラメータには X 要素の値を y パラメータには Y 要素の値を指定します。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector2 v1 = new Vector2(); Vector2 v2 = new Vector2(1); Vector2 v3 = new Vector2(10, 100); Debug.WriteLine("v1=" + v1); Debug.WriteLine("v2=" + v2); Debug.WriteLine("v3=" + v3); } }
コード1は Vector2 型の値を生成し、その文字列表現を出力しています。実行結果を見れば、正しく 2 つの要素を持ったベクトルが生成できていることが確認できます。
各要素の値はフィールドで公開されています。X 要素の値は X フィールドから、Y 要素の値は Y フィールドから設定または取得できます。
public float X
public float Y
Vector2 構造体の値は、これらのフィールドを通して任意のタイミングで設定または取得できます。各要素の値は float 型なので、コンストラクタやフィールドで浮動小数点数リテラルを指定する場合は F 接尾辞を忘れないでください。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector2 v; v.X = 3.14F; v.Y = 1.41F; Debug.WriteLine("X=" + v.X + ", Y=" + v.Y); } }
コード2は X フィールドと Y フィールドの値を読み書きしています。
3次元ベクトル
X 要素、Y 要素、Z 要素からなる3次元ベクトルは Microsoft.Xna.Framework.Vector3 構造体で表されます。機能的には Z 要素が増えた以外は Vector2 構造体と同じです。
[TypeConverterAttribute("typeof(Microsoft.Xna.Framework.Design.Vector3Converter)")] [SerializableAttribute] public struct Vector3 : IEquatable<Vector3>
この構造体のコンストラクタはオーバーロードされており、各要素を初期化する値を指定できます。
public Vector3 (float value)
public Vector3 (float x, float y, float z)
public Vector3 (Vector2 value, float z)
パラメータを受け取らないコンストラクタは全ての要素を 0 で初期化します。単一の value パラメータを指定するコンストラクタは、パラメータの値でベクトルのすべての要素を初期化します。x パラメータには X 要素の値を、y パラメータには Y 要素の値を、z パラメータには Z 要素の値を指定します。Vector2 型の value パラメータは X 要素と Y 要素を初期化する値を指定します。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector3 v1 = new Vector3(); Vector3 v2 = new Vector3(1); Vector3 v3 = new Vector3(1.5F, 2.5F, 3.5F); Vector3 v4 = new Vector3(new Vector2(10, 20), 30); Debug.WriteLine("v1=" + v1); Debug.WriteLine("v2=" + v2); Debug.WriteLine("v3=" + v3); Debug.WriteLine("v4=" + v4); } }
コード3は Vector3 構造体の値を生成し、それぞれの値の文字列表現を出力しています。
Vector2 構造体と同様、各要素の値はフィールドで公開されています。X 要素の値は X フィールドから、Y 要素の値は Y フィールドから、Z 要素の値は Z フィールドから設定または取得できます。
public float X
public float Y
public float Z
Vector3 構造体の値は、これらのフィールドを通して任意のタイミングで設定または取得できます。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector3 v; v.X = 10; v.Y = 20; v.Z = 30; Debug.WriteLine("X=" + v.X + ", Y=" + v.Y + ", Z=" + v.Z); } }
コード4は Vector3 構造体の X フィールド、Y フィールド、Z フィールドから要素の値を設定及び取得しています。
4次元ベクトル
X 要素、Y 要素、Z 要素、そして W 要素からなる4次元ベクトルは Microsoft.Xna.Framework.Vector4 構造体で表されます。
[TypeConverterAttribute("typeof(Microsoft.Xna.Framework.Design.Vector4Converter)")] [SerializableAttribute] public struct Vector4 : IEquatable<Vector4>
この構造体のコンストラクタはオーバーロードされており、各要素を初期化する値を指定できます。パラメータの構造は Vector2 構造体や Vector3 構造体と同じです。
public Vector4 ()
public Vector4 (float value)
public Vector4 (float x, float y, float z, float w)
public Vector4 (Vector2 value, float z, float w)
public Vector4 (Vector3 value, float w)
パラメータを受け取らないコンストラクタは全ての要素を 0 で初期化します。単一の value パラメータを指定するコンストラクタは、パラメータの値でベクトルのすべての要素を初期化します。
x パラメータには X 要素の値を、y パラメータには Y 要素の値を、z パラメータには Z 要素の値を、w パラメータには W 要素の値を指定します。Vector2 型の value パラメータには X 要素と Y 要素の値を表す2次元ベクトルを、Vector3 型の value パラメータには X 要素、Y 要素、Z 要素の値を表す3次元ベクトルを指定します。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector4 v1 = new Vector4(); Vector4 v2 = new Vector4(1); Vector4 v3 = new Vector4(1.5F, 2.5F, 3.5F, 4.5F); Vector4 v4 = new Vector4(new Vector2(10, 20), 30, 40); Vector4 v5 = new Vector4(new Vector3(100, 200, 300), 400); Debug.WriteLine("v1=" + v1); Debug.WriteLine("v2=" + v2); Debug.WriteLine("v3=" + v3); Debug.WriteLine("v4=" + v4); Debug.WriteLine("v5=" + v5); } }
コード5は Vector4 構造体の値を生成し、その結果を出力しています。
Vector2 や Vector3 構造体と同様、各要素の値はフィールドで公開されています。X 要素の値は X フィールドから、Y 要素の値は Y フィールドから、Z 要素の値は Z フィールドから、W 要素の値は W フィールドから設定または取得できます。
public float X
public float Y
public float Z
public float W
Vector4 構造体の値は、これらのフィールドを通して任意のタイミングで設定または取得できます。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector4 v; v.X = 10; v.Y = 20; v.Z = 30; v.W = 40; Debug.WriteLine("X=" + v.X + ", Y=" + v.Y + ", Z=" + v.Z + ", W=" + v.W); } }
コード6は Vector4 構造体の各要素を表す X フィールド、Y フィールド、Z フィールド、及び W フィールドから値を設定し、出力しています。
固定値の取得
ベクトルを表す各構造体は、ベクトルの初期値や基準値として用いられる標準的な値のいくつかを静的な読み取り専用プロパティで提供しています。コンストラクタで初期化するよりも、簡単かつ確実に値を取得できるため利用すると便利です。
全ての要素が 0 の状態のベクトルは Zero プロパティで提供されています。これはパラメータを渡さない空のコンストラクタで作られる値と同じですが、ベクトルが 0 であるという意図をコード上で明示的に表すことができます。
public static Vector2 Zero { get; }
public static Vector3 Zero { get; }
public static Vector4 Zero { get; }
Zero プロパティは Vector2 構造体、Vector3 構造体、Vector4 構造体の、すべてのベクトル型で用意されています。ベクトルのすべての要素が 0 の状態の値を得られます。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Debug.WriteLine("Vector2 Zero=" + Vector2.Zero); Debug.WriteLine("Vector3 Zero=" + Vector3.Zero); Debug.WriteLine("Vector4 Zero=" + Vector4.Zero); } }
同様に、すべての要素が 1 に設定されたベクトルは One プロパティから取得できます。
public static Vector2 One { get; }
public static Vector3 One { get; }
public static Vector4 One { get; }
One プロパティも、すべてのベクトル型で提供されています。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Debug.WriteLine("Vector2 One=" + Vector2.One); Debug.WriteLine("Vector3 One=" + Vector3.One); Debug.WriteLine("Vector4 One=" + Vector4.One); } }
Vector2 構造体と Vector3 構造体には、これらのプロパティに加えて方向を表す単位ベクトルを返すプロパティも提供しています。
Vector2 構造体では X 軸の単位ベクトルを UnitX プロパティから、Y 軸の単位ベクトルを UnitY プロパティから取得できます。
public static Vector2 UnitX { get; }
public static Vector2 UnitY { get; }
同様に Vector3 構造体では UnitX プロパティと UnitY プロパティに加えて Z 軸の単位ベクトル UnitZ プロパティが提供されています。
public static Vector3 UnitX { get; }
public static Vector3 UnitY { get; }
public static Vector3 UnitZ { get; }
この流れから容易に想像できますが Vector4 構造体でも各軸の単位ベクトルを返す UnitX プロパティ、UnitY プロパティ、UnitZ プロパティに加えて、W 軸の単位ベクトルを返す UnitW プロパティが提供されています。
public static Vector4 UnitX { get; }
public static Vector4 UnitY { get; }
public static Vector4 UnitZ { get; }
public static Vector4 UnitW { get; }
これらの単位ベクトルはゲーム中のベクトルの初期値として多様な場所で利用されるため、覚えておくと便利です。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Debug.WriteLine("Vector2 UnitX=" + Vector2.UnitX); Debug.WriteLine("Vector2 UnitY=" + Vector2.UnitY); Debug.WriteLine("Vector3 UnitX=" + Vector3.UnitX); Debug.WriteLine("Vector3 UnitY=" + Vector3.UnitY); Debug.WriteLine("Vector3 UnitZ=" + Vector3.UnitZ); Debug.WriteLine("Vector4 UnitX=" + Vector4.UnitX); Debug.WriteLine("Vector4 UnitY=" + Vector4.UnitY); Debug.WriteLine("Vector4 UnitZ=" + Vector4.UnitZ); Debug.WriteLine("Vector4 UnitW=" + Vector4.UnitW); } }
コード9では、各次元のベクトル型で用意された単位ベクトルのプロパティを出力しています。コンストラクタで明示的に初期化して同じ値を作ることができますが、これらのプロパティを用いたほうが便利でしょう。
これら各軸の単位ベクトルに加えて Vector3 構造体型では 3 次元空間の各方向を表す単位ベクトルを静的プロパティで提供しています。左方向(-1, 0, 0)は Left プロパティ、右方向(1, 0, 0)は Right プロパティ、上方向(0, 1, 0)は Up プロパティ、下方向(0, -1, 0)は Down プロパティ、後方向(0, 0, 1)は Backward プロパティ、そして前方向(0, 0, -1)は Forward プロパティから取得できます。
public static Vector3 Left { get; }
public static Vector3 Right { get; }
public static Vector3 Up { get; }
public static Vector3 Down { get; }
public static Vector3 Backword { get; }
public static Vector3 Foward { get; }
なかでも重要なのは Z 軸の方向で、Z 軸の値が正であれば手前に、負であれば奥に向かう座標系のことを右手座標系と呼び、その逆の座標系を左手座標系と呼びます。Windows で標準的な 3D グラフィックスの API である DirectX では左手座標系が用いられていますが、XNA Framework は右手座標系が用いられています。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Debug.WriteLine("Left=" + Vector3.Left); Debug.WriteLine("Right=" + Vector3.Right); Debug.WriteLine("Up=" + Vector3.Up); Debug.WriteLine("Down=" + Vector3.Down); Debug.WriteLine("Backward=" + Vector3.Backward); Debug.WriteLine("Forward=" + Vector3.Forward); } }
コード10は Vector3 構造体の上下左右前後を表す各単位ベクトルを出力しています。
ベクトルの大きさ
ベクトルの大きさは、すべてのベクトル型で共通して Length() メソッドから取得できます。このメソッドは、ベクトルに設定されている各要素の値からベクトルの大きさを計算して結果を返します。
public float Length ()
public float Length ()
public float Length ()
このプロパティから得られるベクトルの大きは、ベクトル要素の2乗和の平方根です。ベクトルは大きさと向きを情報として持っていますが、そこから大きさだけを取り出しすことができます。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector2 v1 = new Vector2(5, 3); Vector2 v2 = new Vector2(1, 1); Debug.WriteLine(v1 + " Length=" + v1.Length()); Debug.WriteLine(v2 + " Length=" + v2.Length()); } }
コード11は 2 次元ベクトルの大きさを取得し、それを出力しています。実行結果から X 要素の 2 乗と Y 要素の 2 乗を加算した結果(x * x + y * y)の平方根であることが確認できます。
ベクトルの大きさをどのように解釈し、何に利用するかはプログラム次第です。大きさ、長さ、強さなど、ベクトル型を使って表現するデータの種類によって意味は変わってきます。重要なのは、ベクトルの向きに関係なく、ベクトルの大きさだけを float 型の値として抽出できるという点です。例えば、異なる向きのベクトルを容易に比較できるようになります。
単位ベクトル
単位ベクトルは常にベクトルの大きさ、すなわち Length() メソッドの戻り値が 1 になるベクトルのことです。ベクトルの大きさを均一化することで、ベクトルの向きだけを抽出できます。単位ベクトルは、方向を表すベクトルとして利用できます。
任意のベクトルから単位ベクトルを取得するには、すべてのベクトル型で共通して Normalize() メソッドを用います。このメソッドは、現在のベクトルを単位ベクトルに変換するインスタンスメソッドと、指定したベクトルを単位ベクトルに変換して返す静的メソッドが、各ベクトル型で用意されています。
public void Normalize ()
public static Vector2 Normalize (Vector2 value)
public void Normalize ()
public static Vector3 Normalize (Vector3 value)
public void Normalize ()
public static Vector4 Normalize (Vector4 value)
value パラメータには単位ベクトルに変換する元のベクトルを指定します。
using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { Vector2 source = new Vector2(5, 3); Vector2 normalized = Vector2.Normalize(source); float length = normalized.Length(); Debug.WriteLine("Source=" + source); Debug.WriteLine("Normalized=" + normalized); Debug.WriteLine("Length=" + length); } }
コード12は適当な Vector2 構造体の値から Normalize() メソッドを用いて単位ベクトルを取得し、その結果を出力しています。また、Length() メソッドで単位ベクトルからベクトルの大きさを取得し、その結果が 1 であることも実行結果から確認できます。