デジタル・デザイン・ラボラトリーな日々

アラフィフプログラマーが数学と物理を基礎からやり直す。https://qiita.com/yaju

3Dを基礎から勉強する 行列の導入

このブログの最初の記事が2011/12/30の「3Dを基礎から勉強する」の記事でした。3Dを理解する前に2Dを理解しないとねってことで月日が経ってしまったわけですが、2Dの理解も深まったので、ようやく3Dの勉強に本格的に入ることにしました。

再度、「てっく煮ブログ」の下記の記事を参考にFlash版をJavascriptに移植してみました。

AS3.0 で 3D プログラミングを1から勉強する (2) - 行列の導入
AS3.0 で 3D プログラミングを1から勉強する (3) - 透視投影


以前の記事である「行列による2Dアフィン変換を理解してみる」にて行列の有効性の理解した、今度は3D版の行列である。

3Dの行列は下記の通り、2Dの場合は3x3行列でしたが3Dの場合は4x4行列となります。

 回転行列 
  X軸周りの回転    Y軸周りの回転         Z軸周りの回転         
 | 1 0    0  0 |  |  cos  0  sin 0 |  | cos  -sin  0 0 |
 | 0 cos -sin 0 |  |   0   1   0   0 |  | sin   cos  0 0 |  
 | 0 sin  cos 0 |  | -sin  0  cos  0 |  |   0   0    1  0 |  
 | 0  0    0  1 |  |   0   0   0   1 |  |   0   0    0  1 | 

 拡大縮小行列        平行移動行列     
 | sx 0    0 0 |  | 1  0   0 tx | 
 | 0 sy   0 0 |  | 0  1   0 ty |  
 | 0 0   sx 0 |  | 0  0   1 tz |  
 | 0 0    0 1 |  | 0 0   0  1 | 

参考:回転行列、拡大縮小行列、平行移動行列(三次元座標の場合)
http://imagingsolution.net/math/rotation-scaling-translation-3d-matrix/

行列による3Dアフィン変換のソースリスト

var Matrix3D = (function () {
    // | m00 m01 m02 tx |
    // | m10 m11 m12 ty |
    // | m20 m21 m22 tz |

    function Matrix3D() {
        this.m00 = this.m11 = this.m22 = 1;
        this.m10 = this.m20 = this.m01 = this.m21 = this.m02 = this.m12 = this.tx = this.ty = this.tz = 0;
    }
    
    Matrix3D.prototype.set = function (mx) {
        this.m00 = mx.m00;
        this.m01 = mx.m01;
        this.m02 = mx.m02;
        this.tx = mx.tx;
        this.m10 = mx.m10;
        this.m11 = mx.m11;
        this.m12 = mx.m12;
        this.ty = mx.ty;
        this.m20 = mx.m20;
        this.m21 = mx.m21;
        this.m22 = mx.m22;
        this.tz = mx.tz;
        return this;
    };
    
    Matrix3D.prototype.multi = function (mx) {
        var wx, wy, wz;
        wx = this.m00;
        wy = this.m01;
        wz = this.m02;
        this.m00 = wx * mx.m00 + wy * mx.m10 + wz * mx.m20;
        this.m01 = wx * mx.m01 + wy * mx.m11 + wz * mx.m21;
        this.m02 = wx * mx.m02 + wy * mx.m12 + wz * mx.m22;
        this.tx = wx * mx.tx + wy * mx.ty + wz * mx.ty + this.tx;
        wx = this.m10;
        wy = this.m11;
        wz = this.m12;
        this.m10 = wx * mx.m00 + wy * mx.m10 + wz * mx.m20;
        this.m11 = wx * mx.m01 + wy * mx.m11 + wz * mx.m21;
        this.m12 = wx * mx.m02 + wy * mx.m12 + wz * mx.m22;
        this.ty = wx * mx.tx + wy * mx.ty + wz * mx.tz + this.ty;
        wx = this.m20;
        wy = this.m21;
        wz = this.m22;
        this.m20 = wx * mx.m00 + wy * mx.m10 + wz * mx.m20;
        this.m21 = wx * mx.m01 + wy * mx.m11 + wz * mx.m21;
        this.m22 = wx * mx.m02 + wy * mx.m12 + wz * mx.m22;
        this.tz = wx * mx.tx + wy * mx.ty + wz * mx.tz + this.tz;
        return this;
    };
    
    Matrix3D.prototype.rotateX = function (rad) {
        var mx = new Matrix3D();
        mx.m11 = Math.cos(rad);
        mx.m12 = -Math.sin(rad);
        mx.m21 = Math.sin(rad);
        mx.m22 = Math.cos(rad);
        return this.multi(mx);
    };
    
    Matrix3D.prototype.rotateY = function (rad) {
        var mx = new Matrix3D();
        mx.m00 = Math.cos(rad);
        mx.m02 = Math.sin(rad);
        mx.m20 = -Math.sin(rad);
        mx.m22 = Math.cos(rad);
        return this.multi(mx);
    };
    
    Matrix3D.prototype.rotateZ = function (rad) {
        var mx = new Matrix3D();
        mx.m00 = Math.cos(rad);
        mx.m01 = -Math.sin(rad);
        mx.m10 = Math.sin(rad);
        mx.m11 = Math.cos(rad);
        return this.multi(mx);
    };
    
    Matrix3D.prototype.scale = function (sx, sy, sz) {
        var mx = new Matrix3D();
        mx.m00 = sx;
        mx.m11 = sy;
        mx.m22 = sz;
        return this.multi(mx);
    };
    
    Matrix3D.prototype.translate = function (tx, ty, tz) {
        this.tx = tx;
        this.ty = ty;
        this.tz = tz;
    };
    
    Matrix3D.prototype.transformPoint = function (p) {
        return new Point3D(this.m00 * p.x + this.m01 * p.y + this.m02 * p.z + this.tx, this.m10 * p.x + this.m11 * p.y + this.m12 * p.z + this.ty, this.m20 * p.x + this.m21 * p.y + this.m22 * p.z + this.tz);
    };
    
    return Matrix3D;
})();

今回、行列をクラス化した際に4x4行列ではなく4x3行列にしています。
これは先程の各行列を見ると最下行がどれも|0 0 0 1|と固定となっており、0に対して値を掛け算しても値は0になるため、無駄な計算を少し省いた感じです。

今回はここまでとして、次はもう少し中身を追っていきます。