第二章:三维空间基础
各位同学,今天我们来聊聊三维空间的基石。说实话,我见过太多新手一上来就扎进渲染管线、光照模型,结果连坐标系都没搞明白,最后 debug 到崩溃。嗯,咱们先把地基打牢。
2.1 坐标系:从世界到屏幕的旅程
三维空间里,坐标系就是我们的「度量衡」。你想想看,没有统一的坐标系,AR 里的虚拟物体和现实世界根本对不上。我早期做的一个 AR 项目,就因为坐标系没对齐,虚拟茶壶直接飘到了天花板上——那场面,挺尴尬的。
2.1.1 世界坐标系
世界坐标系是全局的参考系。它固定不动,所有物体都相对于它定义位置。在 AR/VR 里,世界坐标系通常以场景中心为原点,或者以用户初始位置为原点。
- 右手系 vs 左手系:OpenGL 用右手系,DirectX 用左手系。我个人习惯用右手系,因为数学上更直观。
- 单位:一般用米。别问为什么,问就是物理世界用米,AR 要跟现实对齐。
2.1.2 相机坐标系
相机坐标系是以相机为原点的坐标系。说白了,就是「从相机眼里看世界」。Z 轴指向相机前方,X 轴向右,Y 轴向上。
这里有个坑:相机坐标系和世界坐标系之间的转换。我曾经在项目里直接用相机的位置作为原点,结果旋转时物体全飞了。后来才意识到,得用 view matrix 来做转换。
2.1.3 图像坐标系
图像坐标系是二维的,对应屏幕上的像素位置。它从相机坐标系投影而来,涉及内参矩阵(焦距、主点等)。
核心公式:从世界坐标到像素坐标的完整流程
世界坐标 → 相机坐标 (view matrix)
相机坐标 → 归一化设备坐标 (projection matrix)
归一化设备坐标 → 像素坐标 (viewport transform)
2.2 刚体变换:旋转、平移、缩放
刚体变换,说白了就是「不改变物体形状的移动」。你拿着手机转一转,那就是刚体变换。我做过一个 VR 手柄追踪的项目,刚体变换算不准,手柄就在空中乱晃——用户玩五分钟就晕了。
2.2.1 平移
平移最简单,就是加一个位移向量。在齐次坐标下,用 4x4 矩阵表示:
| 1 0 0 tx |
| 0 1 0 ty |
| 0 0 1 tz |
| 0 0 0 1 |
2.2.2 旋转
旋转稍微复杂点。绕 X、Y、Z 轴旋转的矩阵,大家应该都见过。但要注意:旋转顺序很重要。先绕 X 再绕 Y,和先绕 Y 再绕 X,结果完全不同。
我建议用 RPY(Roll-Pitch-Yaw) 顺序,也就是绕 Z、Y、X 轴依次旋转。这是航空领域的标准,AR/VR 里也常用。
2.2.3 缩放
缩放分两种:均匀缩放和非均匀缩放。均匀缩放就是整体放大缩小,非均匀缩放可以拉长压扁。在 AR 里,我一般只用均匀缩放,不然虚拟物体看起来会「变形」。
小技巧:如果你要做非均匀缩放,记得先缩放再旋转。否则旋转后的物体,缩放方向会跟着变,结果很难控制。
2.3 四元数与欧拉角
说到旋转表示,就不得不提四元数和欧拉角。这两个东西,我当年学的时候也绕了很久。咱们慢慢来。
2.3.1 欧拉角
欧拉角用三个角度表示旋转:偏航(Yaw)、俯仰(Pitch)、滚转(Roll)。直观,好理解。但有个大问题——万向锁。
什么是万向锁?当俯仰角达到 ±90° 时,偏航和滚转就「锁」在一起了,丢失一个自由度。我在做无人机仿真时遇到过,飞机翻了个跟头,姿态直接崩了。嗯,那感觉,就像你开车时方向盘突然失灵。
避坑指南:如果你用欧拉角做插值(比如平滑旋转),一定会遇到万向锁。我曾经花了两天 debug,最后发现是欧拉角的问题。从那以后,我只要涉及插值,一律用四元数。
2.3.2 四元数
四元数用四个数表示旋转:一个实部 + 三个虚部。形式是 q = w + xi + yj + zk,其中 w 是实部,x、y、z 是虚部。
四元数的好处:
- 无万向锁:这是最大的优势
- 插值平滑:用球面线性插值(SLERP),旋转过渡非常自然
- 计算高效:比矩阵快,比欧拉角稳定
但四元数也有缺点:不直观。你看着 (0.707, 0, 0.707, 0) 这个四元数,能想象出它代表什么旋转吗?反正我不能。所以实际开发中,我通常用欧拉角做输入,内部转成四元数计算,最后再转回欧拉角显示。
2.3.3 转换关系
| 表示方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 欧拉角 | 直观、易理解 | 万向锁、插值困难 | 用户输入、调试显示 |
| 四元数 | 无万向锁、插值平滑 | 不直观、难调试 | 内部计算、动画插值 |
| 旋转矩阵 | 通用、易组合 | 占用内存大、计算慢 | 渲染管线、坐标变换 |
2.4 知识体系总览
下面这张图,是我自己总结的三维空间基础的知识结构。你把它记在心里,后面学渲染、学追踪、学 SLAM,都会用到。
2.5 实战建议
好了,理论讲完了。说点实际的。
第一,别自己手写矩阵运算。 除非你在做学术研究,否则直接用现成的库。Unity 有 Matrix4x4,Unreal 有 FMatrix,OpenCV 有 cv::Mat。自己写?我试过,bug 多到怀疑人生。
第二,调试时把坐标可视化。 在场景里画三个箭头表示坐标轴,画一个网格表示地面。这样你一眼就能看出物体在哪、朝向哪。我每次做新项目,第一件事就是画坐标轴。
第三,四元数一定要用单位四元数。 非单位四元数会导致缩放变形。我曾经在 VR 项目里,因为四元数没归一化,虚拟手越转越大,最后比人还大——用户吓得直接摘了头显。
我的习惯:每次创建或修改四元数后,立刻调用 normalize()。别偷懒,这行代码能救你命。
嗯,这一章的内容就到这。坐标系、刚体变换、四元数与欧拉角,这三块是三维空间的基石。你把它吃透了,后面学相机标定、三维重建、SLAM,都会轻松很多。