我所了解的法线贴图

Posted by 蔡华的博客 on March 16, 2017

两篇文章

一些基本的概念

1.法线贴图的定义

顾名思义,法线贴图保存的是法线的信息贴图。

2.用来干什么

用来进行凹凸映射。

凹凸映射的目的是使用一张纹理来修改模型表面的法线,以便为模型提供更多的细节。这种方法不会真的改变模型的顶点位置,只是让模型看起来好像是“凹凸不平”的,但可以从模型的轮廓处看出“破绽”。

有两种主要的方法可以用来进行凹凸映射:一种方法是使用一张高度纹理(height map)来模拟表面位移(displacement),然后得到一个修改后的法线值,这种方法也被称为高度映射(height mapping);另一种方法则是使用一张法线纹理(normal map)来直接存储表面法线,这种方法又被称为法线映射(normal mapping)。尽管我们常常将凹凸映射和法线映射当成是相同的技术,但要知道它们之间的不同。

3.种类

  • 世界空间下的法线纹理
  • 模型空间下的法线纹理
  • 切线空间下的法线纹理

对比不同的法线贴图

  • 首先按照不同的坐标系得到了不同类型的法线贴图。模型顶点的法线根据所处坐标系的不同,在成图后表现也是不同的。以模型法线贴图和切线法线贴图为例:

上图 左边: 模型空间下的法线纹理 右边: 切线空间下的法线纹理

  • 不同的颜色是因为法线作为一个Vector3类型的需要转换到2D的颜色,而通常来说我们可以把颜色的RGB看成一个坐标系,这样法线就对应了一个RGB颜色。但是法线的范围是[-1,1],而颜色是没有负值的,因此有以下的转换过程。

法线纹理存储的是表面的法线方向。由于法线方向的分量范围在[-1, 1],而像素的分量范围为[0, 1],因此我们需要做一个映射,通常使用的映射就是: pixel=(normal + 1) / 2 这就要求,我们在Shader中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向。反映射的过程实际就是使用上面映射函数的逆函数: normal=pixel × 2 - 1

1.world space normal map

一旦从贴图里解压出来后,就可以直接用了,效率很高.但是有个缺点,这个world space normal 是固定了,如果物体没有保持原来的方向和位置,那原来生成的normal map就作废了。

  • 世界坐标下的顶点的法线是现成的,因此好用。不过如果进入模型在U3D里面进行了位置或者方向的转换,那么在没有转换矩阵的情况下,法线信息就是错误的,也就无法使用了。进一步思考如果场景中存在大量的静态模型,可以考虑用这个。

2.object space normal map

对于模型顶点自带的法线,它们是定义在模型空间中的,因此一种直接的想法就是将修改后的模型空间中的表面法线存储在一张纹理中,这种纹理被称为模型空间的法线纹理(object-space normal map)。

  • 对象空间的法线贴图,这个贴图中记录的法线信息是基于模型空间的,因此数值是相对的,这样模型在场景中是可以位移和旋转的,只要在计算的时候乘上对应的矩阵即可。而且从上面的图里可以看到贴图是彩色的,因为模型上的顶点法线在这个空间中是朝各个方向的。

  • 对象空间的法线贴图比起世界空间的在使用了已经有了很大的进步,不过它仍旧有自己的局限性,就是这样的贴图还是依赖于模型本身。从其定义也可以看出来,它也是一种“绝对位置”。如果模型发生了形变,则这个贴图的信息就是错误的。从目前来看,我只能想到模型在场景中存在动画这个问题。

3.tangent space normal map

对于模型的每一个顶点,它都有一个属于自己的切线空间,这个切线空间的原点就是该顶点本身,而z轴是顶点的法线方向(n),x轴是顶点的切线方向(t),而y轴可由法线和切线叉积而得,也被称为副切线(bitangent, b)或副法线。这种纹理被称为是切线空间的法线纹理(tangent-space normal map)。

  • 定义很好理解,从本质上讲切线空间的法线贴图解决了模型变形的问题。因为在另一个模型上的顶点的切线空间坐标系里面,法线贴图中的信息是可以使用的,它不依赖于模型本身、也不依赖于模型所处的坐标系。

4.切线空间下的法线贴图如何生成

  • 以3DMAX为例,法线贴图需要高模和低模配合,具体过程不说了,网上大把的视频。
  • 重点是法线贴图的生成需要高模和低模,因为没有高模就不知道法线方向,没有低模,就不知道高模上某点的法线对应于低模上哪个点。
  • 下面才是重点,因为多个高模的面使用了同一个低模的面,因此在生成法线贴图时,高模不能使用自己的tangent space,而是使用低模的tangent space。这样一些高模上的点的法线与低模面上的法线出现了不一致,你可以想象低模上的某个面上的法线指向一个方向,但是对应了几个高模的点,可能一些点的法线与低模面的法线方向一样,那么很好,完美融入低模的切线空间,颜色呈现出淡蓝色。但是其它那些和低模面法线不一致的点的法线就产生了夹角,就造成了在切线法线贴图上那些不是[0.5,0.5,1]色值的点。

    低模上的这个tangent space,也必须与高模上的坐标系tangent space。因为低模上的一个面,可能对应了高模上的几个面(精度高),按照新方法每个面都有一个局部坐标系,那对于低模上的每个面,高模因为存在好几个面,就会出现好几个局部坐标系,这肯定是不行的。所以高模所用的tangent space,就是低模上的。生成法线贴图,必定会确认高模上哪些面都对应低模上的哪个面,然后高模上的这几个面的法线,都会转换为低模这个面上所构建的tangent space的坐标。这样,当低模变形时,即三角面变化时,它的tangent space也会跟着变化,保存在贴图里的法线乘以低模这个面的tangent space到外部坐标系的转换矩阵即可得到外部坐标。顺便再提一点,高模保存的这个法线,是高模上object space里的法线。

  • 对于上述的内容,这张图做了很好的诠释。

如何将高模的法线贴图用在低模上

  • 对于object space normal map,低模的object space坐标系与高模中的object space坐标系是重合的。所以不需要构建,所以低模上某点才能直接用高模的法线替换自己的法线。