3D空间中的变换矩阵

3D 空间中的变换矩阵详解

在 3D 计算机图形学中,所有几何变换都可以通过 4×4 齐次变换矩阵 来表示。以下详细介绍各种变换矩阵及其原理。

核心变换矩阵

1. 单位矩阵(不变变换)

I=[1000010000100001] I = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} I= 1000010000100001

2. 平移矩阵(Translation)

将点 p=[x,y,z,1]T\mathbf{p} = [x, y, z, 1]^Tp=[x,y,z,1]T 沿向量 t=[tx,ty,tz]\mathbf{t} = [t_x, t_y, t_z]t=[tx,ty,tz] 移动:

T=[100tx010ty001tz0001] T = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} T= 100001000010txtytz1

变换后坐标:
p′=T⋅p=[x+tx,y+ty,z+tz,1]T p' = T \cdot p = [x+t_x, y+t_y, z+t_z, 1]^T p′=T⋅p=[x+tx,y+ty,z+tz,1]T

T逆矩阵:将 (tx,ty,tz)(t_x,t_y,t_z)(tx,ty,tz) 取负

3. 缩放矩阵(Scaling)

沿坐标轴缩放,缩放因子 (sx,sy,sz)(s_x, s_y, s_z)(sx,sy,sz):

S=[sx0000sy0000sz00001] S = \begin{bmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} S= sx0000sy0000sz00001

特殊类型:

  • 均匀缩放 :sx=sy=szs_x = s_y = s_zsx=sy=sz
  • 镜像 :某个分量为负(如 sx=−1s_x = -1sx=−1)
    逆矩阵:使用 (1/sx,1/sy,1/sz)(1/s_x,1/s_y,1/s_z)(1/sx,1/sy,1/sz)(假设因子不为0)。

4. 旋转矩阵(Rotation)

a) 绕坐标轴旋转
旋转轴 矩阵
X轴 Rx(θ)=[10000cos⁡θ−sin⁡θ00sin⁡θcos⁡θ00001]R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta & 0 \\ 0 & \sin\theta & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}Rx(θ)= 10000cosθsinθ00−sinθcosθ00001
Y轴 Ry(θ)=[cos⁡θ0sin⁡θ00100−sin⁡θ0cos⁡θ00001]R_y(\theta) = \begin{bmatrix} \cos\theta & 0 & \sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin\theta & 0 & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}Ry(θ)= cosθ0−sinθ00100sinθ0cosθ00001
Z轴 Rz(θ)=[cos⁡θ−sin⁡θ00sin⁡θcos⁡θ0000100001]R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}Rz(θ)= cosθsinθ00−sinθcosθ0000100001

旋转方向:右手坐标系中,逆时针为正方向(从旋转轴正向观察)

b) 绕任意轴旋转(Rodrigues公式)

绕单位向量 u=[ux,uy,uz]\mathbf{u} = [u_x, u_y, u_z]u=[ux,uy,uz] 旋转角度 θ\thetaθ:

R(u,θ)=[cos⁡θ+ux2(1−cos⁡θ)uxuy(1−cos⁡θ)−uzsin⁡θuxuz(1−cos⁡θ)+uysin⁡θ0uyux(1−cos⁡θ)+uzsin⁡θcos⁡θ+uy2(1−cos⁡θ)uyuz(1−cos⁡θ)−uxsin⁡θ0uzux(1−cos⁡θ)−uysin⁡θuzuy(1−cos⁡θ)+uxsin⁡θcos⁡θ+uz2(1−cos⁡θ)00001] R(\mathbf{u}, \theta) = \begin{bmatrix} \cos\theta + u_x^2(1-\cos\theta) & u_xu_y(1-\cos\theta) - u_z\sin\theta & u_xu_z(1-\cos\theta) + u_y\sin\theta & 0 \\ u_yu_x(1-\cos\theta) + u_z\sin\theta & \cos\theta + u_y^2(1-\cos\theta) & u_yu_z(1-\cos\theta) - u_x\sin\theta & 0 \\ u_zu_x(1-\cos\theta) - u_y\sin\theta & u_zu_y(1-\cos\theta) + u_x\sin\theta & \cos\theta + u_z^2(1-\cos\theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} R(u,θ)= cosθ+ux2(1−cosθ)uyux(1−cosθ)+uzsinθuzux(1−cosθ)−uysinθ0uxuy(1−cosθ)−uzsinθcosθ+uy2(1−cosθ)uzuy(1−cosθ)+uxsinθ0uxuz(1−cosθ)+uysinθuyuz(1−cosθ)−uxsinθcosθ+uz2(1−cosθ)00001

5. 组合变换矩阵

变换矩阵可以通过乘法组合。注意矩阵乘法的顺序非常重要(不满足交换律)。通常,变换按以下顺序应用:缩放 -> 旋转 -> 平移,即:
M=T⋅R⋅S M = T \cdot R \cdot S M=T⋅R⋅S

但是,如果以不同的顺序应用,结果会不同。

例如,先绕Y轴旋转90度,再沿X轴平移3个单位,与先平移后旋转的结果不同:

​​先旋转后平移​​:点会先旋转,然后沿世界坐标系的X轴平移。

​​先平移后旋转​​:点先在世界坐标系中沿X轴平移,然后旋转(此时点的位置已经改变,旋转后位置不同)。

注意顺序

  1. 缩放 (S)
  2. 旋转 ®
  3. 平移 (T)

这是因为矩阵乘法是 右乘结合 :p′=T⋅(R⋅(S⋅p)) \mathbf{p}' = T \cdot (R \cdot (S \cdot \mathbf{p})) p′=T⋅(R⋅(S⋅p))

特殊变换类型

1. 剪切矩阵(Shear)

H=[1hxyhxz0hyx1hyz0hzxhzy100001] H = \begin{bmatrix} 1 & h_{xy} & h_{xz} & 0 \\ h_{yx} & 1 & h_{yz} & 0 \\ h_{zx} & h_{zy} & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} H= 1hyxhzx0hxy1hzy0hxzhyz100001

2. 正交投影矩阵

向XY平面投影:
Portho=[1000010000000001] P_{\text{ortho}} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Portho= 1000010000000001

变换矩阵在编程中的实现

使用GLM(OpenGL Mathematics)库示例(两种写法)

  1. GLM函数叠加运算

    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>

    // 初始化矩阵
    glm::mat4 model = glm::mat4(1.0f); // 单位矩阵

    // 平移:沿X轴移动1.5单位
    model = glm::translate(model, glm::vec3(1.5f, 0.0f, 0.0f));

    // 旋转:绕Z轴旋转90度
    model = glm::rotate(model, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));

    // 缩放:Y轴放大2倍
    model = glm::scale(model, glm::vec3(1.0f, 2.0f, 1.0f));

变换顺序很重要:矩阵乘法从右到左应用变换,所以上面代码的顺序实际上是先缩放,再旋转,最后平移。
在组合变换时,考虑使用逆序:先进行的变换在矩阵乘法中放在右边(后乘),后进行的变换放在左边(先乘)

  1. 计算好各个变换矩阵后再相乘
cpp 复制代码
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>

int main() {
    // 单位矩阵
    glm::mat4 identity = glm::mat4(1.0f);
    
    // 平移变换:沿X轴移动2个单位
    glm::mat4 translation = glm::translate(glm::mat4(1.0), glm::vec3(2.0f, 0.0f, 0.0f));
    
    // 缩放变换:Y轴扩大3倍
    glm::mat4 scaling = glm::scale(glm::mat4(1.0), glm::vec3(1.0f, 3.0f, 1.0f));
    
    // 旋转变换:绕Z轴旋转45度
    glm::mat4 rotation = glm::rotate(glm::mat4(1.0), 
                                    glm::radians(45.0f), 
                                    glm::vec3(0.0f, 0.0f, 1.0f));
    
    // 绕任意轴旋转:绕(1,1,0)旋转30度
    glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 1.0f, 0.0f));
    glm::mat4 customRotation = glm::rotate(glm::mat4(1.0), 
                                         glm::radians(30.0f), 
                                         axis);
    
    // 组合变换:先缩放,再旋转,最后平移
    glm::mat4 composite = translation * rotation * scaling;
    
    // 应用变换到点
    glm::vec4 point(1.0f, 1.0f, 0.0f, 1.0f);
    glm::vec4 transformedPoint = composite * point;
    
    return 0;
}

手动实现矩阵类

cpp 复制代码
class Matrix4f {
public:
    float m[4][4];
    
    Matrix4f() { /* 初始化为单位矩阵 */ }
    
    static Matrix4f translate(float tx, float ty, float tz) {
        Matrix4f m;
        m.m[0][3] = tx;
        m.m[1][3] = ty;
        m.m[2][3] = tz;
        return m;
    }
    
    static Matrix4f scale(float sx, float sy, float sz) {
        Matrix4f m;
        m.m[0][0] = sx;
        m.m[1][1] = sy;
        m.m[2][2] = sz;
        return m;
    }
    
    static Matrix4f rotateX(float angle) {
        float rad = angle * M_PI/180.0;
        float c = cos(rad);
        float s = sin(rad);
        
        Matrix4f m;
        m.m[1][1] = c; m.m[1][2] = -s;
        m.m[2][1] = s; m.m[2][2] = c;
        return m;
    }
    
    Matrix4f operator*(const Matrix4f& right) const {
        // 矩阵乘法实现
    }
    
    Vec4f operator*(const Vec4f& v) const {
        // 矩阵向量乘法
    }
};

变换矩阵的性质与应用

  1. 齐次坐标

    • 3D点:[x, y, z, 1]ᵀ
    • 3D向量:[x, y, z, 0]ᵀ(防止被平移)
  2. 逆变换

    • 平移逆矩阵: T\^{-1}(t_x, t_y, t_z) = T(-t_x, -t_y, -t_z)
    • 缩放逆矩阵: S\^{-1}(s_x, s_y, s_z) = S(1/s_x, 1/s_y, 1/s_z)
    • 旋转逆矩阵: R\^{-1} = R\^T (正交矩阵)
  3. 组合变换求逆

    组合变换的逆矩阵可以通过矩阵求逆得到。如果变换矩阵是正交的(纯旋转,不含缩放或错切),则转置就是逆矩阵。如果包含平移和缩放,则需要计算逆矩阵:

    (T⋅R⋅S)−1=S−1⋅R−1⋅T−1 (T \cdot R \cdot S)^{-1} = S^{-1} \cdot R^{-1} \cdot T^{-1} (T⋅R⋅S)−1=S−1⋅R−1⋅T−1

    其中:

    T −1 :将平移向量取负。

    R −1:旋转矩阵的转置(因为旋转矩阵正交)。

    S −1:缩放因子的倒数(假设非零)。

  4. 在变换链中的位置

    复制代码
    局部坐标 → 模型矩阵 → 世界坐标
                 ↓
    世界坐标 → 视图矩阵 → 观察坐标
                 ↓
    观察坐标 → 投影矩阵 → 裁剪坐标

可视化理解变换顺序

考虑一个点 P(0,1,0) 的变换过程:
原始位置: 0,1,0 缩放: 2倍Y轴 结果: 0,2,0 旋转: 绕Z轴90度 结果: -2,0,0 平移: X轴移动3单位 最终位置: 1,0,0 错误顺序 先平移: 3,1,0 后旋转: -1,3,0 结果不符

正确的矩阵乘法顺序:
Pfinal=[1003010000100001]⏟平移×[0−100100000100001]⏟旋转×[1000020000100001]⏟缩放×[0101] P_{\text{final}} = \underset{\text{平移}}{\underbrace{ \begin{bmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} }} \times \underset{\text{旋转}}{\underbrace{ \begin{bmatrix} 0 & -1 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} }} \times \underset{\text{缩放}}{\underbrace{ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 2 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} }} \times \begin{bmatrix} 0 \\ 1 \\ 0 \\ 1 \end{bmatrix} Pfinal=平移 1000010000103001 ×旋转 0100−100000100001 ×缩放 1000020000100001 × 0101

这些矩阵构成了 3D 图形学的基础,在 OpenGL、DirectX、Vulkan 等图形 API 以及 Unity、Unreal 等游戏引擎中广泛使用。掌握它们对于理解 3D 渲染管线至关重要。

相关推荐
中科米堆8 小时前
中科米堆CASAIM金属件自动3d测量外观尺寸三维检测解决方案
人工智能·3d·视觉检测
山楂树の9 小时前
模型优化——在MacOS 上使用 Python 脚本批量大幅度精简 GLB 模型(通过 Blender 处理)
python·macos·3d·图形渲染·blender
Blossom.11817 小时前
基于深度学习的医学图像分析:使用BERT实现医学文本分类
人工智能·深度学习·机器学习·3d·分类·cnn·bert
渲吧-云渲染19 小时前
材质:3D渲染的隐形支柱
3d·材质
是数学系的小孩儿1 天前
论文:M矩阵
线性代数·矩阵
冲帕Chompa1 天前
今日矩阵系列
数据结构·算法·矩阵
sunbyte1 天前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | 3dBackgroundBoxes(3D背景盒子组件)
前端·javascript·vue.js·3d·vue
xueyongfu1 天前
PTX指令集基础以及warp级矩阵乘累加指令介绍
人工智能·线性代数·算法·矩阵
广州华锐视点1 天前
3D 网上展厅,到底是什么?
3d