法线贴图(凹凸贴图)

法线贴图(凹凸贴图) - 图1

法线贴图是 凹凸贴图 的一种。凹凸贴图是一种特殊的纹理,允许为模型添加表面细节,例如凸起、凹槽和划痕光,这些细节将捕获光线,就像真实的几何体一样。

举个例子,你可能想要显示一个具有凹槽、螺丝钉或铆钉的表面,例如飞机机身。一种实现方式是为这些细节进行几何建模,如下所示。

A sheet of aircraft metal with details modeled as real geometry.

一张飞机金属板,以真实的几何形状为细节建模。

以真实几何形状为如此细微的细节建模通常不是一个好主意。在上图右侧, 你可以看到组成单个螺丝头细节所需的多边形。带有大量清晰细节的大型模型,将需要绘制非常多数量的多边形。为了避免这种情况,应该使用法线贴图来表示清晰的表面细节,较大形状的模型使用较低分辨率的多边形表面。

如果使用一张凹凸贴图来呈现细节,表面的几何形状可以变得更加简单,用细节纹理调制光从表面反射的方式。现代图形硬件可以非常快地执行这个个过程。现在,金属表面是一个低面数(低多边形)的平面,螺丝、铆钉和划痕将捕获光线,并且因为纹理贴图,它们看起来似乎有了深度。

The screws, grooves and scratches are defined in a normalmap, which modifies how light reflects off the surface of this low-poly plane, giving the impression of 3D detail. As well as the rivets and screws, a texture allows us to include far more detail like subtle bumps and scratches.

法线贴图中定义了螺钉、凹槽和划痕,修改了从低面数平面外表反射光线的方式,从而使这些细节呈现出 3D 效果。除了铆钉和螺丝,纹理支持更多的细节,例如微小的凸起和划痕。

在现代游戏开发流程中,设计师将使用 3D 建模软件,基于非常高分辨率的原始模型生成法线贴图。然后,将法线贴图映射到较低分辨率的模型(游戏中实际使用的模型)上,这样,通过法线贴图,原始的高分率细节就被渲染了。

如何创建和使用凹凸贴图

凹凸贴图是一种相对古老的图形技术,但仍然是创建细节逼真的实时图形所需的核心方法之一。凹凸贴图通常也被称为 法线题图高度图,不过这些术语的含义略有不同,将会在下面解释。

什么是表面法线?

为了真正理解法线贴图的工作原理,需要先清楚什么是 法线,以及如何在实时光照中使用法线。也许最基本的例子是,一个模型的多边形表面根据表面相对于光线的角度简单地被点亮。表面角度可以表示为一条垂直于表面并向上突出的线,是一个向量,这条线成为 表面法线,或者简单地称为 法线

Two 12-sided cylinders, on the left with flat shading, and on the right with smoothed shading

两个 12 面圆柱体,左边是平面着色,右边是平滑着色。

在上面的图像中,左边的圆柱体具有基本的平面着色,每个多边形按照其相对于光源的角度被着色。因为表面是平坦的,每个多边形区域大小一样,所以,每个多边形上的光照是恒定的。下面是这两个圆柱体的线框网格:

Two 12-sided cylinders, on the left with flat shading, and on the right with smoothed shading

两个 12 面圆柱体,左边是平面着色,右边是平滑着色。

右边的模型具有与左边模型相同数量的多边形,但是着色看起来是平滑的 — 覆盖多边形的光照呈现为一张曲线表面。为什么会这样?原因是,沿着多边形,每个点用于反射光线的表面法线逐渐变化,因此,对于表面上的任意给定点,就好像它的表面真的弯曲了,而不是平坦的恒定多边形,

平面着色圆柱体表面上相邻 3 个多边形的 2D 图看起来像下面这样:

Flat shading on three polygons, viewed as a 2D diagram

3 个平面着色多边形的 2D 图。

表面法线用橙色剪头表示。它们的值用于计算表面如何反射光线。在每个多边形上,因为表面法线的方向相同,所以反射同样的光线。所以左侧圆柱体呈现出平面着色,并且具有硬边缘。

然而,对于平滑着色圆柱体,表面法线沿着平面多边形逐渐变化,如下所示:

Smooth shading on three polygons, viewed as a 2D diagram

3 个平滑着色多边形的 2D 图。

法线方向沿着平面多边形表面逐渐变化,所以看起来像是平滑曲面(用绿线表示)。这并不会影响网格的多边形特质,只会影响表面的光照计算。这种视觉上的曲线表面并不是真正存在,在掠射角观察时将显示平面多边形的本质,尽管如此,从大多数视角查看,这个圆柱体看起来具有平滑的曲线表面。

使用这种基本的平滑着色时,决定法线方向的数据实际上只存储每个定点,所以,沿着表面变化的法线数据其实是相邻两个定点之间的插值。在上图中,红色剪头表示每个顶点存储的法线方向,黄色剪头表示多边形区域的法线方向插值示例。

什么是法线贴图?

更进一步,用法线贴图表示表面法线的这种变化。法线贴图通过通过一张纹理来存储如何改变表面法线的信息。法线贴图是一张映射到模型表面的图像纹理。有些类似于常规的颜色纹理,不同的是,法线贴图中每个顶点(称为纹素)还表示了在表面法线方向上离开平面(或平滑)多边形真实表面的距离。

Normal mapping across three polygons, viewed as a 2D diagram

3 个相邻多边形的法线贴图的 2D 图。

在这张 3D 模型表面的 3 个相邻多边形的 2D 示意图中,每个橙色剪头对应法线贴图中的一个像素。下面是法线贴图的每个像素切片。在中间位置,你可以看到法线被改变了,在多边形表面上呈现出多个凸起。只在当光照出现在表面上时,这些凸起才会显示,因为这些被改变的法线只用于光照计算。

原始法线贴图文件中的可见颜色呈现蓝色色调,并且不含有任何光照或阴影着色 — 这是因为这些颜色本身并不会显示。相反,每个纹素的 RGB 值表示的是一个方向矢量的 X、Y、Z 值,用于修改多边形表面平滑法线的插值。

An example normal map texture

一个法线贴图示例。

这是一张简单的法线贴图,包含了一些矩形和文本的凹凸信息。这张发现贴图可以导入 Unity,并放入标准着色器的法线贴图插槽。把把它和颜色贴图(即漫反射贴图)一起应用在圆柱体网格的表面上时,效果如下所示:

The example normal map applied to the surface of the cylinder mesh used above

把这张法线贴图应用到上面的圆柱体网格表面。

同样不会影响网格的多边形特质,只是用于计算表面的光照。这种视觉上的字符和形状凸起并不真正存在,在掠射角观察时讲现实平坦表面的本质,尽管如此,在大多数视角查看,这个圆柱体表面呈现出凸起的浮雕细节。

如何获取或制作法线贴图?

通常情况下,法线贴图是同 3D 或纹理设计师制作的模型或纹理一起生成的,通常反应了漫反射贴图的结构和内容。有时也会手工制作,有时由 3D 软件渲染生成。

3D 软件如何渲染法线贴图超出了本文档的范围,但是基本的概念是,3D 设计师会为一个模型生成两个版本 — 一个包含了所有多边形细节的超高分率模型,和一个程序用的低分辨率模型。在游戏中,高分辨率模型含有太多细节(网格中有太多的三角形),以至于不适合在游戏中运行,但是用于在 3D 模型软件中生成法线贴图。低分辨率模型则可以运行在游戏中,并且忽略那些存储在法线贴图中的非常清晰的几何细节,几何细节则用法线贴图渲染。一个典型的用例是,在角色服饰上现实折痕、纽扣、带扣和接缝。

有些软件包可以分析普通摄影纹理中的光照信息,并从中提取法线贴图。工作原理是,假设一束平行光照射到原始纹理上,然后分析浅色和暗色区域,作为法线的角度。不过,在真正使用凹凸贴图时,你需要确保漫反射纹理中不含有任何方向的光照 — 理想情况下,它应该呈现完全无光情况下的表面颜色 — 因为光照信息由 Unity 根据光照角度、表面角度和凹凸贴图计算。

下面是两个示例,一个是简单重复的石墙问题以及对应的法线贴图,一个是角色的纹理图集以及对应的法线贴图。

A stone wall texture and its corresponding normal map texture

一个石墙纹理及其对应的法线贴图。

A character texture atlas, and its corresponding normal map texture atlas

一个角色纹理图集及其对应的法线贴图集。

凹凸贴图、法线贴图和高度图有什么区别?

法线贴图高度图都属于凹凸贴图类型。它们都含有用于简化多面性网格的表面视觉细节,但是以不同的方式存储数据。

On the left, a height map for bump mapping a stone wall. On the right, a normal map for bump mapping a stone wall.

左边是映射石墙凹凸信息的高度图,右边是映射石墙凹凸信息的法线贴图。

在左图中,可以看到一张映射石墙凹凸信息的高度图。高度图是一张简单的黑白纹理,每个像素代表了表面上该点应该凸起的高度,像素点颜色越白,该区域看起来更高。

法线贴图是一张 RGB 纹理,每个像素代表了表面相对于真实法线(垂直)的朝向(夹角)。这些纹理往往是蓝紫色调,因为矢量用 RGB 值存储。

现代实时 3D 图形硬件更信赖(依赖)法线贴图,因为法线贴图包含了表面应该如何弹射光线所需的矢量信息。Unity 的凹凸贴图也可以接受高度图,但导入必须转换为法线贴图才能使用。

为什么是蓝紫色?

对于使用法线贴图,理解为什么并不重要!跳过这一段也没关系。但是,如果你真的想知道的话:RGB 颜色值用于存储矢量的 X、Y、Z,并且 Z 轴向上(与 Unity 使用的 Y 轴向上惯例相关)。此外,纹理中的值被减半处理,并加了 0.5。这样可以存储所有方向的矢量。所以,要将 RGB 值转换为矢量方向,必须乘以 2 然后减 1。例如,RGB 值 (0.5, 0.5, 1) 或者十进制 #8080FF 转换后是 (0,0,1),表示垂直的法线贴图 — 表示模型表面无变化。这是本页前面演示法线贴图平坦区域时的颜色。

A normal map using only #8080FF, which translates to a normal vector of 0,0,1 or straight up. This applies no modification to the surface normal of the polygon, and therefore produces no change to the lighting. Any pixels which are different to this colour results in a vectors that point in a different direction - which therefore modify the angle that is used to calculate light bounce at that point.

一张只使用 #8080FF 的法线贴图,转换为矢量是 0,0,1,即垂直向上。这张贴图不会改变多边形的表面向量,因此光照不会变化。任何与该颜色不同的像素点,都会导致矢量指向不同的方向 — 改变后的角度用于计算该像素点上反射光的角度。

RGB 值 (0.43, 0.91, 0.80) 给出一个矢量 (–0.14, 0.82, 0.6),表示表面非常的陡峭。可以在石墙法线贴图的亮青色区域看到这种颜色,对应于一些石头的边缘。相对于石头上较平坦的表面,这些边缘以非常不同的角度捕获光线。

译注:rgb(110,232,204)

The bright cyan areas in the normalmap for these stones show a steep modification to the polygons surface normals at the top edge of each stone, causing them to catch the light at the correct angle.

这些石头的法线贴图中的亮青色区域显示了对每个石头顶部边缘的多边形表面法线的陡峭修改,使得它们以正确的角度捕获光线。
在这些石头的法线图中,亮青色区域对每个石头顶部边缘的多边形表面法线进行了陡峭的修改,使得它们以正确的角度捕获光线。

法线贴图

A stone wall with no bumpmap effect. The edges and facets of the rock do not catch the directional sun light in the scene.

一堵没有凹凸效果的石墙。岩石的边缘和表面不会捕捉场景中的平行太阳光。

The same stone wall with bumpmapping applied. The edges of the stones facing the sun reflect the directional sun light very differently to the faces of the stones, and the edges facing away.

应用了凹凸贴图的同一堵石墙。岩石面向太阳的边缘反射的平行太阳光,与岩石的表面和不朝向太阳的边缘,非常不同。

The same bumpmapped stone wall, in a different lighting scenario. A point light torch illuminates the stones. Each pixel of the stone wall is lit according to how the light hits the angle of the base model (the polygon), adjusted by the vectors in the normal maps. Therefore pixels facing the light are bright, and pixels facing away from the light are darker, or in shadow.

不同光照情况下,应用了凹凸贴图的同一堵石墙。一个火把点光源照亮了石头。光线撞击基础模型(多边形)的角度,被法线贴图中的矢量所调整,然后点亮石头的每个像素。因此,面向光源的像素是亮的,背光的像素较暗或在阴影中。

如何导入和使用法线贴图和高度图

像平常一样,把纹理文件放入 Assets 文件夹,就可以导入法线贴图。不过,你需要告诉 Unity 这个纹理是一张法线贴图。可以在导入资源的检视视图中,把改变『纹理类型』为『法线贴图』。

法线贴图(凹凸贴图) - 图19

把黑白高度图导入为法线贴图的过程,与直接导入法线贴图几乎完全相同,除了需要选中复选框『Create From Greyscale』。

法线贴图(凹凸贴图) - 图20

选中『Create From Greyscale』后,将出现一个凹凸度滑动器。你可以使用使用这个滑动器,来控制高度图的高度转换为法线贴图中的角度时的陡峭程度。低凹凸度将意味着,即使是高度图中的强烈对比,也将被转换为平缓的角度和凹凸。高凹凸度将创建夸张的凹凸,产生高对比度的光照响应。

Low and High Bumpiness settings when importing a height map as a normal map, and the resulting effect on the model.

把高度图导入为法线贴图时,凹凸度高低对模型的影响。

Assets 文件夹中有了法线贴图后,就可以在检视视图中把它放入材质的法线贴图插槽。标准着色具有一个法线贴图插槽,许多旧的传统着色器也支持法线贴图。

Placing a normal map texture into the correct slot in a material using the Standard Shader

把一张法线法线贴图放入标准着色器材质的正确插槽中。

如果导入了一张法线贴图或高度图,但是没有把它标记为法线贴图(选择纹理类型:法线贴图),材质的检视视图将提醒你修正它,像这样:

The Fix Now warning appears when trying to use a normalmap that has not been marked as such in the inspector.

当尝试在法线贴图插槽上使用未标记为法线贴图的纹理时,将显示『立即修复』警告。

点击『立即修复』,与在纹理检视视图中选择纹理类型:法线贴图,具有相同的效果。如果纹理是一张法线贴图,这么做是有效的。但是,如果它其实是一张灰阶高度图,就无法自动检测到 — 所以对于高度图,你必须总是在纹理的检视视图中选中『Create from Greyscale』。

辅助法线贴图

你可能还注意到,在标准着色器材质的底部有一个辅助法线贴图插槽。它允许你再使用一张法线贴图来创建额外的细节。你可以把一张法线贴图添加到这个插槽中,就像常规法线贴图的插槽一样,但是,你应该使用不同的尺寸或平铺频率,这样,两张法线贴图在不同的尺度上,共同产生高级细节。例如,常规法线贴图可以定义墙壁或车辆上的镶板细节和镶板边缘的凹槽。辅助贴图可以为表面上划痕和磨损提供非常精细的凸起细节,平铺次数可能是基础法线贴图的 5 到 10 倍。这些细节可能非常惊喜,只有贴脸检查才能看到。为了在基础法线贴图上达到这个量级的细节,基础法线贴图需要非常大才行,尽管如此,通过两张不同尺寸但相对较小的法线贴图,可是实现高级细节。