以前の記事でthree.jsのベクトル演算の実装を調べました。
three.jsで学ぶベクトル演算 - Qiita
今回は行列演算についてまとめながら、three.jsの行列クラスの実装を見ていきたいと思います。
three.jsには3x3行列を表すMatrix3クラスと4x4行列を表すMatrix4クラスが存在しています。それぞれMatrix3.js、Matrix4.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)
Matrix4
のtransposeメソッドが対応しています。
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|
Matrix4
のgetInverseメソッドが対応しています。
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
ではmakeRotationX、makeRotationY、makeRotationZが対応しています。
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のMatrix3
、Matrix4
クラスの実装を見てみました。Matrix3
クラス、Matrix4
クラスにはこの記事では触れていないメソッドもあるので注意してください。