在顶点着色器中将普通坐标转换为视图空间

3

当我们想要在Vertex Shader中计算光照时,我们需要在视图空间中的法向量。一般情况下,它看起来像下面这样(来自OpenGL超级宝典第5版):

// normalMatrix is retrieved from GLMatrixStack modelViewMatrix
vec3 vEyeNormal = normalMatrix * vNormal

我希望能够编写程序,而不使用GLT库。在另一个来源中(http://en.wikibooks.org/wiki/GLSL_Programming/GLUT/Diffuse_Reflection),我找到了以下公式:

vec3 normalDirection = normalize(m_3x3_inv_transp * v_normal);

变量 m_3x3_inv_transp 的计算方式如下:
glm::mat3 m_3x3_inv_transp = glm::transpose(glm::inverse(glm::mat3(mesh.object2world)));

我知道:

我的问题是,为什么在反转和转置矩阵后,我会得到 NormalMatrix,并且如何通过反转计算来检查它?

2个回答

6
你的假设并不完全正确。
OpenGL操作的顺序是缩放、平移、旋转。
一般来说,你可以有一个任意的世界矩阵。这可以包括任意数量的操作。
取反矩阵是撤销上一个变换。
不是这样的。如果你倒置一个矩阵,它的整个效果就会被还原。例如,如果你有一个绕x轴旋转45°并沿(1,2,3)平移的矩阵,它的倒数将导致先平移(-1,-2,-3),然后再绕x轴逆时针旋转45°。
法线矩阵只是模型视图矩阵的旋转部分。
不是这样的。如果是这样的话,那么你可以直接抛弃矩阵的平移(和任何透视)部分。但事实并非如此。
法线矩阵用于转换法线,以使它们仍然垂直于相应的表面。对于刚体变换(即旋转和平移),你可以直接使用世界变换。其中一个原因是你可以通过转置来反转旋转矩阵(因为它是正交的)。那么你就得到了transpose(transpose(world)),这是原始的矩阵。
对于一般的矩阵,你必须像你所说的那样计算矩阵。想象一个缩放(1,2)的例子。如果你变换一个圆,它会变成椭圆。让我们看看45度处的法线。这个位置上的圆的法线是(1,1)(未归一化)。如果我们用缩放矩阵变换这个法线,我们得到(1,2)。如果你想象一下变换后的椭圆,你就会发现法线不再垂直于表面了。因此,你必须使用一个不同的变换(在这种情况下是缩放(1,0.5)),以保持正交性。

感谢您指出我思考中的错误,并给出简单、实用的答案。 - CppMonster

4
根据Eric Lengyel的《3D游戏编程与计算机图形学数学》第66页,一个表面上特定点处的切线T和法线N必须垂直,因此:
N . T = 0

假设我们使用矩阵M对曲面进行变换。在变换后的曲面上的点处的切线为:

T' = MT

我们希望找到一个矩阵G,使得N' = GN成为变换后表面上某点的法线。由于变换后表面上该点的法线和切线仍必须垂直,我们有以下关系式:
0 = N' . T' = (GN) . (MT) = trans(GN) * MT = trans(N) * trans(G) * MT

现在:

trans(N) * T = N . T = 0

所以,如果满足以下条件,则上述内容将被满足:
trans(G) * M = I

原因:

G = trans(inv(M))

换句话说,用于转换法线的矩阵是用于转换切线的矩阵的逆矩阵的转置。

如果我理解得正确,切向量(T)始终垂直于表面,以便将其推广到不同形状的表面,并且矩阵M用于转换向量T、N和该表面的顶点? - CppMonster
@CppMonster:不,法向量总是垂直于表面的(根据定义)。假设表面是一个多边形网格,那么M可以用来转换顶点和切线向量,但不能用来转换法向量。要转换法向量,你需要使用M的逆矩阵的转置,这就是上面所说的意思。 - Stuart Golodetz
1
我匆忙中写错了(显然N是垂直于表面的,这是本主题的基础)。我想要写的是向量T垂直于向量N,以便推广不同形状的表面(如果我对切线有良好的理解)。谢谢你给我的信息碎片,因为它很有用且宝贵。 - CppMonster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接