three.jsで学ぶ行列演算

以前の記事でthree.jsのベクトル演算の実装を調べました。
[three\.jsで学ぶベクトル演算 \- Qiita](https://qiita.com/aa_debdeb/items/c58d5eda9a4052b5dd2f)

今回は行列演算についてまとめながら、three.jsの行列クラスの実装を見ていきたいと思います。

three.jsには3x3行列を表す[Matrix3](https://threejs.org/docs/#api/en/math/Matrix3)クラスと4x4行列を表す[Matrix4](https://threejs.org/docs/#api/en/math/Matrix4)クラスが存在しています。それぞれ[Matrix3.js](https://github.com/mrdoob/three.js/blob/r110/src/math/Matrix3.js)、[Matrix4.js](https://github.com/mrdoob/three.js/blob/r110/src/math/Matrix4.js)で定義されています。

three.jsのバージョンは[r110](https://github.com/mrdoob/three.js/tree/r110)です。

## 3x3行列

### 行列の定義

3x3行列は以下のように3行3列の要素を持つ行列です。

```math
M =
\left(
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right)
```

`Matrix3`は内部的には列優先で値を持つので`elements`の各要素は$[e_{11}, e_{21}, e_{31}, e_{12}, e_{22}, e_{32}, e_{13}, e_{23}, e_{33}]$に対応しています。コンストラクタでは単位行列になるようにしています。

```js
function Matrix3() {

	this.elements = [

		1, 0, 0,
		0, 1, 0,
		0, 0, 1

	];

	if ( arguments.length > 0 ) {

		console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );

	}

}
```

[set](https://threejs.org/docs/#api/en/math/Matrix3.set)メソッドで後から要素の値を任意に変更することができます。`set`メソッドの引数は行優先になっていることに注意してください。

```js
	set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {

		var te = this.elements;

		te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
		te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
		te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;

		return this;

	},
```

### 単位行列

3x3行列の単位行列は次のようになります。

```math
I =
\left(
  \begin{array}{ccc}
    1 & 0 & 0 \\
    0 & 1 & 0 \\
    0 & 0 & 1
  \end{array}
\right)
```

単位行列は任意の行列$A$に対して以下を満たします。

```math
IA = AI = A
```

`Matrix3`では[identity](https://threejs.org/docs/#api/en/math/Matrix3.identity)メソッドで単位行列を作成することができます。

```js
	identity: function () {

		this.set(

			1, 0, 0,
			0, 1, 0,
			0, 0, 1

		);

		return this;

	},
```

### 行列式

3x3行列の行列式は次のようになります。

```math
\begin{eqnarray}
|M| &=&
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right| \\
&=& e_{11}e_{22}e_{33} + e_{12}e_{23}e_{31} + e_{13}e_{21}e_{32} - e_{13}e_{22}e_{31} - e_{12}e_{21}e_{33} - e_{11}e_{23}e_{32}
\end{eqnarray} 
```

`Matrix3`では[determinant](https://threejs.org/docs/#api/en/math/Matrix3.determinant)メソッドで行列式を求めることができます。

```js
	determinant: function () {

		var te = this.elements;

		var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
			d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
			g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];

		return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;

	},
```

### 行列とスカラーの積

行列とスカラーの積は行列の各要素とスカラーの積になります。

```math
aM =
a\left(
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right)
=
\left(
  \begin{array}{ccc}
    ae_{11} & ae_{12} & ae_{13} \\
    ae_{21} & ae_{22} & ae_{23} \\
    ae_{31} & ae_{32} & ae_{33}
  \end{array}
\right)
```

`Matrix3`では[multiplyScalar](https://threejs.org/docs/#api/en/math/Matrix3.multiplyScalar)が対応しています。

```js
	multiplyScalar: function ( s ) {

		var te = this.elements;

		te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
		te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
		te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;

		return this;

	},
```

### 2つの行列の積

2つの行列の積は次のように求めることができ、$AB \neq BA$です。

```math
\begin{eqnarray}
AB &=&
\left(
  \begin{array}{ccc}
    a_{11} & a_{12} & a_{13} \\
    a_{21} & a_{22} & a_{23} \\
    a_{31} & a_{32} & a_{33}
  \end{array}
\right)
\left(
  \begin{array}{ccc}
    b_{11} & b_{12} & b_{13} \\
    b_{21} & b_{22} & b_{23} \\
    b_{31} & b_{32} & b_{33}
  \end{array}
\right)
\\ &=&
\left(
  \begin{array}{ccc}
    a_{11}b_{11} + a_{12}b_{21} + a_{13}b_{31} &
    a_{11}b_{12} + a_{12}b_{22} + a_{13}b_{32} & 
    a_{11}b_{13} + a_{12}b_{23} + a_{13}b_{33} \\
    a_{21}b_{11} + a_{22}b_{21} + a_{23}b_{31} &
    a_{21}b_{12} + a_{22}b_{22} + a_{23}b_{32} & 
    a_{21}b_{13} + a_{22}b_{23} + a_{23}b_{33} \\
    a_{31}b_{11} + a_{32}b_{21} + a_{33}b_{31} &
    a_{31}b_{12} + a_{32}b_{22} + a_{33}b_{32} & 
    a_{31}b_{13} + a_{32}b_{23} + a_{33}b_{33} 
  \end{array}
\right)
\end{eqnarray}
```

`Matrix3`では[multiply](https://threejs.org/docs/#api/en/math/Matrix3.multiply)メソッド、[premultiply](https://threejs.org/docs/#api/en/math/Matrix3.premultiply)メソッド、[multiplyMatrices](https://threejs.org/docs/#api/en/math/Matrix3.multiplyMatrices)メソッドが2つの行列の積に対応しています。
行列の積の順番が結果が変わるので、`multiply`メソッドのほかに`premultiply`メソッドが定義されています。

```js
	multiply: function ( m ) {

		return this.multiplyMatrices( this, m );

	},

	premultiply: function ( m ) {

		return this.multiplyMatrices( m, this );

	},

	multiplyMatrices: function ( a, b ) {

		var ae = a.elements;
		var be = b.elements;
		var te = this.elements;

		var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
		var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
		var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];

		var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
		var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
		var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];

		te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
		te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
		te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;

		te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
		te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
		te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;

		te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
		te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
		te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;

		return this;

	},
```

### 逆行列

以下を満たす$M^{-1}$を$M$の逆行列と呼びます。

```math
MM^{-1} = M^{-1}M = I
```

逆行列$M^{-1}$は余因子行列$\tilde{M}$を用いて以下のように求めることができます。

```math
M^{-1} = \frac{\tilde{M}}{|M|}
```

3x3行列の余因子行列$\tilde{M}$は次のようになります。

```math
\tilde{M} = \left(
  \begin{array}{ccc}
    a_{11} & a_{12} & a_{13} \\
    a_{21} & a_{22} & a_{23} \\
    a_{31} & a_{32} & a_{33}
  \end{array}
\right) \\
a_{11} = 
\left|
  \begin{array}{ccc}
    e_{22} & e_{23} \\
    e_{32} & e_{33}
  \end{array}
\right|,
a_{12} = -
\left|
  \begin{array}{ccc}
    e_{21} & e_{23} \\
    e_{31} & e_{33}
  \end{array}
\right|,
a_{13} =
\left|
  \begin{array}{ccc}
    e_{21} & e_{22} \\
    e_{31} & e_{32}
  \end{array}
\right| \\
a_{21} = -
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} \\
    e_{32} & e_{33}
  \end{array}
\right|,
a_{22} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} \\
    e_{31}& e_{33}
  \end{array}
\right|,
a_{23} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} \\
    e_{31} & e_{32}
  \end{array}
\right| \\
a_{31} =
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} \\
    e_{22} & e_{23}
  \end{array}
\right|,
a_{32} = -
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} \\
    e_{21} & e_{23}
  \end{array}
\right|,
a_{33} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} \\
    e_{21} & e_{22}
  \end{array}
\right|
```

`Matrix3`では[getInverse](https://threejs.org/docs/#api/en/math/Matrix3.getInverse)メソッドで逆行列を計算しています。行列式が0になる場合は逆行列が存在しないので、`if ( det === 0 )`でチェックしています。

```js
	getInverse: function ( matrix, throwOnDegenerate ) {

		if ( matrix && matrix.isMatrix4 ) {

			console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." );

		}

		var me = matrix.elements,
			te = this.elements,

			n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
			n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
			n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],

			t11 = n33 * n22 - n32 * n23,
			t12 = n32 * n13 - n33 * n12,
			t13 = n23 * n12 - n22 * n13,

			det = n11 * t11 + n21 * t12 + n31 * t13;

		if ( det === 0 ) {

			var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0";

			if ( throwOnDegenerate === true ) {

				throw new Error( msg );

			} else {

				console.warn( msg );

			}

			return this.identity();

		}

		var detInv = 1 / det;

		te[ 0 ] = t11 * detInv;
		te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
		te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;

		te[ 3 ] = t12 * detInv;
		te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
		te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;

		te[ 6 ] = t13 * detInv;
		te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
		te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;

		return this;

	},
```

### 転置行列

行列$M$に対して行と列を入れ替えた行列を行列$M$の逆行列$M^T$と呼びます。

```math
M^T =
\left(
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right)^T =
\left(
  \begin{array}{ccc}
    e_{11} & e_{21} & e_{31} \\
    e_{12} & e_{22} & e_{32} \\
    e_{13} & e_{23} & e_{33}
  \end{array}
\right)
```

`Matrix3`では[transpose](https://threejs.org/docs/#api/en/math/Matrix3.transpose)メソッドが対応しています。

```js
	transpose: function () {

		var tmp, m = this.elements;

		tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
		tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
		tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;

		return this;

	},
```

### 法線行列

オブジェクト座標系からワールド座標系に座標変換する行列をモデル行列と呼びます。位置ベクトルはモデル行列で変換可能ですが、法線ベクトルはモデル行列にスケール成分が含まれている場合にうまくいかず、モデル行列の逆行列の転置行列で変換する必要があります。

モデル行列を$M$とすると、その法線行列は$M_N$は以下のようになります。

```math
M_N = (M^{-1})^T
```

`Matrix3`では[getNormalMatrix](https://threejs.org/docs/#api/en/math/Matrix3.getNormalMatrix)が対応しています。

```js
	getNormalMatrix: function ( matrix4 ) {

		return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();

	},
```

## 4x4行列

4x4行列`Matrix4`を見ていきます。

### 行列の定義

4x4行列は以下のように4行4列の要素を持つ行列です。

```math
M = \left(
    \begin{array}{ccc}
      e_{11} & e_{12} & e_{13} & e_{14} \\
      e_{21} & e_{22} & e_{23} & e_{24} \\
      e_{31} & e_{32} & e_{33} & e_{34} \\
      e_{41} & e_{42} & e_{43} & e_{44}
    \end{array}
  \right)
```

`Matrix4`も内部的には列優先で値を持つので`elements`の各要素は$[e_{11}, e_{21}, e_{31}, e_{41}, e_{12}, e_{22}, e_{32}, e_{42}, e_{13}, e_{23}, e_{33}, e_{43}, e_{14}, e_{24}, e_{34}, e_{44}]$に対応しています。

```js
function Matrix4() {

	this.elements = [

		1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0,
		0, 0, 0, 1

	];

	if ( arguments.length > 0 ) {

		console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );

	}

}
```

`Matrix3`同様に[set](https://threejs.org/docs/#api/en/math/Matrix4.set)メソッドの引数は行優先になります。

```js
	set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {

		var te = this.elements;

		te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
		te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
		te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
		te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;

		return this;

	},
```

### 単位行列

4x4行列の単位行列は次のようになります。

```math
I = \left(
    \begin{array}{ccc}
      1 & 0 & 0 & 0 \\
      0 & 1 & 0 & 0 \\
      0 & 0 & 1 & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)
```

`Matrix4`では[identity](https://threejs.org/docs/#api/en/math/Matrix4.identity)メソッドが対応しています。

```js
	identity: function () {

		this.set(

			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1

		);

		return this;

	},
```

### 行列式

4x4行列の行列式を余因子展開で求めると次のようになります。4行目を余因子展開しています。

```math
\begin{eqnarray}
|M| &=&
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} & e_{14} \\
    e_{21} & e_{22} & e_{23} & e_{24} \\
    e_{31} & e_{32} & e_{33} & e_{34} \\
    e_{41} & e_{42} & e_{43} & e_{44}
  \end{array}
\right|
\\ &=&
- e_{41}
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} & e_{14} \\
    e_{22} & e_{23} & e_{24} \\
    e_{32} & e_{33} & e_{34}
  \end{array}
\right|
+ e_{42}
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} & e_{14} \\
    e_{21} & e_{23} & e_{24} \\
    e_{31} & e_{33} & e_{34}
  \end{array}
\right|
- e_{43}
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{14} \\
    e_{21} & e_{22} & e_{24} \\
    e_{31} & e_{32} & e_{34}
  \end{array}
\right|
+ e_{44}
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right|
\end{eqnarray}
```

`Matrix4`では[determinant](https://threejs.org/docs/#api/en/math/Matrix4.determinant)メソッドが対応しています。

```js
	determinant: function () {

		var te = this.elements;

		var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
		var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
		var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
		var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];

		//TODO: make this more efficient
		//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )

		return (
			n41 * (
				+ n14 * n23 * n32
				 - n13 * n24 * n32
				 - n14 * n22 * n33
				 + n12 * n24 * n33
				 + n13 * n22 * n34
				 - n12 * n23 * n34
			) +
			n42 * (
				+ n11 * n23 * n34
				 - n11 * n24 * n33
				 + n14 * n21 * n33
				 - n13 * n21 * n34
				 + n13 * n24 * n31
				 - n14 * n23 * n31
			) +
			n43 * (
				+ n11 * n24 * n32
				 - n11 * n22 * n34
				 - n14 * n21 * n32
				 + n12 * n21 * n34
				 + n14 * n22 * n31
				 - n12 * n24 * n31
			) +
			n44 * (
				- n13 * n22 * n31
				 - n11 * n23 * n32
				 + n11 * n22 * n33
				 + n13 * n21 * n32
				 - n12 * n21 * n33
				 + n12 * n23 * n31
			)

		);

	},
```

### 行列とスカラーの積

行列とスカラーの積は行列の各要素とスカラーの積になります。

```math
aM =
a\left(
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} & e_{14} \\
    e_{21} & e_{22} & e_{23} & e_{24} \\
    e_{31} & e_{32} & e_{33} & e_{34} \\
    e_{41} & e_{42} & e_{43} & e_{44}
  \end{array}
\right)
=
\left(
  \begin{array}{ccc}
    ae_{11} & ae_{12} & ae_{13} & ae_{14} \\
    ae_{21} & ae_{22} & ae_{23} & ae_{24} \\
    ae_{31} & ae_{32} & ae_{33} & ae_{34} \\
    ae_{41} & ae_{42} & ae_{43} & ae_{44}
  \end{array}
\right)
```

`Matrix4`では[multiplyScalar](https://threejs.org/docs/#api/en/math/Matrix4.multiplyScalar)が対応しています。

```js
	multiplyScalar: function ( s ) {

		var te = this.elements;

		te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
		te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
		te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
		te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;

		return this;

	},
```

### 行列と行列の積

2つの4x4行列の積は次のようになります。

```math
\begin{eqnarray}
AB &=&
\left(
  \begin{array}{ccc}
    a_{11} & a_{12} & a_{13} & a_{14} \\
    a_{21} & a_{22} & a_{23} & a_{24} \\
    a_{31} & a_{32} & a_{33} & a_{34} \\
    a_{41} & a_{42} & a_{43} & a_{44}
  \end{array}
\right)
\left(
  \begin{array}{ccc}
    b_{11} & b_{12} & b_{13} & b_{14} \\
    b_{21} & b_{22} & b_{23} & b_{24} \\
    b_{31} & b_{32} & b_{33} & b_{34} \\
    b_{41} & b_{42} & b_{43} & b_{44}
  \end{array}
\right)
\\ &=&
\left(
  \begin{array}{ccc}
    a_{11}b_{11} + a_{12}b_{21} + a_{13}b_{31} + a_{14}b_{41} &
    a_{11}b_{12} + a_{12}b_{22} + a_{13}b_{32} + a_{14}b_{42} & 
    a_{11}b_{13} + a_{12}b_{23} + a_{13}b_{33} + a_{14}b_{43} & 
    a_{11}b_{14} + a_{12}b_{24} + a_{13}b_{34} + a_{14}b_{44} \\
    a_{21}b_{11} + a_{22}b_{21} + a_{23}b_{31} + a_{24}b_{41} &
    a_{21}b_{12} + a_{22}b_{22} + a_{23}b_{32} + a_{24}b_{42} & 
    a_{21}b_{13} + a_{22}b_{23} + a_{23}b_{33} + a_{24}b_{43} & 
    a_{21}b_{14} + a_{22}b_{24} + a_{23}b_{34} + a_{24}b_{44} \\
    a_{31}b_{11} + a_{32}b_{21} + a_{33}b_{31} + a_{34}b_{41} &
    a_{31}b_{12} + a_{32}b_{22} + a_{33}b_{32} + a_{34}b_{42} & 
    a_{31}b_{13} + a_{32}b_{23} + a_{33}b_{33} + a_{34}b_{43} & 
    a_{31}b_{14} + a_{32}b_{24} + a_{33}b_{34} + a_{34}b_{44} \\
    a_{41}b_{11} + a_{42}b_{21} + a_{43}b_{31} + a_{44}b_{41} &
    a_{41}b_{12} + a_{42}b_{22} + a_{43}b_{32} + a_{44}b_{42} & 
    a_{41}b_{13} + a_{42}b_{23} + a_{43}b_{33} + a_{44}b_{43} & 
    a_{41}b_{14} + a_{42}b_{24} + a_{43}b_{34} + a_{44}b_{44}
  \end{array}
\right)
\end{eqnarray}
```

`Matrix3`と同様に`Matrix4`にも[multiply](https://threejs.org/docs/#api/en/math/Matrix4.multiply)メソッド、[premultiply](https://threejs.org/docs/#api/en/math/Matrix4.premultiply)メソッド、[multiplyMatrices](https://threejs.org/docs/#api/en/math/Matrix4.multiplyMatrices)メソッドが存在しています。

```js
	multiply: function ( m, n ) {

		if ( n !== undefined ) {

			console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
			return this.multiplyMatrices( m, n );

		}

		return this.multiplyMatrices( this, m );

	},

	premultiply: function ( m ) {

		return this.multiplyMatrices( m, this );

	},

	multiplyMatrices: function ( a, b ) {

		var ae = a.elements;
		var be = b.elements;
		var te = this.elements;

		var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
		var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
		var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
		var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];

		var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
		var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
		var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
		var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];

		te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
		te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
		te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
		te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

		te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
		te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
		te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
		te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

		te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
		te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
		te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
		te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

		te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
		te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
		te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
		te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

		return this;

	},
```

### 転置行列

4x4行列の転置行列です。

```math
M^T =
\left(
    \begin{array}{ccc}
      e_{11} & e_{12} & e_{13} & e_{14} \\
      e_{21} & e_{22} & e_{23} & e_{24} \\
      e_{31} & e_{32} & e_{33} & e_{34} \\
      e_{41} & e_{42} & e_{43} & e_{44}
    \end{array}
  \right)^T
=
\left(
  \begin{array}{ccc}
    e_{11} & e_{21} & e_{31} & e_{41} \\
    e_{12} & e_{22} & e_{32} & e_{42} \\
    e_{13} & e_{23} & e_{33} & e_{43} \\
    e_{14} & e_{24} & e_{34} & e_{44}
  \end{array}
\right)
```

`Matrix4`の[transpose](https://threejs.org/docs/#api/en/math/Matrix4.transpose)メソッドが対応しています。

```js
	transpose: function () {

		var te = this.elements;
		var tmp;

		tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
		tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
		tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;

		tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
		tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
		tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;

		return this;

	},
```


### 逆行列


4x4行列の逆行列も余因子行列$\tilde{M}$を用いて求めることができます。

```math
M^{-1} = \frac{\tilde{M}}{|M|} \\
\tilde{M} =
\left(
  \begin{array}{ccc}
      a_{11} & a_{12} & a_{13} & a_{14} \\
      a_{21} & a_{22} & a_{23} & a_{24} \\
      a_{31} & a_{32} & a_{33} & a_{34} \\
      a_{41} & a_{42} & a_{43} & a_{44}
  \end{array}
\right)\\
a_{11} = 
\left|
  \begin{array}{ccc}
      e_{22} & e_{23} & e_{24} \\
      e_{32} & e_{33} & e_{34} \\
      e_{42} & e_{43} & e_{44}
  \end{array}
\right|,
a_{12} = -
\left|
  \begin{array}{ccc}
    e_{21} & e_{23} & e_{24} \\
    e_{31} & e_{33} & e_{34} \\
    e_{41} & e_{43} & e_{44}
  \end{array}
\right|,
a_{13} = 
\left|
  \begin{array}{ccc}
    e_{21} & e_{22} & e_{24} \\
    e_{31} & e_{32} & e_{34} \\
    e_{41} & e_{42} & e_{44}
  \end{array}
\right|
a_{14} = -
\left|
  \begin{array}{ccc}
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33} \\
    e_{41} & e_{42} & e_{43}
  \end{array}
\right| \\
a_{21} = -
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} & e_{14} \\
    e_{32} & e_{33} & e_{34} \\
    e_{42} & e_{43} & e_{44}
  \end{array}
\right|,
a_{22} = 
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} & e_{14} \\
    e_{31} & e_{33} & e_{34} \\
    e_{41} & e_{43} & e_{44}
  \end{array}
\right|,
a_{23} = -
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{14} \\
    e_{31} & e_{32} & e_{34} \\
    e_{41} & e_{42} & e_{44}
  \end{array}
\right|
a_{24} = 
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{31} & e_{32} & e_{33} \\
    e_{41} & e_{42} & e_{43}
  \end{array}
\right|\\
a_{31} = 
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} & e_{14} \\
    e_{22} & e_{23} & e_{24} \\
    e_{42} & e_{43} & e_{44}
  \end{array}
\right|,
a_{32} = 
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} & e_{14} \\
    e_{21} & e_{23} & e_{24} \\
    e_{41} & e_{43} & e_{44}
  \end{array}
\right|,
a_{33} = -
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{14} \\
    e_{21} & e_{22} & e_{24} \\
    e_{41} & e_{42} & e_{44}
  \end{array}
\right|,
a_{34} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{41} & e_{42} & e_{43}
  \end{array}
\right|\\
a_{41} = -
\left|
  \begin{array}{ccc}
    e_{12} & e_{13} & e_{14} \\
    e_{22} & e_{23} & e_{24} \\
    e_{32} & e_{33} & e_{34}
  \end{array}
\right|,
a_{42} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{13} & e_{14} \\
    e_{21} & e_{23} & e_{24} \\
    e_{31} & e_{33} & e_{34}
  \end{array}
\right|,
a_{43} = -
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{14} \\
    e_{21} & e_{22} & e_{24} \\
    e_{31} & e_{32} & e_{34}
  \end{array}
\right|,
a_{44} =
\left|
  \begin{array}{ccc}
    e_{11} & e_{12} & e_{13} \\
    e_{21} & e_{22} & e_{23} \\
    e_{31} & e_{32} & e_{33}
  \end{array}
\right|
```

`Matrix4`の[getInverse](https://threejs.org/docs/#api/en/math/Matrix4.getInverse)メソッドが対応しています。

```js
	getInverse: function ( m, throwOnDegenerate ) {

		// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
		var te = this.elements,
			me = m.elements,

			n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
			n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
			n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
			n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],

			t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
			t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
			t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
			t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;

		var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;

		if ( det === 0 ) {

			var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0";

			if ( throwOnDegenerate === true ) {

				throw new Error( msg );

			} else {

				console.warn( msg );

			}

			return this.identity();

		}

		var detInv = 1 / det;

		te[ 0 ] = t11 * detInv;
		te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
		te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
		te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;

		te[ 4 ] = t12 * detInv;
		te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
		te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
		te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;

		te[ 8 ] = t13 * detInv;
		te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
		te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
		te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;

		te[ 12 ] = t14 * detInv;
		te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
		te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
		te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;

		return this;

	},
```


### 平行移動行列

x軸方向に$t_x$、y軸方向に$t_y$、z軸方向に$t_z$だけ平行移動させる行列$T$は以下のようになります。

```math
T = \left(
    \begin{array}{ccc}
      1 & 0 & 0 & t_x \\
      0 & 1 & 0 & t_y \\
      0 & 0 & 1 & t_z \\
      0 & 0 & 0 & 1
    \end{array}
  \right)
```

`Matrix4`では[makeTranslation](https://threejs.org/docs/#api/en/math/Matrix4.makeTranslation)が対応しています。

```js
	makeTranslation: function ( x, y, z ) {

		this.set(

			1, 0, 0, x,
			0, 1, 0, y,
			0, 0, 1, z,
			0, 0, 0, 1

		);

		return this;

	},
```

### 回転行列

x軸対して$\theta_x$だけ回転させる回転行列$R_x$、y軸に対して$\theta_y$だけ回転させる回転行列$R_y$、z軸に対して$\theta_z$だけ回転させる回転行列$R_z$はそれぞれ以下のようになります。

```math
R_x = \left(
    \begin{array}{ccc}
      1 & 0 & 0 & 0 \\
      0 & cos\theta_x & -sin\theta_x & 0 \\
      0 & sin\theta_x & cos\theta_x & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)\\
R_y = \left(
    \begin{array}{ccc}
      cos\theta_y & 0 & sin\theta_y & 0 \\
      0 & 1 & 0 & 0 \\
      -sin\theta_y & 0 & cos\theta_y & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)\\
R_z = \left(
    \begin{array}{ccc}
      cos\theta_z & -sin\theta_z & 0 & 0 \\
      sin\theta_z & cos\theta_z & 0 & 0 \\
      0 & 0 & 1 & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)
```

`Matrix4`では[makeRotationX](https://threejs.org/docs/#api/en/math/Matrix4.makeRotationX)、[makeRotationY](https://threejs.org/docs/#api/en/math/Matrix4.makeRotationY)、[makeRotationZ](https://threejs.org/docs/#api/en/math/Matrix4.makeRotationZ)が対応しています。

```js
	makeRotationX: function ( theta ) {

		var c = Math.cos( theta ), s = Math.sin( theta );

		this.set(

			1, 0, 0, 0,
			0, c, - s, 0,
			0, s, c, 0,
			0, 0, 0, 1

		);

		return this;

	},

	makeRotationY: function ( theta ) {

		var c = Math.cos( theta ), s = Math.sin( theta );

		this.set(

			 c, 0, s, 0,
			 0, 1, 0, 0,
			- s, 0, c, 0,
			 0, 0, 0, 1

		);

		return this;

	},

	makeRotationZ: function ( theta ) {

		var c = Math.cos( theta ), s = Math.sin( theta );

		this.set(

			c, - s, 0, 0,
			s, c, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1

		);

		return this;

	},
```

### 拡大縮小行列

x軸、y軸、z軸に対してそれぞれ$s_x$、$s_y$、$s_z$だけ拡大縮小させる拡大縮小行列$S$は次のようになります。

```math
S = \left(
    \begin{array}{ccc}
      s_x & 0 & 0 & 0 \\
      0 & s_y & 0 & 0 \\
      0 & 0 & s_z & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)
```

`Matrix4`では[makeScale](https://threejs.org/docs/#api/en/math/Matrix4.makeScale)メソッドが対応しています。

```js
	makeScale: function ( x, y, z ) {

		this.set(

			x, 0, 0, 0,
			0, y, 0, 0,
			0, 0, z, 0,
			0, 0, 0, 1

		);

		return this;

	},
```

## 終わりに

行列演算についてまとめながら、three.jsの`Matrix3`、`Matrix4`クラスの実装を見てみました。`Matrix3`クラス、`Matrix4`クラスにはこの記事では触れていないメソッドもあるので注意してください。