2013-07-01 39 views
3

当我们要在顶点着色器中计算光时,我们需要视图空间中的法向量。一般来说,它看起来如下(从OpenGL Superbible 5th开始):将顶点变换为顶点着色器中的视图空间

// 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))); 

我意识到:

我的问题是,为什么反演和transponsing矩阵后,我得到NormalMatrix以及如何检查它与逆向计算?基于对3D游戏编程和计算机图形(由埃里克·伦盖尔)数学第66页

回答

3

你的假设并不完全正确。

OpenGL操作顺序是Scalng,翻译,旋转。

不,一般来说,你可以有一个任意的世界矩阵。这可以包括任何数量的操作。

逆矩阵是撤消上次变换。

否。如果反转矩阵,则会恢复其整个效果。例如。如果您有一个矩阵围绕x轴旋转45°并且由(1,2,3)进行平移,则其反转将导致(-1,-2,-3)平移,接下来是 - 围绕x轴45°。

正常矩阵只是模型视图矩阵的旋转分量。

不是。如果是这种情况,那么您可以放弃矩阵的翻译(以及任何视角)部分。但事实并非如此。

正常矩阵用于转换法线,以便它们仍然与相应的曲面正交。对于刚体转换(即旋转和平移),您可以直接使用世界变换。其中一个原因是你可以通过转置旋转矩阵来反转(因为它是正交的)。然后你有transpose(transpose(world))这是原始矩阵。

对于一般矩阵,您必须按照您所述计算矩阵。想象一下按(1,2)进行缩放。如果你转换一个圆圈,它将变成一个椭圆。我们来看看45°的正常值。在这个位置的圆的正常值是(1,1)(未标准化)。如果我们用标度矩阵转换这个法线,我们可以得到(1,2)。如果你想象转换后的椭圆,你会发现法线不再与表面正交。所以你必须使用一个不同的变换(在这种情况下,按(1,0.5)进行缩放),这保留了正交性。

+0

感谢您在我的思考和简单实用的答案中发现错误。 – CppMonster

3

答:

切线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)) 

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

+0

如果我很好理解切线向量(T)总是垂直于曲面将其推广到不同形状的曲面,并且矩阵M用于变换此曲面的向量T,N和顶点? – CppMonster

+0

@CppMonster:不,法向矢量总是垂直于表面(按照定义)。假设曲面是多边形网格,那么M可以用来变换顶点和切向量,但不是法向量。为了转换法向量,需要使用M的倒数的转置,这就是上面所说的。 –

+1

我在急速写错了(显然N是垂直于表面,它是这个线程的基础)。我想写出矢量T垂直于矢量N来泛化不同形状的表面(如果我对切线有很好的理解)。感谢您提供给我的一些信息,因为它非常有用且有价值。 – CppMonster