文章翻译|法线矩阵(The Normal Matrix)

gl_NormalMatrix 存在于很多顶点着色器中,在这里,将会对这个矩阵和他的作用做出解释.本节的灵感来自Eric Lengyel的优秀著作"Mathematics for 3D Game Programming and Computer Graphics".

许多计算都是在眼睛空间完成的,这与光照通常在这个空间中执行有关,否则依赖于眼睛位置的效果,比如镜面光,将更难实现.

因此我们需要一个方式将法线转换到眼部空间.对于顶点到眼睛空间的转换可以使用下面的代码:

vertexEyeSpace = gl_ModelViewMatrix * gl_Vertex;

那么为什么我们不可以直接对于法线向量做相同的计算呢?首先,法线是一个由3个浮点数组成的向量,而模型-视口矩阵是4X4的,其次,由于法线是一个向量,所以我们只想要变换他的方向.模型-视口矩阵中包含旋转功能的部分是左上角3X3的子矩阵,所以为什么不将法线与子矩阵相乘呢?

这很容易用下面的代码实现:

normalEyeSpace = vec3(gl_ModelViewMatrix * vec4(gl_Normal,0.0));

那么,gl_NormalMatrix只是简化代码编写或优化代码的一种快捷方式吗?并不是,上面的代码仅在一些情况下有效.

让我们看一下一种可能的情况:

在这里插入图片描述
在上面的图中有一个三角形,并标有法线和切线的向量,下面的图片展示当模型-视口矩阵发生不均匀变换时的情况.

在这里插入图片描述

注意:如果缩放是均匀的,那么法线的方向将会被保留,长度将会被改变但可以通过归一化很容易的修复.

在上面的图中,模型-视口矩阵被应用到所有的顶点以及法线上,但结果显然是错误的:被变换后的法线不再垂直于表面.

我们知道,一个向量可以表示为两个点之间的差异,考虑切向量,它可以计算为三角形边的两个顶点之间的差异.如果P1和P2是定义边的顶点,我们知道:

T = P 2 − P 1 T = P_2-P_1 T=P2P1
考虑到一个向量可以被写成有四个分量的数组,其中最后一个分量是零,我们可以在等式的两侧乘以模型视口矩阵

T ∗ M o d e l v i e w = ( P 2 − P 1 ) ∗ M o d e l v i e w T * Modelview = (P_2-P_1)*Modelview TModelview=(P2P1)Modelview

那么结果就是:

T ∗ M o d e l v i e w = P 2 ∗ M o d e l v i e w − P 1 ∗ M o d e l v i e w T ′ = P 2 ′ − P 1 ′ T*Modelview = P_2*Modelview -P_1*Modelview \\T'=P_2'-P_1' TModelview=P2ModelviewP1ModelviewT=P2P1

其中P1’和P2’是变换后三角形的两个顶点,T’代表变换三角形的边的切线,因此,模型视口矩阵保留了切线,但不会保留法线.

考虑用于向量T的方法,我们可以找到两个点Q1和Q2:

N = Q 2 − Q 1 N=Q_2 - Q_1 N=Q2Q1
主要问题在于,通过变换后的点Q2’,Q1’定义的向量,不一定保持垂直,如上图所示,法向量不是定义为两点之间的差异,就像切向量一样,它被定义为垂直于表面的向量.

所以现在我们知道不可以在任何情况下都使用模型视口矩阵来变换法向量,那下面的问题是应该使用什么矩阵.

考虑一个3X3的矩阵G,然后让我们看看如何计算这个矩阵以正确的变换法向量.

我们知道在矩阵变换之前T·N=0,因为从定义上来说这两个向量相互垂直,我们也知道在变换后T’·N’一定仍然是0,因为他们必须保持垂直, T可以安全的乘以模型视口矩阵左上角3x3的子矩阵(T是一个向量,因此W分量为零),我们称此矩阵为M.

让我们假设矩阵G是用于变换法向量的正确的矩阵,因此有下面的等式:

N ′ ⋅ T ′ = ( G N ) ⋅ ( M T ) = 0 N'·T' = (GN)·(MT) = 0 NT=(GN)(MT)=0
点积可以转化为向量的乘积,因此:
( G N ) ⋅ ( M T ) = ( G N ) T ⋅ ( M T ) (GN)·(MT) = (GN)^T·(MT) (GN)(MT)=(GN)T(MT)
注意,必须考虑第一个向量的转置,因为这是乘以向量所必须的.我们还知道乘法的转置是转置的乘法,因此:
( G N ) T ⋅ ( M T ) = N T G T M T (GN)^T·(MT)=N^TG^TMT (GN)T(MT)=NTGTMT

我们在开始的时候声明了N和T的点积为0,所以如果:

G T M = I G^TM = I GTM=I
然后还有:
N ′ ⋅ T ′ = N ⋅ T = 0 N'·T' =N·T=0 NT=NT=0

这正是我们想要的,所以我们可以基于M计算G
G T M = I G = ( M − 1 ) T G^TM=I\\G=(M^{-1})^T GTM=IG=(M1)T

于是用于变换法线的正确的矩阵就是M矩阵的逆矩阵的转置,OpenGl使用gl_NormalMatrix表示.

在这一节的开始指出在某些情况下使用模型视口矩阵是可行的,每当模型视口矩阵的3X3左上子矩阵是正交的时候我们就有:
M − 1 = M T G = M M^{-1} = M^T \\G=M M1=MTG=M
这是因为在正交矩阵中,转置矩阵与逆矩阵相等。那么,什么是正交矩阵呢?正交矩阵是一个矩阵,其中所有列/行都具有单位长度,并且彼此相互垂直。这意味着当两个向量通过这样一个矩阵相乘时,经过正交矩阵变换后它们之间的夹角与变换之前相同。简单来说,这种变换保持了向量之间的夹角关系,因此变换后的法向量仍然垂直于切向量!此外,它也保持了向量的长度不变。

那么,我们何时可以确信矩阵M是正交的呢?当我们将几何操作限制在旋转和平移上时,即在OpenGL应用中仅使用glRotate和glTranslate而不使用glScale时,可以确保M是正交的。请注意:gluLookAt也创建了一个正交矩阵!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
COMPUTE_VIEW_NORMAL是一个在Unity引擎的UnitCG.cginc文件中定义的宏。它用于计算视图法线,具体的定义如下: #define COMPUTE_VIEW_NORMAL normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal))。这个宏的作用是将顶点法线转换为视图空间下的法线,并进行归一化处理。在计算过程中,它使用了UNITY_MATRIX_IT_MV矩阵来将法线从模型空间转换到视图空间。通过这种方式,可以在渲染期间对法线进行变换和改变。这个宏主要用于处理法线贴图,可以同时处理DXT5nm和DXT格式的法线贴图,并正确地解码出法线贴图中的扰动向量。如果使用了DXT5nm压缩格式或者BC5压缩格式的法线贴图纹理,引擎会调用UnpackNormalmapRGorAG函数对法线进行解码。而如果没有使用这两种压缩格式,引擎会直接将法线进行归一化处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UnityShader源码2017---学习笔记与自我拓展046](https://blog.csdn.net/u012871784/article/details/81585580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [(三)unity自带的着色器源码剖析之——————UnityCG.cginc文件(下篇:法线贴图及编解码操作函数、线性...](https://blog.csdn.net/cgy56191948/article/details/104123461)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值