DongGu
透视矫正与法线向量变换

透视矫正与法线向量变换

三维空间内的模型要最终呈现在屏幕上,必须经过透视投影变换。透视投影让我们看到了”近大远小”的真实世界,但它的代价是故意扭曲了空间的均匀性——透视除法(除以 )引入了一个非线性环节。

这种非线性会导致两个在渲染管线中必须解决的问题:

  1. 屏幕空间插值失真:在屏幕空间直接对深度或纹理坐标做线性插值,得到的不是三维空间中正确线性插值的结果,纹理会出现扭曲。
  2. 法线向量失效:变换后直接用原变换矩阵作用于法线向量,法线将不再垂直于表面,导致光照计算全部出错。

问题 1 通过透视矫正(Perspective Correction)解决,问题 2 通过法线矩阵(Normal Matrix)解决。下面分别讨论。

透视矫正

问题根源

为了理解为什么需要透视矫正,我们先看透视投影下插值为什么会失真。

透视插值失真

在这张图中,我们将 2D 空间内的一条线段 透视投影到 1D 空间,得到线段 。设 点的属性值为 , 点的属性值为 ,那么投影后 点也为 , 点也为 。

现在取 的中点 (屏幕空间 ,即 等于 和 的线性插值中点),但将其逆透视投影回 2D 空间后,对应点 却不是 的中点—— 偏向更近的 一侧。

根源在于:透视投影中的”透视除法”(除以 )引入了非线性变换,导致 3D 空间中的坐标与屏幕投影坐标之间不再是线性对应关系。同样的结论可以从 2D→1D 推广到 3D→2D。

推导透视矫正公式

既然直接线性插值不对,我们能否找到透视前后两个插值系数之间的映射关系?如果能,就可以在屏幕空间做插值时”纠偏”回来。

透视矫正几何模型

如上图,虚拟相机在观察坐标系中沿 方向观察,图像平面位于相机前方距离 处。2D 空间中的线段 经透视投影后变为 1D 空间中的线段 。 与 分别具有透视前后的映射关系, 和 分别是屏幕空间和观察空间的线性插值系数。 代表某种需要插值的属性值。

我们要找的就是 和 之间的关系。

第一步:建立相似三角形关系。

第二步:写出线性插值定义。

屏幕空间内 和 的线性关系:

观察空间内 与 的线性关系:

第三步:消元求解。

将公式 和 代入公式 :

将公式 和 代入 并消去 :

将公式 左侧的 替换为公式 :

从公式 中解出 与 的关系:

至此我们得到了 和 的非线性映射关系。在屏幕空间插值时代入这个关系,就能得到正确的属性值。但直接在每次插值时套用公式 代价太高,需要进一步化简。

第四步:发现关键不变量。

将公式 代回深度插值公式 :

对公式 进行化简:

即:

又定义屏幕空间内 的线性插值结果为:

比较 和 ,右侧完全相同,因此:

结论一: 在透视变换后仍然满足线性关系。 在屏幕空间对 做线性插值,得到的结果等于观察空间的真实值 。取倒数即可还原出三角形内部像素的真实深度 。

第五步:推广到任意属性 。

观察空间中任意属性 的线性插值:

将透视映射关系 代入:

通分整理:

将公式 代入分母:

即:

又定义屏幕空间内 的线性插值结果为:

比较 和 :

结论二: 在透视变换后同样满足线性关系。 在屏幕空间对 做线性插值,结果等于真实的 ,乘以 即可得到真实的 。

透视矫正的实践流程

综合以上推导,GPU 在光栅化阶段进行透视矫正插值的实际操作流程为:

  1. 对三角形每个顶点的属性 ,计算 和 。
  2. 在屏幕空间对 和 分别做重心坐标线性插值。
  3. 对插值出的 取倒数得到该像素的真实深度 。
  4. 将插值出的 乘以 ,得到该像素的真实属性值 。

这样就在屏幕空间内得到了与观察空间插值完全等价的结果,同时保持了光栅化的高效性。

为什么不在观察空间直接插值? 你可能会问:既然屏幕空间插值会失真,直接在透视之前的观察空间做插值不就行了?答案是不可行,因为光栅化本身就是一个屏幕空间操作。GPU 的固定功能光栅化器在裁剪空间/NDC 中确定三角形覆盖了哪些屏幕像素,生成片元时天然处于屏幕坐标。若想在观察空间插值,就需要对每个像素反算其对应的 3D 观察空间位置——这等价于光线追踪的求交操作,完全背离了光栅化管线以屏幕空间为核心的效率优势。透视矫正正是以最小的数学代价,在屏幕空间内”模拟”出观察空间插值的正确结果。

法线向量变换

透视矫正解决了属性插值的问题。但透视投影的非线性变换还带来了第二个问题:变换后的法线向量不再垂直于表面,而我们做光照计算时严重依赖法线方向。这个问题不仅限于透视投影——任何包含非均匀缩放、剪切或透视的变换都会破坏法线的正交性。

为什么法线在变换后会失效?

法线向量和位置/方向向量是不同类型的几何量。位置向量和切向量可以直接用变换矩阵 来变换(因为切向量是顶点坐标的差值,服从相同的坐标变换规则),但法线是垂直于切平面的向量,它不遵循相同的变换规律。

以一个简单的二维例子来说明。考虑二维空间中的一条线段,方向向量为 ,法向量 满足 (即 垂直于 )。

对该平面施加一个非均匀缩放变换矩阵 :

即将 方向拉伸为原来的 2 倍, 方向保持不变。

取线段方向 ,法向量 。容易验证 ,二者正交。

  • 变换后的线段方向:
  • 如果直接用 变换法向量:
  • 验证正交性:

法向量已经不再垂直于变换后的线段了!

而正确的变换后法向量应该是 ——它与 的点积为 ,确实垂直。

问题来了:对于任意变换矩阵 ,是否存在一个专门用于变换法向量的矩阵 ,使得 总能满足 ?

答案是肯定的。下面推导这个矩阵 。

法线矩阵的推导

设原始空间中平面的一条切向量为 ,法向量为 ,满足:

经过变换矩阵 后: - 切向量 是顶点坐标的差值,直接受 作用: - 我们需要找到一个矩阵 ,使得新的法向量 仍然与 垂直:

代入 和 :

将点积写成矩阵乘法形式(将向量视为列向量,对第一个向量取转置):

我们已知 。为了使上式对所有切向量 都成立,必须有:

其中 是单位矩阵。这是因为 是切平面的“正交补”——在切平面(2D 子空间)中,只有一条唯一的法线方向(忽略长度)。为了使得 对所有切向量成立, 作用于切平面的结果必须仍在切平面中。最简洁的保证方式就是令 。

由此:

这就是法线矩阵(Normal Matrix)——变换矩阵 的逆的转置(Inverse Transpose)。

完整的变换规则为:

注意:变换后的法向量 通常不再是单位向量,着色前需要在 Shader 中重新归一化(normalize)。

验证与特例

回到前面的二维例子来验证。非均匀缩放矩阵 :

原始法向量 ,变换后:

与 方向相同,与 的点积确实为 。正确。

下面讨论几种常见的特殊情况:

变换类型 的性质
纯旋转 是正交矩阵: ,法线直接随顶点旋转
均匀缩放( 倍) , ,法线方向不变,仅长度缩放,归一化后无影响
非均匀缩放 / 剪切 任意可逆 必须计算完整的 ,法线方向会改变
平移 不影响方向向量 取 的左上角 子矩阵计算即可

总结

透视投影引入的非线性变换,给渲染管线带来了两个必须应对的挑战:

  • 透视矫正:揭示了 和 在透视变换后仍然保持线性这一关键性质,使得 GPU 可以在屏幕空间内高效地完成正确的属性插值,无需回到观察空间重算。
  • 法线矩阵:指出了法向量与位置/切向量在变换规则上的本质差异——法线需要用 而非 本身来变换,才能在所有变换下(尤其是非均匀缩放和剪切变换)保持与表面的正交关系,确保光照计算正确。

两者结合,使得光栅化管线在享受透视投影带来的真实感的同时,保持了数学上的正确性和工程上的高效性。


这次的插图来自画师 烏

图片地址:https://www.pixiv.net/artworks/130723113

关于透视矫正的文献地址:https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf

本文作者:DongGu
本文链接:https://donggu.xyz/2026/05/31/图形学入门/透视矫正与法线向量变换/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可