# 【3D矩阵】

很多意义都与2d矩阵一致,只是比2d矩阵多一个z轴。

内存里数据结构如图所示:

Markdown 图片

# 创建Matrix4x4

public Matrix4x4(Vector4 column0, Vector4 column1, Vector4 column2, Vector4 column3)
{
    this.m00 = column0.x; this.m01 = column1.x; this.m02 = column2.x; this.m03 = column3.x;
    this.m10 = column0.y; this.m11 = column1.y; this.m12 = column2.y; this.m13 = column3.y;
    this.m20 = column0.z; this.m21 = column1.z; this.m22 = column2.z; this.m23 = column3.z;
    this.m30 = column0.w; this.m31 = column1.w; this.m32 = column2.w; this.m33 = column3.w;
}

传参参考下图:

Markdown 图片

# Matrix4x4 相乘

这个操作可以进行多个矩阵的合并。

public static Matrix4x4 operator*(Matrix4x4 lhs, Matrix4x4 rhs)
{
    Matrix4x4 res;
    res.m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20 + lhs.m03 * rhs.m30;
    res.m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21 + lhs.m03 * rhs.m31;
    res.m02 = lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22 + lhs.m03 * rhs.m32;
    res.m03 = lhs.m00 * rhs.m03 + lhs.m01 * rhs.m13 + lhs.m02 * rhs.m23 + lhs.m03 * rhs.m33;

    res.m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20 + lhs.m13 * rhs.m30;
    res.m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21 + lhs.m13 * rhs.m31;
    res.m12 = lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22 + lhs.m13 * rhs.m32;
    res.m13 = lhs.m10 * rhs.m03 + lhs.m11 * rhs.m13 + lhs.m12 * rhs.m23 + lhs.m13 * rhs.m33;

    res.m20 = lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20 + lhs.m23 * rhs.m30;
    res.m21 = lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21 + lhs.m23 * rhs.m31;
    res.m22 = lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22 + lhs.m23 * rhs.m32;
    res.m23 = lhs.m20 * rhs.m03 + lhs.m21 * rhs.m13 + lhs.m22 * rhs.m23 + lhs.m23 * rhs.m33;

    res.m30 = lhs.m30 * rhs.m00 + lhs.m31 * rhs.m10 + lhs.m32 * rhs.m20 + lhs.m33 * rhs.m30;
    res.m31 = lhs.m30 * rhs.m01 + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + lhs.m33 * rhs.m31;
    res.m32 = lhs.m30 * rhs.m02 + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + lhs.m33 * rhs.m32;
    res.m33 = lhs.m30 * rhs.m03 + lhs.m31 * rhs.m13 + lhs.m32 * rhs.m23 + lhs.m33 * rhs.m33;

    return res;
}

# Vector4 进行 Matrix4x4 变换

对一个Vector4进行矩阵变换操作。

public static Vector4 operator*(Matrix4x4 lhs, Vector4 vector)
{
    Vector4 res;
    res.x = lhs.m00 * vector.x + lhs.m01 * vector.y + lhs.m02 * vector.z + lhs.m03 * vector.w;
    res.y = lhs.m10 * vector.x + lhs.m11 * vector.y + lhs.m12 * vector.z + lhs.m13 * vector.w;
    res.z = lhs.m20 * vector.x + lhs.m21 * vector.y + lhs.m22 * vector.z + lhs.m23 * vector.w;
    res.w = lhs.m30 * vector.x + lhs.m31 * vector.y + lhs.m32 * vector.z + lhs.m33 * vector.w;
    return res;
}

缩放和平移如图所示:

Markdown 图片

旋转矩阵如图所示:

Markdown 图片

# 使用4x4矩阵转换坐标系示例

炮塔发射一条射线射中物体后,在物体击中位置的法线方向放置一组盒子形状的顶点。

public void testFunction()
{
    if(Physics.Raycast(headPos,transform.forward,out RaycastHit hit))
    {
        Vector3 hitPos = hit.point;
        Vector3 up = hit.normal;
        Vector3 right = Vector3.Cross(up,lookDir).normalized;
        Vector3 forward = Vector3.Cross(right,up);

        Quaternion turretRot = Quaternion.LookRotation(forward,up);
        Matrix4x4 turretToWorld = Matrix4x4.TRS(hitPos,turretRot,Vector3.one);
        Matrix4x4 worldToTurret = turretToWorld.inverse;//逆矩阵

        // 测试用盒子形状顶点数据
        Vector3[] pts = new Vector3[] {
            new Vector3(1,0,1),
            new Vector3(-1,0,1),
            new Vector3(-1,0,-1),
            new Vector3(1,0,-1),
            new Vector3(1,2,1),
            new Vector3(-1,2,1),
            new Vector3(-1,2,-1),
            new Vector3(1,2,-1)
        }
        
        Gizmos.color = Color.red;
        for(int i = 0; i < pts.Length;i++)
        {
            // 把顶点局部坐标转换成基于击中位置的世界坐标
            Vector3 worldPt = turretToWorld.MultiplyPoint3x4(pts[i]);
            Gizmos.DrawSphere(worldPt,0.075f);
        }
    }
}

# 平移旋转缩放矩阵

mat4 setScale( float x, float y, float z )
{
    return mat4( x, 0.0, 0.0, 0.0,
                 0.0, y, 0.0, 0.0,
                 0.0, 0.0, z, 0.0,
                 0.0, 0.0, 0.0, 1.0 );
}

mat4 setRotation( float x, float y, float z )
{
    float a = sin(x); float b = cos(x); 
    float c = sin(y); float d = cos(y); 
    float e = sin(z); float f = cos(z); 

    float ac = a*c;
    float bc = b*c;

    return mat4( d*f,      d*e,       -c, 0.0,
                 ac*f-b*e, ac*e+b*f, a*d, 0.0,
                 bc*f+a*e, bc*e-a*f, b*d, 0.0,
                 0.0,      0.0,      0.0, 1.0 );
}

mat4 setTranslation( float x, float y, float z )
{
    return mat4( 1.0, 0.0, 0.0, 0.0,
                 0.0, 1.0, 0.0, 0.0,
                 0.0, 0.0, 1.0, 0.0,
                 x,     y,   z, 1.0 );
}

// 使用例子
void example()
{
    // 这个变换是先旋转再平移。具体顺序是:
    // 1.先绕X轴旋转-0.2弧度
    // 2.再绕Y轴旋转(20.0 + 0.2iTime)弧度
    // 3.最后绕Z轴旋转0.1弧度
    // 4.最后将旋转后的物体平移到(0.0, -0.5, -5.0)的位置
    mat4 mdv = setTranslation( 0.0, -0.5, -5.0 ) * setRotation( -0.2, 20.0+0.2*iTime, 0.1 );

    vec4 p1 = vec4();
    // 使用矩阵转换坐标系
    vec4 p2 = mdv * p1;
}