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 协议进行许可