计算机图形学 02:相机怎样把空间投到屏幕
一个点的屏幕横坐标算错,可能来自模型位置、相机姿态、投影矩阵、透视除法或视口映射。只打印最终像素,无法区分这些原因。本篇让一个点逐站输出坐标,并用近平面、远平面与相机后方的样本检查边界。
实验沿用第 01 篇的列向量与右手系。相机朝自身的 −Z 方向观察;透视投影后的 NDC 横纵坐标范围是 [-1,1],深度范围是 [0,1]。这些是本系列的具体选择。不同教材可以使用不同约定,矩阵必须随之变化。
从物体位置到相机坐标
模型空间用于描述物体自身的几何,例如一个三角形相对于其建模原点的位置。模型矩阵 M 把它放进世界空间。观察矩阵 V 再将世界坐标表达为相机坐标,投影矩阵 P 产生裁剪坐标:
这不是三个任意可交换的操作。按照第 01 篇的列向量约定,最右侧的 M 先作用;写成 MVP 会改变含义。实现中分别保存中间结果,验收通过后才有理由预先合并矩阵。
设模型点为 ,模型矩阵将物体沿 Z 平移 −1,世界坐标便是 。相机位于世界点 ,朝原点看,竖直参考方向为 。相机没有额外旋转,观察矩阵只需从世界点中减去相机位置,所以相机坐标为 。
相机前方点的 z 为负,这是后面 的来源。不能把“距离为 2”直接填成相机坐标 z=2;距离非负与有向坐标是不同概念。
look-at 怎样构造一组相机基
一般相机不一定正对世界 −Z。给定相机位置 e、目标点 t 和参考上方向 u,先构造相机后向单位向量:
r、v、b 分别是相机的右、上、后方向。观察矩阵前三行是这些基向量,第四列为它们与相机位置的负点积:
于是相机坐标的第一分量为 ,即相对位移沿相机右方向的投影。其余两行同理。这个推导说明 V 是世界到相机的变换,不能把相机在世界中的位姿矩阵不求逆就当作 V。
参考上方向不必已归一化,也不必与视线严格垂直,但不能与视线平行。相机位置等于目标点时,b 无法归一化;参考上方向平行于 b 时,叉积为零。这两种输入在本实验中抛出异常。近乎平行仍可能数值敏感,当前实现没有宣称适用于任意极端尺度。
透视先来自相似三角形
在相机前方放一个虚拟成像平面。相机坐标为 、z<0 的点,与相机原点连线。相似三角形给出投影横纵坐标正比于 与 。保持 x 不变而把距离翻倍,投影横向偏移就减半。
令竖直视场角为 ,宽高比为 a,。将成像平面的可见范围归一化后,有:
宽高比 a 控制水平可见范围。给 640×480 视口采用 a=1,会将方形相机窗口映到矩形视口。本篇坐标追踪故意这样固定参数,方便手算;若希望横纵几何比例一致,应使用 a=640/480。矩阵参数与最终视口比例不一致,不是右手系或左手系问题。
投影不能只丢掉 z,因为渲染器还需要判定前后遮挡。设正的近、远距离分别为 n、f,满足 。选取裁剪坐标 ,并令 ;要求 z=−n 时深度为 0,z=−f 时深度为 1,可以解得:
所以本系列的透视矩阵是:
这套公式的前提包含相机 −Z、列向量和深度 [0,1]。PBRT 的相机约定为 +Z,其矩阵不能直接复制进当前代码再期待相同符号。核对教材时应比较映射目标与端点,不只比较矩阵外观。
裁剪坐标还不是屏幕坐标
取 ,前面的相机点 经 P 变为:
这四个分量用于齐次裁剪。完成合法性判断后,再将前三个分量除以 w,得到:
本篇对单个点使用范围判定:
并要求 w>0、分量有限。inside_clip 负责这个范围判断;divide_w 只负责正 w 与有限分量检查,不替代完整范围判定。一个点可能能除以 w,却在视锥外。调用方必须理解两个函数的不同职责。
相机平面上的点 z=0 产生 w=0,除法没有定义。相机后方 z>0 产生负 w;如果无条件相除,横纵坐标可能翻转后重新落入 [-1,1],造成后方点似乎可见的错误。当前检查器分别验证这两种情况都被拒绝。
这只是单点判定。一个三角形有一个顶点在视锥外,并不表示整个三角形都不可见;三个顶点全部在外也可能仍与视锥相交。第 06 篇会在齐次空间把边裁到边界,生成交点后再透视除法。本篇不以“丢掉非法顶点”冒充三角形裁剪。
NDC 到图像数组还差一次映射
第 00 篇图像原点在左上,Y 向下;当前 NDC 的 Y 向上。宽 W、高 H 的视口使用:
对于 640×480, 得到 。这里是连续屏幕坐标,不是已经选中的像素索引。NDC 的右边界 x=1 会映成 x=W,它是视口外边界,不能直接用于访问数组 at(W,y)。
后续三角形覆盖测试采用像素中心 。哪个中心落在投影三角形内,需要另外计算;不能把每个顶点坐标截成整数就认为三角形已经光栅化。
同理,屏幕映射不改变深度 z。透视深度是关于相机距离的非线性函数:令 d=−z,则 。它在 n 与 f 处满足端点要求,中间却不等于距离的等比例缩放。后续深度精度实验会依赖这一差别。
正交投影改变了什么
正交投影不做随距离缩小。令可见窗口的半高为 h、宽高比为 a,本篇使用对称窗口,其矩阵为:
w 始终为 1,横纵坐标不再除以距离,深度则在 −n 到 −f 之间线性映到 [0,1]。它适合需要保留平行关系的示意或工程视图,但不会产生透视缩短。
实验选 h=2,比较同一横坐标 x=1 在 z=−2 和 z=−4 时的结果。两种投影在近一点处都给出 x=0.5;远一点处,透视变成 0.25,正交仍为 0.5。对照固定了两个实际点与窗口参数,没有把相机移动、缩放窗口和投影类型同时更改。
实际运行的坐标链
代码复用 math.hpp,新增 camera.hpp 与 camera_check.cpp。运行:
1 | |
本次 C++17 实际输出:
1 | |
检查器将这些结果与独立手算常数比较,绝对容差为 ,并核对近、远平面深度分别为 0 与 1。五个范围外样本包括相机平面、相机后方、近面之前、远面之后和横向越界。
六个异常输入包括 w=0、w<0、相机与目标重合、上方向与视线平行、视场角为零和近远距离倒置。参数验证只定义当前有限规模的教学接口,不保证所有极端浮点参数都能安全产生矩阵。没有随机数、性能比较或三角形画面验收;本篇的证据是逐空间数值与边界检查。
练习与自检
练习一:保持画面比例。 将实验视口设为 640×480,投影视场角仍为竖直90°,但把 a 改成4/3。相机点 的屏幕横坐标变成多少?
答案:,,屏幕横坐标为 。横向可见范围扩大,因此相同点离中心的像素偏移从160减到120。不能只改视口尺寸却假定投影比例自动更新。
练习二:后方点与边界点。 相机点 为什么不能直接除 w 后判断横纵范围?NDC 点 映到640×480后,能直接作为合法数组索引吗?
答案:后方点 w=−1,横纵除完仍为0,单查横纵会误收;应在齐次范围与正w条件下拒绝。第二个点映到连续坐标(640,240),x=640是外边界,合法像素列仍为0到639。覆盖测试需检查像素中心,数组访问需独立的整数边界检查。
系列导航与资料
前篇:01:点、向量与矩阵怎样表达几何。系列入口。下一篇:03:为什么细线会断裂、棋盘格会闪烁。
PBRT 4ed §5.2 Projective Camera Models,重点阅读5.2.1 Orthographic Camera、5.2.2 Perspective Camera与图5.2–5.6,核对投影、近远平面与坐标空间的用途。本文矩阵由本系列−Z相机和深度区间重新推导,不等同于PBRT源码约定。查阅日期2026-09-19。
源码:examples/computer-graphics/camera.hpp、camera_check.cpp;原始输出:writing-plans/computer-graphics/evidence/02-cpu.txt。三角形裁剪、遮挡和真实GPU投影尚不属于本篇验收。




