13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

three.jsで学ぶ行列演算

Last updated at Posted at 2019-11-10

以前の記事でthree.jsのベクトル演算の実装を調べました。
three.jsで学ぶベクトル演算 - Qiita

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

three.jsには3x3行列を表すMatrix3クラスと4x4行列を表すMatrix4クラスが存在しています。それぞれMatrix3.jsMatrix4.jsで定義されています。

three.jsのバージョンはr110です。

3x3行列

行列の定義

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

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}]$に対応しています。コンストラクタでは単位行列になるようにしています。

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メソッドで後から要素の値を任意に変更することができます。setメソッドの引数は行優先になっていることに注意してください。

	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行列の単位行列は次のようになります。

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

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

IA = AI = A

Matrix3ではidentityメソッドで単位行列を作成することができます。

	identity: function () {

		this.set(

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

		);

		return this;

	},

行列式

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

\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メソッドで行列式を求めることができます。

	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;

	},

行列とスカラーの積

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

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が対応しています。

	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$です。

\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メソッド、premultiplyメソッド、multiplyMatricesメソッドが2つの行列の積に対応しています。
行列の積の順番が結果が変わるので、multiplyメソッドのほかにpremultiplyメソッドが定義されています。

	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$の逆行列と呼びます。

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

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

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

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

\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メソッドで逆行列を計算しています。行列式が0になる場合は逆行列が存在しないので、if ( det === 0 )でチェックしています。

	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$と呼びます。

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メソッドが対応しています。

	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$は以下のようになります。

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

Matrix3ではgetNormalMatrixが対応しています。

	getNormalMatrix: function ( matrix4 ) {

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

	},

4x4行列

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

行列の定義

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

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}]$に対応しています。

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メソッドの引数は行優先になります。

	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行列の単位行列は次のようになります。

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メソッドが対応しています。

	identity: function () {

		this.set(

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

		);

		return this;

	},

行列式

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

\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メソッドが対応しています。

	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
			)

		);

	},

行列とスカラーの積

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

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が対応しています。

	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行列の積は次のようになります。

\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メソッド、premultiplyメソッド、multiplyMatricesメソッドが存在しています。

	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行列の転置行列です。

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)

Matrix4transposeメソッドが対応しています。

	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}$を用いて求めることができます。

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|

Matrix4getInverseメソッドが対応しています。

	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$は以下のようになります。

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が対応しています。

	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$はそれぞれ以下のようになります。

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ではmakeRotationXmakeRotationYmakeRotationZが対応しています。

	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$は次のようになります。

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メソッドが対応しています。

	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のMatrix3Matrix4クラスの実装を見てみました。Matrix3クラス、Matrix4クラスにはこの記事では触れていないメソッドもあるので注意してください。

13
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?