Normal Mapping & Tangent Space

大憨熊 提交于 2019-12-05 02:23:09

讲道理,这篇blog大概是我思考最久的主题了(大概花了2天的时间,期间找了很多的参考资料看),那么我现在在这里尝试着将它们说清楚。


首先是Normal Mapping法线贴图。

一般的,为了增加物体表面的细节,我们会使用法线贴图。为什么? 我们可以仔细想一下光照渲染中的各种步骤,除了ambient/diffuse, specular等纹理贴图采样,剩下的就是各种各样的光照效果,而这些光照效果都由什么计算出来呢:答案是光的位置,以及法线方向(视线焦点暂且不算,虽然最后看到的结果也有视线的一部分因素)。那么自然而然,我们就可以考虑更改原来贴图的法线方向来改变这些光照细节,从而使得渲染的效果更加棒(与其增加模型中多边形的数量,不如直接靠光照的明暗来营造细节)。

法线贴图存储高模图形的法线值,所谓高模图形,就是细节更加丰富的图形。我们可以将一些原本(已经渲染好的)图形拿出,把它们的法线方向(值)用颜色纹理的方式存起来,再给低模的图形用。

是的,用的是颜色的方式。在生成法线贴图的时候法线一般是指向正Z轴的(后面会讲到,一般法线贴图所处的空间被称作切空间Tangent Space, 也有的在object-space(既local space)或者world space中),也就是说它们的值偏向正Z轴(0, 0, 1),用颜色通道的方式存储的话就是淡蓝色(“blue-ish")。

事情到这里貌似结束了:我们给物体表面一个简单的(低模)材质贴图,然后再使用法线贴图使得物体表面细节更加的丰富(采样法线值,映射到[-1, 1],然后使用这个法线值进行各种计算)。

 

然而远没有这么简单,我们能用法线贴图正常进行光照渲染的前提是材质贴图的(法线)方向和法线贴图的方向相同,一旦方向不同就会导致渲染出来的东西不符合逻辑(因为我们没有对(位于切线空间中)法线贴图里的法线方向进行变换,或者也可以暂时把这个法线贴图当作是从world space得来的)。

那么我们能用什么办法解决这个问题?(假设当前法线贴图是从世界空间中得到的,也就是说不能变换)很简单,我们只要将法线贴图改成从Object-space(local space)或者Tangent space中取得的就行了。

如果是Object space的话,法线贴图一般是五彩斑斓的(world space也是),此时我们可以通过将采样得到的normal值在Vertex Shader中进行坐标的变换(用model矩阵),之后的渲染结果是正常的。

而如果是Tangent space的话也能达到和Object space同样的渲染效果,但是Tangent space normal map不单单只是如此而已,对于一些发生形状改变(deform)的纹理(比如说用Tangent space normal map去渲染一个球形,而这个normal map原本只是从一个高模2D平面得到的)Tangent space normal map也可以做到添加细节。这就是牛叉之处了。


 

下面讲解Tangent space的知识:

首先Tangent Space是针对于一个渲染单元(图元Primitive)来说的,当然法线贴图一般也位于Tangent Space(这也就是说它是从高模的Tangent Space中获取的),这个空间的正Z半轴永远都是从里到外,我们称这个Z轴为N(Normal)轴,那么另外两个轴呢?一般来讲,对于简单的几何形状,我们可以自己定义剩下的两个轴(这两个轴分别叫做Tangent 和 Bitangent),只要它们相互垂直就行;而对于更加简单的三角形网格,我们可以考虑用模型顶点的位置坐标随着纹理坐标(u, v)的变化(Δu, Δv)作为切线空间剩下的两个轴(的方向)。如下图:

P.S.由于在切线空间中N轴都是从里向外的,因此我们可以只存储B T轴的方向,然后叉乘得到N轴。

 

 那么怎么计算呢,很简单,用线性代数的相关知识就行了(TBN都为单位基向量):

\[E1 = \Delta U1\overrightarrow{T} + \Delta V1\overrightarrow{B}\]

\[E2 = \Delta U2\overrightarrow{T} + \Delta V2\overrightarrow{B}\]

写成坐标的形式就是:

\[(E_{1x},E_{1y}, E_{1z}) = \Delta U1(T_{x}, T_{y}, T_{z}) + \Delta V1 (B_{x}, B_{y}, B_{z})\]

\[(E_{2x},E_{2y}, E_{2z}) = \Delta U2(T_{x}, T_{y}, T_{z}) + \Delta V2 (B_{x}, B_{y}, B_{z})\]

 再转化成矩阵的形式:

\[\begin{pmatrix} E_{1x} & E_{1y} & E_{1z}\\ E_{2x} & E_{2y} & E_{2z} \end{pmatrix} = \begin{pmatrix} \Delta U1 & \Delta V1\\ \Delta U2 & \Delta V2 \end{pmatrix} \begin{pmatrix} T_{x} & T_{y} & T_{z}\\ B_{x} & B_{y} & B_{z} \end{pmatrix}\]

两边同乘以ΔUΔV的逆矩阵,得:

\[\begin{pmatrix} \Delta U1 & \Delta V1\\ \Delta U2 & \Delta V2 \end{pmatrix}^{-1} \begin{pmatrix} E_{1x} & E_{1y} & E_{1z}\\ E_{2x} & E_{2y} & E_{2z} \end{pmatrix} = \begin{pmatrix} T_{x} & T_{y} & T_{z}\\ B_{x} & B_{y} & B_{z} \end{pmatrix}\]

将逆矩阵进一步化简(具体来说\[A^{-1} = \frac{1}{\left | A \right |} A^{*}\])可得:

\[\begin{pmatrix} T_{x} & T_{y} & T_{z}\\ B_{x} & B_{y} & B_{z} \end{pmatrix} = \frac{1}{\Delta U1 \Delta V2 - \Delta U2 \Delta V1} \begin{pmatrix} V2 & -V1 \\ -U2 & U1 \end{pmatrix} \begin{pmatrix} E_{1x} & E_{1y} & E_{1z}\\ E_{2x} & E_{2y} & E_{2z} \end{pmatrix}\]

有了这个等式之后,我们就可以用三角形的两条边(E1, E2)以及纹理坐标计算出单位基向量T和B了(N直接叉乘,或者不算都OK,因为N的值在Tangent space中固定为(0.0, 0.0, 1.0)。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!