Normal Mapping (一)

openGL高级光照部分目录 见 openGL高级光照部分目录

我们所有的场景都是用网格填充的,每个网格都由数百个或数千个三角形组成。我们通过在这些平面三角形上包装二维纹理来增强真实感,隐藏了多边形只是很小的平面三角形的事实。纹理有帮助,但是当你仔细观察网格时,仍然很容易看到底层的平面。然而,大多数现实生活中的表面并不是平坦的,并且显示出很多(凹凸不平)的细节。

例如,以砖表面为例。砖的表面是一个相当粗糙的表面,而且显然不是完全平坦的:它含有凹陷的水泥条纹和许多细密的小孔和裂缝。如果我们在一个明亮的场景中看到这样一个砖块表面,沉浸感很容易被打破。下面我们可以看到一个砖块纹理应用到一个由点光源照亮的平面上。

灯光没有考虑到任何小裂缝和洞,完全忽略了砖之间的深条纹;表面看起来非常平坦。我们可以通过使用高光贴图来部分修复平面外观,因为深度或其他细节,某些曲面的照明度较低,但这更像是一种破解,而不是真正的解决方案。我们需要的是一些方法来通知照明系统所有的小深度,如表面的细节。

如果我们从光的角度来思考这个问题:为什么表面被照亮成一个完全平坦的表面?答案是曲面的法向量。从照明技术的角度来看,它决定物体形状的唯一方法是通过其垂直法向量。砖块曲面只有一个法线向量,因此曲面将基于该法线向量的方向均匀照明。如果我们不是对每个片段使用相同的每曲面法线,而是对每个片段使用不同的每片段法线,会怎么样?通过这种方法,我们可以根据曲面的小细节稍微偏离法线向量;这会给人一种错觉,即曲面要复杂得多:

通过使用每个片段法线,我们可以欺骗灯光,使其相信曲面由微小的平面(垂直于法线向量)组成,从而使曲面在细节上得到了极大的提升。这种使用逐片段法线与逐曲面法线的技术称为法线贴图或凹凸贴图。应用于砖平面时,它看起来有点像:

正如你所看到的,它在细节上给予了巨大的提升,而且成本相对较低。因为我们只改变每个片段的法向量,所以不需要改变光照方程。现在,我们将逐片段法线传递给照明算法,而不是插值曲面法线。剩下的就由灯光来完成了。

法线映射

为了使法线映射正常工作,我们需要一个每个片段的法线。与我们对漫反射和高光贴图所做的类似,我们可以使用2D纹理来存储每个片段的法线数据。这样我们就可以对2D纹理进行采样以获得特定片段的法线向量。

虽然法向量是几何实体,纹理通常仅用于颜色信息,但在纹理中存储法向量可能不会立即明显。如果你考虑纹理中的颜色向量,它们被表示为带有r、g和b分量的3D向量。同样,我们可以将法向量的x、y和z分量存储在各自的颜色分量中。法向量的范围在-1和1之间,因此它们首先映射到[0,1]:

vec3 rgb_normal = normal * 0.5 + 0.5; // transforms from [-1,1] to [0,1]

vec3 rgb_normal = normal * 0.5 + 0.5; // transforms from [-1,1] to [0,1] 

通过将法线向量转换为RGB颜色分量,我们可以将从曲面形状派生的每个片段法线存储到2D纹理上。本章开头的砖表面法线贴图示例如下所示:

这张照片(以及你在网上找到的几乎所有法线贴图)都是蓝色的。这是因为法线都非常向外指向正z轴(0,0,1):蓝色。颜色上的偏差表示法线向量,这些向量与一般的正z方向稍有偏移,使纹理具有深度感。例如,您可以看到,在每个砖的顶部,颜色趋向于更绿,这是有意义的,因为砖的顶面将有更多的法线指向正y方向(0,1,0),这正好是绿色!

用一个简单的平面,看z轴的正方向,我们可以用这个漫反射纹理和这个法线贴图来渲染上一节的图像。请注意,链接的法线贴图与上面显示的不同。原因是OpenGL读取纹理坐标时,y(或v)坐标与通常创建纹理的方式相反。因此,链接法线贴图的y(或绿色)分量反转(可以看到绿色现在指向下方);如果不考虑这一点,照明将不正确。加载两个纹理,将它们绑定到适当的纹理单位,并在照明片段着色器中使用以下更改渲染平面:

uniform sampler2D normalMap;  

void main()
{           
    // obtain normal from normal map in range [0,1]
    normal = texture(normalMap, fs_in.TexCoords).rgb;
    // transform normal vector to range [-1,1]
    normal = normalize(normal * 2.0 - 1.0);   
  
    [...]
    // proceed with lighting as normal
}  

在这里,我们将法线映射到RGB颜色的过程相反,方法是将采样的法线颜色从[0,1]重新映射回[-1,1],然后将采样的法线向量用于即将到来的照明计算。在本例中,我们使用了Blinn Phong着色器。

随着时间的推移慢慢移动光源,使用法线贴图可以真正获得深度感。运行此法线映射示例将得到本章开头所示的精确结果:

但是有一个问题极大地限制了法线贴图的使用。我们使用的法线贴图的法向量都指向正z方向。这是因为平面的曲面法线也指向正z方向。但是,如果我们在一个平面上使用相同的法线贴图,并且曲面法向量指向正y方向,会发生什么情况?

灯光看起来不对劲!发生这种情况是因为该平面的采样法线仍然大致指向正z方向,即使它们大部分应该指向正y方向。因此,当平面指向z正方向时,灯光会认为曲面的法线与之前相同;照明不正确。下图显示了采样法线在该曲面上的大致外观:

你可以看到所有法线都指向z方向,它们本该朝着表面法线指向y方向的。一个可行方案是为每个表面制作一个单独的法线贴图。如果是一个立方体的话我们就需要6个法线贴图,但是如果模型上有无数的朝向不同方向的表面,这就不可行了(中文版译者注:实际上对于复杂模型可以把朝向各个方向的法线储存在同一张贴图上,你可能看到过不只是蓝色的法线贴图,不过用那样的法线贴图有个问题是你必须记住模型的起始朝向,如果模型运动了还要记录模型的变换,这是非常不方便的;此外就像作者所说的,如果把一个diffuse纹理应用在同一个物体的不同表面上,就像立方体那样的,就需要做6个法线贴图,这也不可取)。

另一种解决方案是在不同的坐标空间中进行所有照明:法线贴图向量始终指向正z方向的坐标空间;然后,所有其他照明向量都将相对于该正z方向进行变换。这样,无论方向如何,我们都可以使用相同的法线贴图。这个坐标空间称为切线空间

猜你喜欢

转载自blog.csdn.net/tiao_god/article/details/107304438
今日推荐