原文地址:https://zhuanlan.zhihu.com/p/72734738

The Normal Transformation Matrix

在渲染管线中,模型的坐标会从局部空间(Local space)经Model matrix(简记为M)变换到世界空间(World space),从世界空间经View matrix(简记为V)变换到观察空间(View space,也称为eye space),然后再经Projection matrix变换到裁剪空间(clip space) (vertex shader要计算出裁剪空间的坐标),最后经视口变换(viewport transform)变换到屏幕空间。
(更多关于渲染管线的介绍见这篇文章,关于坐标变换见这篇文章)
在进行光照计算时,为了得到逼真的效果,一般要使用到模型的顶点的法线。可以在观察空间(View space)或世界空间(World space)中进行光照计算。中View space中进行光照计算的好处是观察者(即Camera)的坐标永远是(0, 0)。
假设在View space中进行光照计算。Local space到View space的变换矩阵为M乘以V,简记为[转]法线变换矩阵推导 - 图1
Vertex Shader中输入数据有顶点位置aVertex及法线N,它们都是在局部空间中的向量。则View space空间中的顶点位置可由以下公式计算:
[转]法线变换矩阵推导 - 图2
而View space中顶点的法线N一般不能由[转]法线变换矩阵推导 - 图3计算得到。比如,当模型发生non-uniform缩放时,经[转]法线变换矩阵推导 - 图4变换后的所谓“法线”已不与模型表面垂直了,根本不是法线了。如图1是未发生变换时三角形的法线,而图2是使用[转]法线变换矩阵推导 - 图5变换后的三角形“法线”。
[转]法线变换矩阵推导 - 图6
图1. 变换前三角形的法线
[转]法线变换矩阵推导 - 图7
图2. 使用MV点乘N得到的变换后的“法线”
那么用于法线变换的矩阵应该长什么样呢?答案是:
把法线从local space变换到view space的变换矩阵为顶点位置变换矩阵[转]法线变换矩阵推导 - 图8的逆矩阵的转置矩阵,即[转]法线变换矩阵推导 - 图9
(如果在world space中进行光照计算,则normal matrix为[转]法线变换矩阵推导 - 图10
推导
上面图1中的[转]法线变换矩阵推导 - 图11和图2中的[转]法线变换矩阵推导 - 图12表示模型顶点的切线向量。切线向量可以由三角形边上的某两个点([转]法线变换矩阵推导 - 图13)计算得到。比如:
[转]法线变换矩阵推导 - 图14
三维空间中切线向量[转]法线变换矩阵推导 - 图15可以写成一个四维向量(把最后一个分量设置为0),则:
[转]法线变换矩阵推导 - 图16
可以得到:
[转]法线变换矩阵推导 - 图17
[转]法线变换矩阵推导 - 图18
因为[转]法线变换矩阵推导 - 图19是三角形变换后的顶点,所以[转]法线变换矩阵推导 - 图20为变换后三角形变换后的切线。因此可以使用[转]法线变换矩阵推导 - 图21来变换切向量。
上面已说过了[转]法线变换矩阵推导 - 图22是不能用来变换法线向量的。但我们知道在一个点处,法线向量永远与切线向量垂直,即[转]法线变换矩阵推导 - 图23
因为法线向量仅是一个方向向量,其没有齐次坐标(即第四个分量为0),则平移对法线无作用。设我们要求的把法线从局部空间变换到观察空间的矩阵为一个[转]法线变换矩阵推导 - 图24矩阵[转]法线变换矩阵推导 - 图25,则变换后的法线向量[转]法线变换矩阵推导 - 图26和变换后的切线向量[转]法线变换矩阵推导 - 图27也满足[转]法线变换矩阵推导 - 图28
所以: (由于切线也只是一个方向向量,不受平移变换影响,以下推导中我们用[转]法线变换矩阵推导 - 图29表示真正的[转]法线变换矩阵推导 - 图30矩阵的左上角的[转]法线变换矩阵推导 - 图31子矩阵)
[转]法线变换矩阵推导 - 图32
注意,这里[转]法线变换矩阵推导 - 图33[转]法线变换矩阵推导 - 图34其实都是列向量,所以它们的点积可以这样计算:
[转]法线变换矩阵推导 - 图35
注意:[转]法线变换矩阵推导 - 图36中首尾两个符号(相乘为0),假如:[转]法线变换矩阵推导 - 图37,其中[转]法线变换矩阵推导 - 图38是单位矩阵,则[转]法线变换矩阵推导 - 图39,即新的法线向量和新的切线向量垂直。
[转]法线变换矩阵推导 - 图40
因此正确的normal transform matrix[转]法线变换矩阵推导 - 图41
当然,如果是在世界空间中进行光照计算,则法线变换的矩阵为[转]法线变换矩阵推导 - 图42
有些情形下(如,仅有旋转和平移时),使用[转]法线变换矩阵推导 - 图43对法线进行变换也会得到正确的结果,这是为什么呢?因为这时候,[转]法线变换矩阵推导 - 图44左上角的[转]法线变换矩阵推导 - 图45矩阵是正交矩阵,即[转]法线变换矩阵推导 - 图46
注意计算逆矩阵的过程性能代价很大,不适合在vertex shader或pixel shader(或fragment shader)中对每一个顶点甚至像素都计算一遍法线变换矩阵。一般是在CPU上计算一次,然后把它放到shader中的一个uniform变量中。
另外这篇文章指出在大部分正常情形下,甚至不用计算法线变换矩阵,就能得到变换后的法线。
———更新2019-12-11
最近更新了一篇文章:图形学中的基本变换(Basic Transforms)里面有涉及到法线变换矩阵(打开页面搜索 法线变换),可以从中得知,可以用模型变换矩阵的伴随矩阵的转置矩阵来作为法变换矩阵。

伴随矩阵总是存在,伴随矩阵除以原矩阵的行列式得到逆矩阵,便如果(奇异矩阵)行列式为0,则逆矩阵就不存了。一个方阵的逆阵是该方阵的伴随矩阵除以该方阵的行列式。即伴随矩阵和逆矩阵只差一个系数(即只影响变换后法线的长度),而伴随矩阵总是存在的,所以我们可以用伴随矩阵的转置来作为法线变换矩阵。注意变换得到的新法线不一定是单位长度的,需要进行归一化。

References: