01 / 为什么需要多个坐标空间
一把椅子的建模数据以自身中心为原点最方便;把它摆进房间时,世界坐标最方便;判断相机前方的深度时,观察坐标又最方便。空间变换不是无意义的搬运,而是在为下一阶段选择合适参照系。
| 空间 | 参照 | 适合处理 |
|---|---|---|
| 模型 | 物体自身 | 建模数据、局部动画 |
| 世界 | 场景原点 | 物体关系、世界灯光 |
| 观察 | 相机 | 深度与视锥 |
| 裁剪 | 齐次视锥 | 裁剪与投影 |
| NDC / 屏幕 | 标准立方体 / 像素 | 光栅化 |
02 / 移动相机等于反向移动世界
观察矩阵把世界变换到以相机为原点的坐标系。若相机在世界中向右移动 2 个单位,从相机看来,整个世界就应向左移动 2 个单位,因此观察矩阵本质上是相机世界变换的逆矩阵。
p_view = V · p_world
V = inverse(camera_world_transform)
V = inverse(camera_world_transform)
常见的 lookAt 会根据相机位置、目标点和上方向构造右、上、前三个正交轴。若观察方向与给定上方向平行,叉积会退化,必须选择另一个上方向。
03 / 正交投影与透视投影
正交投影
不随距离缩小物体,平行线保持平行。它适合工程视图、编辑器辅助视图和部分二维游戏。
透视投影
远处物体更小,符合针孔相机的成像规律。视野角 FOV 越大,看到的范围越广,边缘变形也越明显;宽高比必须匹配视口,否则画面会拉伸。
clip_position = P · V · M · local_position
近裁剪面不能设为 0。更重要的是,过大的远近面比例会损害深度缓冲精度,造成远处表面闪烁。
04 / 从裁剪空间到 NDC
投影矩阵输出齐次裁剪坐标 (x, y, z, w)。GPU 先在齐次空间判断图元与视锥关系,随后执行透视除法。
x_ndc = x_clip / w_clip
y_ndc = y_clip / w_clip
z_ndc = z_clip / w_clip
y_ndc = y_clip / w_clip
z_ndc = z_clip / w_clip
除以与深度相关的 w 后,远处坐标被压缩,于是产生“近大远小”。NDC 通常落在固定范围内,再由视口变换映射到实际像素坐标。
为什么先裁剪再除法?跨过相机平面的三角形可能包含异常的 w。先在齐次空间裁剪,能避免透视除法把它翻转或放大到无穷。
05 / API 约定并不完全相同
- NDC 的 Z 范围可能是
[-1,1],也可能是[0,1]。 - 屏幕坐标原点可能在左上或左下,Y 轴方向也随之变化。
- 观察空间可能采用相机看向 -Z 或 +Z 的约定。
- 纹理坐标 V 轴方向与图像文件原点可能相反。
排查原则:不要只问“这个数对不对”,要问“这个数在哪个空间、遵守哪套 API 约定”。