法线贴图

在三维计算机图形学中，法线贴图（Normal mapping）是一种模拟凹凸处光照效果的技术，是凸凹贴图的一种实现。法线贴图可以在不添加多边形的前提下，为模型添加细节。常见的使用场景是为低多边形模型改善外观、添加细节，此时的法线贴图一般根据高多边形模型或高度贴图生成。

法线贴图通常以普通RGB图像的形式存储，其中的R、G、B分量分别对应法线的X、Y、Z坐标。

法线贴图有时也称为“Dot3（仿立体）凸凹纹理贴图”。凸凹与纹理贴图通常是在现有的模型法线添加扰动不同，法线贴图要完全更新法线。与凸凹贴图类似的是，它也是用来在不增加多边形的情况下在浓淡效果中添加细节。但是凸凹贴图通常根据一个单独的灰度图像通道进行计算，而法线贴图的数据源图像通常是从更加细致版本的物体得到的多通道图像，即红、绿、蓝通道都是作为一个单独的颜色对待。

法线贴图通常有两个变体，即物体空间与tangent-space法线贴图，它们的不同之处在于法线测量与储存所用坐标系统。

这项技术的一个有趣的应用是使用高分辨率模型的法线贴图表大幅度地提高low poly低面模型的显示效果。尽管这种利用该分辨率模型细节创建位移贴图取代非均匀有理B样条的方法早在1996年由Krishnamurthy与Levoy在Proc. SIGGRAPH 1996的文章中已经提出，但是直到后来才开始用到更加普通的三角形网格。1998年另外两篇论文提出了从高分辨率向低分辨率多边形网格转换细节作为法线贴图的方法：Cohen et al.在SIGGRAPH 1998的论文提出了一个特殊的约束简化算法，在化简过程中跟踪如何将丢失的细节映射到简化的网格；Cignoni et al.在IEEE Visualization '98上的论文提出了分离高低精度多边形网格的简单方法，以及不受低精度模型构建方法影响的丢失细节恢复方法。后一种方法及其变体目前仍然是目前大多数可用工具使用的方法之一。

## 工作原理

<image>

计算表面光照的朗伯值（漫反射值）时，只需计算以下两个单位向量的点积：

- 从浓淡点到光源的单位向量
- 该表面的法线单位向量

得到的结果就是光线在表面上的强度。假设有一个球体，我们只能通过多边形模型近似表示其曲面形状。通过在模型上应用三通道的位图纹理，就可以对更加细致的法线向量信息进行编码。位图中的每一个通道（通常是红色、绿色、蓝色通道）都对应于一个空间尺度（X、Y、Z），这些空间尺度与物体空间法线图的固定的坐标系统相关，或者与切空间法线图场合中根据位置相对于纹理坐标平滑变化的坐标系统相关。这使得模型表面更加细致，尤其是与先进的光照技术一起使用的时候更是如此。

单位法线向量可以根据对应的 UV 纹理坐标映射到法线贴图上。因为几何体上背向观察者的向量不会被观察到，所以法线贴图上只有面向观察者的向量（如果是左手坐标系，则 z 为 0 到 -1）。映射如下：

```
  X: -1 ~ +1 :  R:   0 ~ 255
  Y: -1 ~ +1 :  G:   0 ~ 255
  Z:  0 ~ -1 :  B: 128 ~ 255
```

- 直接指向观察者的法线 (0,0,-1) 映射为 (128,128,255)。所以（法线所创造的凹凸）物体上直接面朝观察者的部分为浅蓝色，也是法线贴图上最常见的颜色。
- 指向纹理右上角的法线 (1,1, 0) 映射为 (255,255,128)。所以物体的右上角一般是浅黄色，也是颜色贴图上最亮的部分。
- 指向纹理右侧的法线 (1,0,0) 映射为 (255,128,128)。所以物体的右边一般是浅红色。
- 指向纹理顶部的法线 (0,1,0) 映射为 (128,255,128)。所以物体的顶边一般是浅绿色。
- 指向纹理左侧的法线 (-1,0,0) 映射为 (0,128,128)。所以物体的左边一般是深蓝绿色。
- 指向纹理底部的法线 (0,-1,0) 映射为 (128,0,128)。所以物体的底边一般是深杨红色。
- 指向纹理左下角的法线 (-1,-1,0) 映射为 (0,0,128)。所以物体的左下角一般是深蓝色，也是颜色贴图上最暗的部分。

<image>

## 具体实现

把高度图（Height map）转换成一张法线图（Normal Map）后，其图的RGB分别是原高度图该点的法线指向：Nx、Ny、Nz，这张图可由Direct3D的专门函数帮助计算。最后在渲染的时候直接将该高度图的每个像素与光源的向量点乘，即可得到表示每一点的明暗系数的图：根据高度图，越突出的地方，法线与光源夹角越小，该点的数值越大。接着将这张图乘到渲染线中即可，这样就使模型在背光的凹处有阴影而在面向光源处更亮的效果，这样的3D模型看起来就像真的凹凸不平一样。这些都可以直接在渲染流水线中由机器完成。

具体可以使用以下几步简单的语句来实现：

```cpp
DWORD Vector2ARGB(D3DXVECTOR3 *v,float height)
{
DWORD r=(DWORD)(127.0f*v->x+128.0f);
DWORD g=(DWORD)(127.0f*v->y+128.0f);
DWORD b=(DWORD)(127.0f*v->z+128.0f);
DWORD a=(DWORD)(255.0f*height);
return((a<<24L)+(r<<16L)+(g<<8L)+b);
}
```

```cpp
D3DXComputeNormalMap(pNormalMap,pHeightMap,NULL,0,D3D_CHANNEL_RED,1.0f);//pHeightMap为原高度图的指针，pNormalMap为一张空纹理，用于存放法线图
```

```cpp
DWORD F=Vector2ARGB(&light,0.0f);//light是单位化的光源向量
pD->SetRenderState(D3DRS_TEXTUREFACTOR,F);//pD是D3D的设备指针，这句将光源法线参数输入
pD->SetTexture(1,pTEXTURE);//设置原纹理，如上面的球，如有需要可以贴上纹理样式
pD->SetTexture(0,pNormalMap);//使用上面生成好的法线图
pD->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);//设置“来源1”为法线图
pD->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3);//将“来源1”（法线图）与“来源2”（光源法线）进行点乘
pD->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TFACTOR);//设置“来源2”为光线的光源法线参数
pD->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_TEXTURE);//这步和下面几步将图片的原纹理加上
pD->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
pD->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);
```

## 在计算机娱乐中的应用

交互式法线贴图最初只能在北卡莱罗纳大学Chapel Hill建造的并行图形机器上进行渲染，后来逐渐可以在高端的SGI工作站上用多次渲染以或者帧缓冲操作进行法线映射，以及在低端个人计算机硬件上用调色板纹理这样的技巧实现。但是，随着家用电脑以及游戏终端的处理计算能力的不断增强以及功能越来越复杂，法线贴图逐渐通过一些鲜活的计算机游戏步入大众的视野，这些游戏包括：Far Cry（Crytek）, Deus Ex: Invisible War（Eidos Interactive）, Thief: Deadly Shadows（Eidos Interactive）, The Chronicles of Riddick: Escape from Butcher Bay（Vivendi Universal）, Halo 2（Microsoft）, Doom 3（id Software）, Half-Life 2（Valve Software）, Call of Duty 2（Activision），以及Tom Clancy's Splinter Cell: Chaos Theory（Ubisoft）。虚幻引擎（Epic Games）中也大量地使用了这项技术。法线贴图在视频游戏设计中的流行源自于同时具有的出色图形质量以及与生成类似效果的其它方法相比所需的较少资源。较少的处理资源要求就可以转变成性能的提升，并且使得按照距离索引细节缩放这种降低纹理法线图细节的技术成为可能，基本上这就意味着距离越远所需的光照模拟复杂性越低，这样就可以减少处理工作，而且能够保持与近距纹理同样的细节层次。

目前，法线贴图已经在个人电脑以及游戏终端上得到成功而广泛的应用。最初，微软的Xbox是唯一完全支持这种效果的家庭游戏终端，其它一些终端由于硬件上不能直接支持这种功能所以只能通过软件实现。如Xbox360这样的下一代终端的开发人员严重依赖于法线贴图的技术，并且已经开始着手实现视差映射技术。

## 延伸阅读

- （PDF） Realistic, Hardware-accelerated Shading and Lighting, Heidrich and Seidel, SIGGRAPH 1999

## 外部链接

- Blender法线贴图
- GIMP法线图插件
- Photoshop法线图插件
- 艺术家使用法线贴图指南 , Ben Cloward
- Free xNormal法线贴图工具 , Santiago Orgaz
- Maya法线贴图插件, Olivier Renouard
- 调色板纹理法线贴图使用旧的OpenGL扩展
- 没有硬件辅助的法线贴图 , Lux aeterna luceat eis Amiga demo from Ephidrena
- ZMapper, Pixologic
- 法线贴图照片通过数字照片分层手工创建法线图