NormalMatrix怎么算或者为什么这么算?
许多计算是在view space中完成的。这与光照通常在该空间中执行的操作有关,否则摄像机位置相关的效果(例如镜面光)将更难实现。
因此,我们需要一种将法线转换为view space的方法(有时候会在世界空间根据原理更改即可)。要将顶点转换为view space,我们可以编写:
vertexEyeSpace = gl_ModelViewMatrix * gl_Vertex;
那么为什么我们不能对法线向量做同样的事情呢?法线是 3 个浮点数的向量,模型视图矩阵(ModelViewMatrix)是 4×4矩阵。然后,由于法线是一个向量,我们只想变换它的方向。包含方向的模型视图矩阵区域是左上角的 3×3 子矩阵。那么为什么不将法线乘以这个子矩阵呢?
这可以通过以下代码轻松实现:
normalEyeSpace = vec3(gl_ModelViewMatrix * vec4(gl_Normal,0.0));
那么,gl_NormalMatrix只是简化代码编写或优化它的捷径?不,不是。上面的代码行在某些情况下会起作用,但不是全部。
让我们看看一个潜在的问题:
在上图中,我们看到一个三角形,有一个法线向量和一个切线向量。下图显示了当模型视图矩阵包含非成比例的变换矩阵时会发生什么。
注意:如果比例是统一的,那么法线的方向会被保留,长度会受到影响,但这可以通过归一化轻松修复。
在上图中,模型视图矩阵应用于所有顶点以及法线,结果显然是错误的:转换后的法线不再垂直于表面。
我们知道向量可以表示为两点之间的差。考虑到切向量,它可以计算为三角形边的两个顶点之间的差。如果P_1和P_2是定义我们知道的边的顶点:
T = P_2 - P_1
考虑到一个向量可以写成一个四分量元组,最后一个分量设置为零,我们可以将等式的两边与Modelview矩阵相乘
T *Modelview = (P_2-P_1)* Modelview
这导致
T * Modelview = P_2 * Modelview - P_1 * Modelview
T’ = P_2’ - P_1’
由于P_1’和P_2’是变换三角形的顶点,T’仍然与三角形的边缘相切。因此,模型视图保留切线,但不保留法线。
考虑与向量T使用相同的方法,我们可以找到两个点Q_1和Q_2这样
N = Q_2 - Q_1
主要问题是通过变换点定义的向量,Q_2’ - Q_1’, 不一定保持正常,如上图所示。法向量不定义为像切向量一样的两点之间的差,它定义为垂直于表面的向量。
所以现在我们知道我们不能在所有情况下都应用模型视图来转换法线向量。那么问题来了,我们应该应用什么矩阵?
考虑一个 3×3 矩阵G,让我们看看如何计算该矩阵以正确转换法线向量。
我们知道,在矩阵变换之前,因为根据定义,向量是垂直的,所以
T•N=0
我们还知道在变换后因为它们必须保持相互垂直,所以
N’•T’= 0
T可以安全地乘以模型视图左上角的 3×3 子矩阵(T是一个向量,因此w分量为零),我们称这个子矩阵为M。
让我们假设矩阵G是转换法线向量T的正确矩阵。因此有以下等式:
N’•T’ = (GN)•(MT) = 0
点积可以转化为向量的乘积,因此:
(GN)•(MT) = (GN)T * (MT)
请注意,必须考虑第一个向量的转置,因为这是乘以向量所必需的。我们还知道乘法的转置是转置的乘法,因此:
(GN)T (MT) = NTGTMT
我们首先声明N和T之间的点积为零,所以如果
GTM = I
那么我们有
N’•T’ = N•T = 0
这正是我们想要的。所以我们可以根据M计算G。
GTM = I ⇿ G = (M-1)T
因此,转换法线的正确矩阵是M矩阵的逆矩阵的转置。OpenGL 在gl_NormalMatrix中为我们计算这个。
在本节的开头,有人说在某些情况下使用Modelview矩阵是可行的。每当Modelview的 3×3 左上子矩阵正交时,我们有:
M-1 = MT → G = M
这是因为对于正交矩阵,转置与逆矩阵相同。那么什么是正交矩阵呢?正交矩阵是所有列和行都是单位长度并且相互垂直的矩阵。这意味着当两个向量乘以这样的矩阵时,它们之间的角度在经过正交矩阵变换后与变换前相同。简单地说,变换保留了向量之间的角度关系,因此变换后的法线保持垂直于切线!此外,它还保留了向量的长度。
那么我们什么时候可以确定M是正交的呢?当我们将几何操作限制为旋转和平移时,即在 OpenGL 应用程序中,我们只使用glRotate和glTranslate而不是glScale。这些操作保证M是正交的。
注意:gluLookAt也会创建一个正交矩阵
参考:http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/