👋

UnityでMatrix4x4を理解する

に公開

行列は何のためにあり、TRSはどう効くのか

Unityで3Dを触っていると、Matrix4x4localToWorldMatrix といった“行列”の存在に必ず出会います。
Transformで普段何気なくやっている「移動・回転・拡縮」や「親子階層の合成」「カメラ投影」などの裏側は、すべて行列で処理されています。

本記事では、

  • そもそも行列は何のために使われるのか
  • Unity / CGの中でどんな場面で登場するのか
  • TRSとは何で、行列の中でどう振る舞うのか
  • Unityでの基本的な使い方と、挙動確認の実験

を順番に整理していきます。


1. 行列の一番コアな役割:「変換」をまとめて扱うため

行列が強いのは、何かを別の状態へ“変換”するときです。

3D/Unityでの例

  • 点やベクトルを動かす

    • 位置の移動(平行移動)
    • 回転
    • 拡大縮小
  • 座標系そのものを切り替える

    • ローカル座標 → ワールド座標
    • ワールド座標 → カメラ座標
    • カメラ座標 → 画面座標(投影)

Transformが当たり前にやっている「位置・回転・拡縮」と「座標系変換」を、
数学的に一発で表してまとめられる形式が “行列” です。


2. いつ使われる?(Unity/CG文脈)

A. オブジェクトの姿勢を決めるとき

transform.position / rotation / scale の裏側は行列で合成されています。

  • 子オブジェクトが親の動きについていく
    親の行列 × 子の行列 によってワールド姿勢が決まる

Transform階層が自然に「合成」できるのは、行列の掛け算があるからです。


B. カメラで“見える世界”を作るとき

カメラはこういう行列を持っています:

  • View行列:ワールド → カメラの視点へ変換
  • Projection行列:3D → 2Dへ投影

Unityのレンダリングは毎フレーム、

モデル行列(オブジェクト)
× ビュー行列(カメラ)
× プロジェクション行列(投影)

をGPUで大量に回しています。


C. たくさんの頂点を超高速に処理するとき

メッシュの頂点を1個ずつ position/rotation/scale で処理するより、
行列1個にまとめて、頂点すべてに同じ掛け算をする方が圧倒的に速いです。

GPUは「行列 × ベクトル」の並列計算が超得意なので、
行列はレンダリングのパイプラインにぴったり噛み合います。


3. Unity以外の“行列が自然に出る場面”

行列は「変換」だけでなく、複数の数値の関係を整理したいときにも現れます。

A. 物理・シミュレーション

  • 剛体の回転
  • 慣性テンソル
  • バネの連立方程式
    多変数の関係を一気に解く形式として行列が自然に出る

B. 画像処理

  • 画像の回転・拡大縮小・歪み補正
  • フィルタ(ぼかし、シャープなど)
    → ピクセルの集合を変換するので行列で書ける

C. 機械学習・統計

  • ニューラルネットの重み
  • 多次元データの変換(PCAなど)
    大量の数値の「関係」を一つの“箱”として扱う

4. なぜ行列を使うと便利なのか?

行列の“得”は次の3つに尽きます。

① 変換を合成できる

「回転→移動→拡大縮小」みたいな複数ステップを

行列の掛け算1回で合成して1個にできる

Transform階層やカメラ処理の根幹です。


② 同じ変換を大量の点に一括適用できる

頂点100万個でも

行列 × ベクトル(点)を並列で回すだけ

GPUフレンドリーで高速。


③ 「座標系を変える」問題を統一的に扱える

ローカル→ワールド、ワールド→カメラ、など
全部同じ形式(行列)で扱えるため、パイプライン化しやすい。


5. 今の段階の「行列=何?」の固定イメージ

まずはこれだけ持っておけばOKです。

行列は 「空間やデータの変換ルールを一つにまとめたもの」
それを 掛け算で合成し、点やデータに一括で適用するために使う。

Unityの Matrix4x4 はその“変換ルール”の典型例です。


TRSとは何か?

TRS は、3D変換で最も基本になる “3つの要素” の略です。

T = Translation(平行移動 / 位置)
R = Rotation(回転)
S = Scale(拡大縮小)

UnityのTransformが持つ
position / rotation / scale をそのまま行列にまとめたものが TRS行列です。


TRSを「変換ルール」として見ると

TRS行列はこういうルールを意味します。

  1. **S(拡縮)**する
  2. **R(回転)**する
  3. **T(移動)**する

この3つを 1つの行列に圧縮して、点に一発適用できるようにしたものです。

Unityではこうやって作ります。

Matrix4x4 m = Matrix4x4.TRS(position, rotation, scale);

Unityでの直感的な対応

Transformと完全に対応しています。

Transform tr = transform;

// TransformのTRSが詰まった行列
Matrix4x4 m = Matrix4x4.TRS(tr.position, tr.rotation, tr.localScale);

// ほぼ同じものがTransformから直接取れる
Matrix4x4 localToWorld = tr.localToWorldMatrix;
  • Matrix4x4.TRS(...)
    「この position/rotation/scale で変換してね」行列を生成

  • transform.localToWorldMatrix
    Transformが実際に使ってるTRS合成結果


ちょっと大事な話:行列内での順序

UnityのTRS行列は内部的に

M = T × R × S

の形でまとまっています。

p を変換するときは

p' = M × p = (T × R × S) × p

行列は 右側に書いた変換が先に効くルールがあるので、
実際の適用順は

S → R → T

になります。

つまり、

  • まずサイズを変えて
  • 次に回して
  • 最後に移動する

というTransformの直感と一致するように設計されています。


簡単な実験コード(TRSの効き方を確認)

using UnityEngine;

public class TRSExample : MonoBehaviour
{
    void Start()
    {
        Vector3 pos = new Vector3(3, 0, 0);                 // T
        Quaternion rot = Quaternion.Euler(0, 90, 0);        // R
        Vector3 scl = new Vector3(2, 2, 2);                 // S

        Matrix4x4 m = Matrix4x4.TRS(pos, rot, scl);

        Vector3 p = new Vector3(1, 0, 0);

        Vector3 p2 = m.MultiplyPoint3x4(p);

        Debug.Log("original: " + p);
        Debug.Log("after TRS: " + p2);
    }
}

期待される変化(頭の中の形)

  • (1,0,0) を 2倍 → (2,0,0)
  • それを Y軸90°回転 → (0,0,-2)
  • それを +3移動 → (3,0,-2)

というように、S→R→T の順で効くことが観察できます。


UnityでMatrix4x4を使う基本

ここからは、実際にUnity上で「行列がどう振る舞うか」を最低限固めます。


(A) TRS行列を作る

using UnityEngine;

public class MatrixBasicTRS : MonoBehaviour
{
    void Start()
    {
        Vector3 position = new Vector3(3f, 0f, 0f);
        Quaternion rotation = Quaternion.Euler(0f, 45f, 0f);
        Vector3 scale = new Vector3(2f, 2f, 2f);

        Matrix4x4 m = Matrix4x4.TRS(position, rotation, scale);

        Debug.Log("TRS Matrix:\n" + m);
    }
}

(B) 行列で点と方向を変換する

  • 点(位置)に使う:MultiplyPoint3x4
  • 方向ベクトルに使う:MultiplyVector
using UnityEngine;

public class MatrixBasicTransformPoint : MonoBehaviour
{
    void Start()
    {
        Matrix4x4 m = Matrix4x4.TRS(
            new Vector3(3f, 0f, 0f),
            Quaternion.Euler(0f, 45f, 0f),
            new Vector3(2f, 2f, 2f)
        );

        Vector3 p = new Vector3(1f, 0f, 0f);

        Vector3 pTransformed = m.MultiplyPoint3x4(p);
        Debug.Log("original point: " + p);
        Debug.Log("transformed point: " + pTransformed);

        Vector3 v = new Vector3(1f, 0f, 0f);
        Vector3 vTransformed = m.MultiplyVector(v);
        Debug.Log("original vector: " + v);
        Debug.Log("transformed vector: " + vTransformed);
    }
}

観察ポイント

  • MultiplyPoint3x4平行移動も含めて変換する
  • MultiplyVector平行移動を無視して回転と拡縮だけ受ける
    → 点は w=1、方向は w=0 の違いがここに出ます。

ミニ実験:平行移動が点だけに効くことを確認

using UnityEngine;

public class MatrixExperimentTranslationOnly : MonoBehaviour
{
    void Start()
    {
        Matrix4x4 t = Matrix4x4.Translate(new Vector3(10f, 0f, 0f));

        Vector3 p = new Vector3(1f, 2f, 3f);
        Vector3 v = new Vector3(1f, 2f, 3f);

        Debug.Log("Point before: " + p);
        Debug.Log("Point after : " + t.MultiplyPoint3x4(p));

        Debug.Log("Vector before: " + v);
        Debug.Log("Vector after : " + t.MultiplyVector(v));
    }
}

期待される振る舞い

  • 点は (x+10, y, z) になる
  • ベクトルは変わらない

「方向」には“どこにあるか”は関係ない、という直感と一致します。


ミニ実験:行列の掛け算順序で結果が変わることを確認

using UnityEngine;

public class MatrixExperimentOrder : MonoBehaviour
{
    void Start()
    {
        Matrix4x4 T = Matrix4x4.Translate(new Vector3(3f, 0f, 0f));
        Matrix4x4 R = Matrix4x4.Rotate(Quaternion.Euler(0f, 90f, 0f));

        Vector3 p = new Vector3(1f, 0f, 0f);

        Vector3 p1 = (T * R).MultiplyPoint3x4(p);
        Vector3 p2 = (R * T).MultiplyPoint3x4(p);

        Debug.Log("(T * R) result: " + p1);
        Debug.Log("(R * T) result: " + p2);
    }
}

形骸化すると

  • T * R は「先にR、後にT」が点に適用
  • R * T は「先にT、後にR」が点に適用

つまり

行列を右に書いた変換が先に効き、左に書いた変換が後に効く

という一般ルールがここで体感できます。


TransformとMatrix4x4の対応

using UnityEngine;

public class MatrixFromTransform : MonoBehaviour
{
    void Start()
    {
        Matrix4x4 localToWorld = transform.localToWorldMatrix;
        Matrix4x4 worldToLocal = transform.worldToLocalMatrix;

        Debug.Log("localToWorld:\n" + localToWorld);
        Debug.Log("worldToLocal:\n" + worldToLocal);

        Vector3 localPoint = new Vector3(1f, 0f, 0f);
        Vector3 worldPoint = localToWorld.MultiplyPoint3x4(localPoint);

        Debug.Log("local point : " + localPoint);
        Debug.Log("world point : " + worldPoint);

        Vector3 backToLocal = worldToLocal.MultiplyPoint3x4(worldPoint);
        Debug.Log("back to local: " + backToLocal);
    }
}
  • localToWorldMatrixローカル → ワールド変換ルール
  • worldToLocalMatrix はその逆変換(ほぼ逆行列)

まとめ

本記事の要点は次の通りです。

  • 行列は 「変換ルールを一つにまとめる仕組み」
  • Unityでは移動・回転・拡縮や座標系変換を 行列として合成・適用している
  • TRS = Translation / Rotation / Scale
  • TRS行列は内部的に T×R×S だが、点への適用順は S→R→T
  • MultiplyPoint3x4MultiplyVector の違いで
    「点は移動を受ける / 方向は受けない」が体感できる

Discussion