【Unity】ベクトルAPIの内部計算式まとめ

こじゃらこじゃら

何気なく使っているVector2Vector3って内部ではどんな計算が行われているの?

このはこのは

数学的な計算が行われているわ。実際にどのような数式が使われていくか見ていくね。

Unityが提供するベクトル構造体では、ベクトルの計算を楽できるようなAPIが提供されています。

ベクトルには、Vector2Vector3Vector4の3種類の構造体があり、それぞれ2次元ベクトル3次元ベクトル4次元ベクトルを表します。

これらの内部実装は、Unity公式のGitHubソースからある程度閲覧可能です。

本記事では、ベクトル構造体の内部処理を知りたい方向けに、代表的なプロパティやメソッドの内部で使われている数式をまとめました。

また、本記事で示す数式は読みやすさのため、一部数式を整理しております。

動作環境
  • Unity 2022.1.7f1

スポンサーリンク

加算(+演算子)

ベクトルの各要素を加算した結果となります。

Vector2.operator+
\bm{a} + \bm{b} = \left( \begin{array}{c} a_x + b_x \\ a_y + b_y \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \end{array} \right)

Vector3.operator+
\bm{a} + \bm{b} = \left( \begin{array}{c} a_x + b_x \\ a_y + b_y \\ a_z + b_z \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \end{array} \right)

Vector4.operator+
\bm{a} + \bm{b} = \left( \begin{array}{c} a_x + b_x \\ a_y + b_y \\ a_z + b_z \\ a_w + b_w \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \\ a_w \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \\ b_w \end{array} \right)

減算(-演算子)

ベクトルの各要素を引き算した結果になります。

Vector2.operator-
\bm{a} - \bm{b} = \left( \begin{array}{c} a_x - b_x \\ a_y - b_y \end{array} \right)

Vector3.operator-
\bm{a} - \bm{b} = \left( \begin{array}{c} a_x - b_x \\ a_y - b_y \\ a_z - b_z \end{array} \right)

Vector4.operator-
\bm{a} - \bm{b} = \left( \begin{array}{c} a_x - b_x \\ a_y - b_y \\ a_z - b_z \\ a_w - b_w \end{array} \right)

スカラーとの乗算(*演算子)

スカラー(float型の値)とベクトルとの掛け算は、次式のように各要素をスカラー倍した結果になります。

Vector2.operator*
s \bm{a} = \left( \begin{array}{c} sa_x \\ sa_y \end{array} \right)

Vector3.operator*
s \bm{a} = \left( \begin{array}{c} sa_x \\ sa_y \\ sa_z \end{array} \right)

Vector4.operator*
s \bm{a} = \left( \begin{array}{c} sa_x \\ sa_y \\ sa_z \\ sa_w \end{array} \right)

スカラーとの除算(/演算子)

ベクトルの各要素をスカラー量で割った結果となります。

Vector2.operator/
\dfrac{\bm{a}}{s} = \left( \begin{array}{c} \dfrac{a_x}{s} \\ \\ \dfrac{a_y}{s} \end{array} \right)

Vector3.operator/
\dfrac{\bm{a}}{s} = \left( \begin{array}{c} \dfrac{a_x}{s} \\ \\ \dfrac{a_y}{s} \\ \\ \dfrac{a_z}{s} \end{array} \right)

Vector4.operator/
\dfrac{\bm{a}}{s} = \left( \begin{array}{c} \dfrac{a_x}{s} \\ \\ \dfrac{a_y}{s} \\ \\ \dfrac{a_z}{s} \\ \\ \dfrac{a_w}{s} \end{array} \right)

長さ・ノルム(magnitudeプロパティ)

各要素の二乗和平方根となります。

magnitudeプロパティが返すベクトルを\|\bm{v}\|とすると、次式で表せます。

Vector2.magnitude
\|\bm{v}\| = \sqrt{x^2 + y^2}

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \end{array} \right)

Vector3.magnitude
\|\bm{v}\| = \sqrt{x^2 + y^2 + z^2}

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \\ z \end{array} \right)

Vector4.magnitude
\|\bm{v}\| = \sqrt{x^2 + y^2 + z^2 + w^2}

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \\ z \\ w \end{array} \right)

長さ・ノルムの二乗(sqrMagnitudeプロパティ)

ベクトルの長さ(ノルム)を二乗するため、平方根のない単なる二乗和となります。

sqrMagnitudeプロパティが返すベクトルを\|\bm{v}\|^2とすると、次式のようになります。

Vector2.sqrMagnitude
\|\bm{v}\|^2 = x^2 + y^2

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \end{array} \right)

Vector3.sqrMagnitude
\|\bm{v}\|^2 = x^2 + y^2 + z^2

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \\ z \end{array} \right)

Vector4.sqrMagnitude
\|\bm{v}\|^2 = x^2 + y^2 + z^2 + w^2

ただし、

\bm{v} = \left( \begin{array}{c} x \\ y \\ z \\ w \end{array} \right)

平方根の計算が無い分、magnitudeプロパティよりもsqrMagnitudeプロパティのほうが処理が軽いと言えます。

正規化(normalizedプロパティ、Normalizeメソッド)

対象のベクトル\bm{v}ベクトルの長さ\|\bm{v}\|割ったベクトルとなります。

ただし、\|\bm{v}\|が非常に小さい場合、零ベクトルとなります。

Vector*.normalized
\bm{n} = \left \{
\begin{array}{}
\dfrac{\bm{v}}{\|\bm{v}\|} & (\|\bm{v}\| > \varepsilon) \\
\\
\bm{0} & (\|\bm{v}\| \leq \varepsilon)
\end{array}
\right.

ただし、\varepsilon = 0.00001

2点間の距離(Distanceメソッド)

引数で与えられる2つのベクトル\bm{a}\bm{b}差のベクトル(\bm{a} – \bm{b})長さとなります。

Vector*.Distance()
d = \|\bm{a} - \bm{b}\|

内積(Dotメソッド)

引数で与えられる2つのベクトル\bm{a}\bm{b}各要素の一次結合となります。

Vector2.Dot(a, b)
\bm{a} \cdot \bm{b} = a_x b_x + a_y b_y

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \end{array} \right)

Vector3.Dot(a, b)
\bm{a} \cdot \bm{b} = a_x b_x + a_y b_y + a_z b_z

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \end{array} \right)

Vector4.Dot(a, b)
\bm{a} \cdot \bm{b} = a_x b_x + a_y b_y + a_z b_z + a_w b_w

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \\ a_w \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \\ b_w \end{array} \right)

外積(Crossメソッド)

3次元ベクトルVector3にのみ定義され、次式のように計算されます。

Vector3.Cross(a, b)
\bm{a} \times \bm{b} = \left( \begin{array}{c} a_y b_z - a_z b_y \\ a_z b_x - a_x b_z \\ a_x b_y - a_y b_x \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \end{array} \right)

各成分の乗算(Scaleメソッド)

Scaleメソッドは、次式のように各要素を単純に掛け算したベクトルを返します。

Vector2.Scale(a, b)
scale(\bm{a},\bm{b}) = \left( \begin{array}{c} a_x b_x \\ a_y b_y \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \end{array} \right)

Vector3.Scale(a, b)
scale(\bm{a},\bm{b}) = \left( \begin{array}{c} a_x b_x \\ a_y b_y \\ a_z b_z \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \end{array} \right)

Vector4.Scale(a, b)
scale(\bm{a},\bm{b}) = \left( \begin{array}{c} a_x b_x \\ a_y b_y \\ a_z b_z \\ a_w b_w \end{array} \right)

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \\ a_w \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \\ b_w \end{array} \right)

2つのベクトルのなす角(Angleメソッド)

ベクトル\bm{a}\bm{b}とのなす角\thetaは、ベクトルの内積アークコサインを用いて計算されます。

Vector2.AngleメソッドVector3.Angleメソッドのみ定義され、Vector4.Angleメソッドは存在しません。

Vector2.Angle(a, b)、Vector3.Angle(a, b)
\theta = \left \{
\begin{array}{}
\arccos \dfrac{\bm{a} \cdot \bm{b}}{\|\bm{a}\| \|\bm{b}\|} & (\|\bm{a}\| \|\bm{b}\| \geq \varepsilon) \\
\\
0 & (\|\bm{a}\| \|\bm{b}\| < \varepsilon)
\end{array}
\right.

ただし、\varepsilon = 10 ^ {-15}

上式の\thetaは弧度法表記ですが、Angleメソッドはこれを度数法表記に変換した角度を返します。

2つのベクトルの符号付きのなす角(SignedAngleメソッド)

ベクトルの回転方向を符号として表す角度を返すSignedAngleメソッドは、外積などを用いて回転方向を求めるような計算を行っています。

Vector2.SignedAngle(a, b)
\begin{aligned}
{\theta}_s &= \theta \, \mathrm{sgn}(a_x b_y - a_y b_x) \\
\\
\theta &= \left \{
\begin{array}{}
\arccos \dfrac{\bm{a} \cdot \bm{b}}{\|\bm{a}\| \|\bm{b}\|} & (\|\bm{a}\| \|\bm{b}\| \geq \varepsilon) \\
\\
0 & (\|\bm{a}\| \|\bm{b}\| < \varepsilon)
\end{array}
\right.
\end{aligned}

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \end{array} \right), \varepsilon = 10 ^ {-15}

Vector3.SignedAngleメソッドは、指定された\bm{n}に対してどちら向きに回転するかを計算しています。

Vector3.SignedAngle(a, b, n)
\begin{aligned} {\theta}_s &= \theta \, \mathrm{sgn} \left \{(\bm{a} \times \bm{b}) \cdot \bm{n} \right \} \\
\\
\theta &= \left \{
\begin{array}{}
\arccos \dfrac{\bm{a} \cdot \bm{b}}{\|\bm{a}\| \|\bm{b}\|} & (\|\bm{a}\| \|\bm{b}\| \geq \varepsilon) \\
\\
0 & (\|\bm{a}\| \|\bm{b}\| < \varepsilon)
\end{array}
\right.
\end{aligned}

ただし、

\bm{a} = \left( \begin{array}{c} a_x \\ a_y \\ a_z \end{array} \right), \bm{b} = \left( \begin{array}{c} b_x \\ b_y \\ b_z \end{array} \right), \bm{n} = \left( \begin{array}{c} n_x \\ n_y \\ n_z \end{array} \right), \varepsilon = 10 ^ {-15}

別ベクトルへの投影(Projectメソッド)

あるベクトル\bm{a}を別のベクトル\bm{b}投影したベクトル次式のように計算されています。

Vector3.Project(a, b)
\bm{v} =
\left \{
\begin{array}{}
\dfrac{\bm{a} \cdot \bm{b}}{\|\bm{b}\|^2} \bm{b} & (\|\bm{b}\|^2 \geq \varepsilon) \\
\\
\bm{0} & (\|\bm{b}\|^2 < \varepsilon)
\end{array}
\right.

ただし、\varepsilonは0との差分を持つ最小の値

Vector4.Project(a, b)
\bm{v} = \frac{\bm{a} \cdot \bm{b}}{\|\bm{b}\|^2} \bm{b}

平面への投影(ProjectOnPlaneメソッド)

3次元ベクトルVector3にのみ定義されるProjectOnPlaneメソッドは、次式のように法線ベクトル\bm{n}で定義される平面に投影する計算を行っています。

Vector3.ProjectOnPlane(v, n)
\bm{v'} =
\left \{
\begin{array}{}
\bm{v} - \dfrac{\bm{v} \cdot \bm{n}}{\|\bm{n}\|^2} \bm{n} & (\|\bm{n}\|^2 \geq \varepsilon) \\
\\
\bm{v} & (\|\bm{n}\|^2 < \varepsilon)
\end{array}
\right.

ただし、\varepsilonは0との差分を持つ最小の値

反射(Reflectメソッド)

法線\bm{n}を基準に反射したベクトルを求めるReflectメソッドは、次式のように計算されます。

Vector2.Reflect(v, n)、Vector3.Reflect(v, n)
\bm{v'} = \bm{v} - 2 (\bm{v} \cdot \bm{n}) \bm{n}

ProjectOnPlaneメソッドとは違い、こちらは法線ベクトルが正規化されないため、呼び元で正規化する必要があります。 [1]

目標への移動(MoveTowardsメソッド)

現在位置\bm{p}から目標位置\bm{q}に向かって最大距離\Delta_{max}移動するようなベクトルを求めます。

Vector*.MoveTowards(p, q, Δ)
\bm{p'} =
\left \{
\begin{array}{}
\bm{p} + \dfrac{\Delta_{max}}{\|\bm{q} - \bm{p}\|} (\bm{q} - \bm{p}) & (\|\bm{q}-\bm{p}\| > \Delta_{max}) \\
\\
\bm{q} & (\|\bm{q}-\bm{p}\| \leq \Delta_{max})
\end{array}
\right.

線形補間(Lerp、LerpUnclampedメソッド)

ベクトル\bm{a}\bm{b}t線形補間する式は次のようになります。

Vector*.Lerp(a, b, t)、Vector*.LerpUnclamped(a, b, t)
\bm{v} = (1 - t) \bm{a} + t \bm{b}

Lerpメソッドは計算前にtを0から1の範囲に制限しますが、LerpUnclampedメソッドはこれを行いません。

球面線形補間(Slerp、SlerpUnclampedメソッド)

与えられたベクトル\bm{a}\bm{b}に対し、回転長さをそれぞれt線形補間するような計算が行われます。

Vector3.Slerp(a, b, t)、Vector3.SlerpUnclamped(a, b, t)
\bm{v} = s \left\{ \dfrac{\sin(1-t) \theta}{\sin \theta} \dfrac{\bm{a}}{\|\bm{a}\|} + \dfrac{\sin t \theta}{\sin \theta} \dfrac{\bm{b}}{\|\bm{b}\|} \right\}
s = (1 - t) \|\bm{a}\| + t \|\bm{b}\|
\theta = \arccos \dfrac{\bm{a} \cdot \bm{b}}{\|\bm{a}\| \|\bm{b}\|}

ただし、\|\bm{a}\| \neq 0, \|\bm{b}\| \neq 0

Lerpメソッド同様、Slerpメソッドはtを0から1の範囲に制限しますが、SlerpUnclampedメソッドはこれを行いません。

さいごに

Unity提供のベクトル構造体Vector2、Vector3、Vector4が提供するAPI内部の数式について示しました。

一部割愛したものもありますが、概ね代表的なものを一通り紹介できたかと思います。

Unityでベクトル計算する際は、APIの恩恵により数式をここまで意識しなくても使える場面も多いですが、内部の数式を理解しておくと安心できる場面もあるかもしれません。

GitHub公式ソースとあわせて参考にしていただければと思います。

参考サイト

スポンサーリンク