矩阵与四元数常见问题解答
摘要
一份参考性常见问题解答文档,涵盖矩阵和四元数,主要针对3D图形和游戏开发,由多位作者修正和贡献。
暂无内容
查看缓存全文
缓存时间: 2026/06/23 13:43
# 矩阵与四元数常见问题解答
来源:https://j3d.org/matrix_faq/matrfaq_latest.html
``
矩阵与四元数常见问题解答
==============================
版本 1.21 2003年11月30日
-------------------------------
请将反馈邮件发送至 [email protected]
邮件主题请以 MATRIX-FAQ 开头
(否则我的垃圾邮件过滤器将直接删除您的邮件)。
欢迎任何补充建议或相关问题。
只需将邮件发送至上述地址即可。
本 FAQ 的最新版本可在以下网页找到:
http://www.j3d.org/matrix_faq/matrfaq_latest.html
可随意分发或复制本 FAQ。
贡献者
-------------
Introduction I1: Steve ?????
Q55 至 Q59 的修正:
Andreas Junghanns
Q50 的修正:Morten Ofstad
Q39 的备注:Tom Nuydens
Q29 和 Q37 的修正:Eric Reiss
Q56 的澄清:Duncan Murdoch
Q37 的澄清:Ron Avitzur
Q1 的修正:Mona Wong
Q36 和 Q37 的修正:Eric Reiss
Q34 和 Q38 的改进:Jon Watte
Q58 和 Q59 的警告与替代方案:Paul Pedriana
Q53 的修正(及优化 [Lee]):Eleanor Groundwater 和 Lee Morgan
Q39 的改进:jhunpingco
Q11 的修正及 Q12 的优化:Gordon
Q54 至 Q60 的修正:Eleanor Groundwater
Q23 和 Q24 的修正与改进:Ben Houston
Q39 的补充:Jon Watte
Q61 的修正:Adam D. Moss
Q63 的补充:Mike Cline
I2 的补充:Jacob Marner
Q38 的修正及 I2 的提出:Armin M�ller
Q60 的补充:Pablo Figueroa
Q14、Q16、Q21 和 Q34 的修正与补充:Tronster Hartley
Q12 和 Q54 的修正:Frank DJ
Q34 的修正:Robert Funnell
历史
-------
我(Andreas)曾试图寻找 "hexapod@(no-spam)netcom.com" 这位似乎维护过本 FAQ 一段时间的人,但 netcom.com 上的站点已不复存在,邮件也被退回。由于我(和同事们)在理解本文档早期版本中某些算法错误上浪费了不少时间,我决定修正它并重新发布到网上。
之前给出的文档位置已不存在:
ftp://ftp.netcom.com/pub/he/hexapod/index.html
http://www.glue.umd.edu/~rsrodger
版本、日期及本地副本链接(供对比):
matrfaq_1.02.html: Version 1.2 1997年9月2日
matrfaq_1.04.html: Version 1.4 1998年12月26日
matrfaq_1.06.html: Version 1.6 2000年9月30日
matrfaq_1.07.html: Version 1.7 2000年12月20日
matrfaq_1.08.html: Version 1.8 2000年12月21日
matrfaq_1.09.html: Version 1.9 2001年1月16日
matrfaq_1.10.html: Version 1.10 2001年1月30日
matrfaq_1.11.html: Version 1.11 2001年2月9日
matrfaq_1.12.html: Version 1.12 2001年3月26日
matrfaq_1.13.html: Version 1.13 2001年7月20日
matrfaq_1.14.html: Version 1.14 2001年8月17日
matrfaq_1.15.html: Version 1.15 2001年8月20日
matrfaq_1.16.html: Version 1.16 2001年10月2日
matrfaq_1.17.html: Version 1.17 2001年11月30日
matrfaq_1.18.html: Version 1.18 2002年1月27日
matrfaq_1.19.html: Version 1.19 2002年3月20日
matrfaq_1.20.html: Version 1.20 2002年1月31日
matrfaq_1.21.html: Version 1.21 2003年11月30日
请不要向我询问数学问题。我只是维护本 FAQ,对相关知识了解甚少。但如果你有本 FAQ 未解答的问题,而后又找到了答案,并认为该答案与本 FAQ(或其读者)相关,请将相关信息(最好已整理好)发送给我,以便收录于此。谢谢!
如果你希望以"匿名"身份出现在贡献者列表中,请告知我,否则我会根据你的邮件头信息使用你的名称。
引言
------------
I1. 关于 OpenGL 与本文档的重要说明
I2. 关于标准化输入的重要说明
问题
---------
基础篇
======
Q1. 什么是矩阵?
Q2. 矩阵的"阶"是什么?
Q3. 如何使用 C/C++ 编程语言表示矩阵?
Q4. 使用矩阵有哪些优势?
Q5. 矩阵与坐标系有何关系?
算术篇
==========
Q6. 什么是单位矩阵?
Q7. 什么是矩阵的主对角线?
Q8. 什么是矩阵的转置?
Q9. 如何将两个矩阵相加?
Q10. 如何将两个矩阵相减?
Q11. 如何将两个矩阵相乘?
Q12. 如何计算矩阵的平方或幂?
Q13. 如何将一个或多个向量与矩阵相乘?
行列式与逆矩阵
=========================
Q14. 什么是矩阵的行列式?
Q15. 如何计算矩阵的行列式?
Q16. 什么是各向同性与各向异性矩阵?
Q17. 什么是矩阵的逆?
Q18. 如何计算任意矩阵的逆?
Q19. 如何计算单位矩阵的逆?
Q20. 如何计算旋转矩阵的逆?
Q21. 如何使用克莱姆法则计算矩阵的逆?
Q22. 如何计算 2x2 矩阵的逆?
Q23. 如何计算 3x3 矩阵的逆?
Q24. 如何计算 4x4 矩阵的逆?
Q25. 如何使用线性方程组计算矩阵的逆?
变换篇
==========
Q26. 什么是旋转矩阵?
Q27. 旋转矩阵与坐标系有何关系?
Q28. 如何生成绕 X 轴的旋转矩阵?
Q29. 如何生成绕 Y 轴的旋转矩阵?
Q30. 如何生成绕 Z 轴的旋转矩阵?
Q31. 什么是欧拉角?
Q32. 什么是偏航、翻滚和俯仰?
Q33. 如何组合旋转矩阵?
Q34. 什么是万向锁?
Q35. 组合旋转矩阵的正确方法是什么?
Q36. 如何从欧拉角生成旋转矩阵?
Q37. 如何从旋转矩阵生成欧拉角?
Q38. 如何生成绕指定轴和角度的旋转矩阵?
Q39. 如何生成将一个向量映射到另一个向量的旋转矩阵?
Q40. 如何使用矩阵在两个坐标系之间转换?
Q41. 什么是平移矩阵?
Q42. 什么是缩放矩阵?
Q43. 什么是剪切矩阵?
Q44. 如何在两个矩阵之间进行线性插值?
Q45. 如何在四个矩阵之间进行三次插值?
Q46. 如何渲染一个矩阵?
四元数篇
===========
Q47. 什么是四元数?
Q48. 四元数与 3D 动画有何关系?
Q49. 如何计算四元数的共轭?
Q50. 如何计算四元数的逆?
Q51. 如何计算四元数的模?
Q52. 如何归一化四元数?
Q53. 如何将两个四元数相乘?
Q54. 如何将四元数转换为旋转矩阵?
Q55. 如何将旋转矩阵转换为四元数?
Q56. 如何将旋转轴和角度转换为四元数?
Q57. 如何将四元数转换为旋转轴和角度?
Q58. 如何将球面旋转角转换为四元数?
Q59. 如何将四元数转换为球面旋转角?
Q60. 如何将欧拉旋转角转换为四元数?
Q61. 如何使用四元数在矩阵之间进行线性插值?
Q62. 如何使用四元数在矩阵之间进行三次插值?
Q63. 如何使用四元数旋转一个向量?
引言
------------
I1. 关于 OpenGL 与本文档的重要说明
-------------------------------------------------------
在本文档中(与大多数数学教科书一样),所有矩阵均按照标准数学方式绘制。遗憾的是,像 IrisGL、OpenGL 和 SGI 的 Performer 等图形库都将其行和列进行了交换。
因此,在本文档中,你将看到(例如)一个 4x4 平移矩阵表示如下:
| 1 0 0 X |
| |
| 0 1 0 Y |
M = | |
| 0 0 1 Z |
| |
| 0 0 0 1 |
而在 Performer(例如)中,它会被填充如下:
M[0][1] = M[0][2] = M[0][3] =
M[1][0] = M[1][2] = M[1][3] =
M[2][0] = M[2][1] = M[2][3] = 0 ;
M[0][0] = M[1][1] = M[2][2] = m[3][3] = 1 ;
M[3][0] = X ;
M[3][1] = Y ;
M[3][2] = Z ;
即,矩阵的存储方式如下:
| M[0][0] M[1][0] M[2][0] M[3][0] |
| |
| M[0][1] M[1][1] M[2][1] M[3][1] |
M = | |
| M[0][2] M[1][2] M[2][2] M[3][2] |
| |
| M[0][3] M[1][3] M[2][3] M[3][3] |
OpenGL 使用一维数组存储矩阵——但幸运的是,其打包顺序导致内存中的字节布局相同——因此,取一个 pfMatrix 的地址并将其强制转换为 float*,就可以直接传递给 glLoadMatrixf 等例程。
在本文档各处散布的代码片段中,使用一维数组存储矩阵。数组元素的顺序相对于 OpenGL 是转置的。
本文档 OpenGL
| 0 1 2 3 | | 0 4 8 12 |
| | | |
| 4 5 6 7 | | 1 5 9 13 |
M = | | M = | |
| 8 9 10 11 | | 2 6 10 14 |
| | | |
| 12 13 14 15 | | 3 7 11 15 |
I2. 关于标准化输入的重要说明
----------------------------------------------------
请注意,大多数算法假定输入已标准化,例如单位长度的向量、主对角线已归一化的矩阵等。有时,算法(以及此处提供的代码片段)确实能在任意输入下正确工作,但依赖这一特性通常被认为是不好的实践(如果你未能遵循此建议,将在调试上付出代价)。
答案
-------
基础篇
======
Q1. 什么是矩阵?
----------------------
矩阵是一个由数值数据组成的二维数组,每行或每列包含一个或多个数值。
矩阵可以执行的算术运算包括加法、减法、乘法和除法。
矩阵的大小由行数和列数定义。
具有 M 行 N 列的矩阵定义为 MxN 矩阵。
矩阵中的各个元素使用两个索引值引用。在数学符号中,这些索引通常分配变量 'i' 和 'j'。顺序是先行后列。
例如,如果一个 4x4 矩阵 M 存在,则其元素由以下行:列对索引:
| 00 01 02 03 |
M = | 10 11 12 13 |
| 20 21 22 23 |
| 30 31 32 33 |
矩阵右上角的元素具有 i=0 和 j=3,引用方式如下:
M = M
i,j 0,3
在计算机动画中,最常用的矩阵具有 2、3 或 4 行和列。它们分别被称为 2x2、3x3 和 4x4 矩阵。
2x2 矩阵用于执行旋转、剪切和其他类型的图像处理。通用 NxN 矩阵可用于执行卷积等图像处理功能。
3x3 矩阵用于执行低预算的 3D 动画。旋转和乘法等操作可以使用矩阵运算执行,但透视深度投影使用标准优化后的纯除法操作执行。
4x4 矩阵用于执行高端的 3D 动画。乘法与透视深度投影等操作可以使用矩阵数学执行。
Q2. 什么是矩阵的"阶"?
-------------------------------------
矩阵的"阶"是矩阵大小的另一种说法。具有 M 行 N 列的矩阵称为 MxN 阶。
Q3. 如何使用 C/C++ 编程语言表示矩阵?
-----------------------------------------------------------------------
使用 C/C++ 编程语言定义矩阵的最简单方法是使用 "typedef" 关键字。3x3 和 4x4 矩阵都可以这样定义,即:
typedef float MATRIX3[9];
typedef float MATRIX4[16];
由于每种矩阵的维度分别为 3x3 和 4x4,因此需要 9 和 16 个数据元素。
乍一看,使用单个线性数据数组似乎有违直觉。使用二维数组可能看起来更方便,即:
typedef float MATRIX3[3][3];
typedef float MATRIX4[4][4];
然而,每个矩阵元素使用两个引用系统常常导致混淆。在数学中,顺序是先 i(行)后 j(列),即:
Mij
在 C/C++ 中,这变成:
matrix[j][i]
使用二维数组还会导致 CPU 性能损失,因为 C 编译器通常会使用乘法操作来解析数组索引操作。
因此,坚持使用线性数组更高效。然而,还有一个问题需要解决:如何将二维矩阵映射到线性数组?因为只有两种方法(先行后列或先列后行)。
两者之间的性能差异很微妙。如果取消所有 for-next 循环,那么对于矩阵-矩阵乘法等操作,性能差异很小。
使用 C/C++ 编程语言,每个矩阵的线性顺序如下:
mat[0] = M mat[3] = M
00 03
mat[12] = M mat[15] = M
30 33
| 0 1 2 3 |
| | | 0 1 2 |
| 4 5 6 7 | | |
M = | | M = | 3 4 5 |
| 8 9 10 11 | | |
| | | 6 7 8 |
| 12 13 14 15 |
Q4. 使用矩阵有哪些优势?
-----------------------------------------------
关于在计算机动画中使用矩阵,最早提出的问题之一是:为什么一开始要使用它们?直觉上,for-next 循环和矩阵乘法的开销似乎会拖慢应用程序。
可以指出一些反驳这些反对意见的论点,包括使用 CPU 寄存器处理循环计数器、利用板上数据缓存优化内存访问。
也可以指出优势。通过遵循数学方法来定义 3D 算法,可以预测和规划 3D 动画系统的设计。这种数学方法允许实现角色动画、样条曲线和反向运动学。
然而,经常出现的一个反对意见是:直接对每组坐标乘以该轴的旋转系数,比执行完整的向量-矩阵乘法要快。
例如:绕 X 轴旋转变换 Y 和 Z
绕 Y 轴旋转变换 X 和 Z
绕 Z 轴旋转变换 X 和 Y
对此的论据如下:
给定顶点 V = (x,y,z),旋转角度 (A,B 和 C) 以及平移 (D,E,F)。算法定义如下:
---------------------------
sx = sin(A) // 设置——仅执行一次
cx = cos(A)
sy = sin(B)
cy = cos(B)
sz = sin(C)
cz = cos(C)
x1 = x * cz + y * sz // 每个顶点的旋转
y1 = y * cz - x * sz
z1 = z
x2 = x1 * cy + z1 * sy
y2 = z1
z2 = z1 * cy - x1 * sy
x3 = x2
y3 = y2 * cx + z1 * sx
z3 = z2 * cx - x1 * s
相似文章
Constant Q变换 – 可视化指南
一个互动可视化指南,解释了常量Q变换(CQT),其对数频率几何结构、与FFT的比较、内核构建和高效计算,专为音乐和音高分析而设计。
ggml-webgpu: 提升k-quants的预填充速度并重构Q4/Q5/Q8及k-quants的矩阵乘法 by yomaytk · Pull Request #24225 · ggml-org/llama.cpp
提升了k-quants的预填充速度,并重构了llama.cpp WebGPU后端中Q4/Q5/Q8及k-quants的矩阵乘法。
计算相机射线
一篇技术博客文章,推导如何从视图投影矩阵计算用于光线追踪的相机射线,包含着色器代码并处理数值稳定性问题。
Unity 与浮点数运算
本文探讨了 Unity 中 Mathf 与 System.MathF 在 C# 中的性能和精度差异,揭示了 Unity 的 Mono 运行时即使对于 float 运算也常常会执行双精度数学运算,这对性能优化具有重要启示。
本地Qwen 3.6与前沿模型在编码基础任务上的对比:单文件HTML Canvas驾驶动画——结果与GIF
用户将本地量化的Qwen 3.6模型与前沿模型在单文件HTML Canvas驾驶动画任务上进行比较,发现本地27B Qwen量化模型在视差和运动方面比某些前沿输出表现更好。