在Unity Shader中用法线贴图和高度图来实现凹凸映射
1、凹凸映射概念
凹凸映射,在不改变顶点位置的前提下,修改模型表面的法线方向,为模型提供更多的细节。
2、凹凸映射的 2 种方法
使用一张高度纹理
来模拟表面位移(Displacement),然后得到一个修改后的法线值。此方法也叫做高度映射(Height Mapping)
。
颜色越浅,越向外凸;颜色越深,越向内凹。能明确表面的凹凸信息
,缺点是计算复杂。
使用一张法线纹理
来直接存储表面法线。此方法也叫做法线映射(Normal Mapping)
。用于存储表面的法线向量
,法线向量的取值范围为 [ -1 , 1] 。但是像素分量的取值范围是 [ 0 , 1],因此需要进行以下两个映射:
-
贴图属性
-
UnpackNormal
是unity内置的函数(在后面⑥定义片元着色器中会出现)。当我们把贴图纹理设置为Normal map类型时,该函数可以得到正确的法线方向。
不仅如此,Unity还可以根据不同平台来调整Normal的细节,使用UnpackNormal函数针对不同压缩格式对法线纹理进行正确采样。(目前存在DXT1、DXT5、DXT5nm的格式,DXT3以及被弃用——Tech-Artists)
可以在UnityCG.cginc之中找到UnpackNormal的具体定义:
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
return packednormal.xyz * 2 - 1;
#else
return UnpackNormalmapRGorAG(packednormal);
#endif
}
代码中可以看到我们可爱的Unity并不能识别除了DXT5nm以外的贴图格式,因此翻阅官方文档然后直接Ctrl-F搜索DXT得到如下结果:
说明在unity之中可以有2个选择,包括XYZ和DXT5nm,且更改法线编码时,最好直接在Unity的项目设置中,而不是在CG Shader代码中,否则解码成本会增加。
- 根据下图可以看到,如果我们把类型设置为Normal map,将会出现
Create from Grayscale
的选项。这个选项的主要作用是可以将我们的先前讲到的 高度图 转换为 法线贴图,方便我们进行法线处理。
不仅如此,Unity给予了我们两种滤波器:
①Sharp——使用Sobel滤波
来生成法线
②Smooth
3、法线纹理的坐标空间
对于先前的纹理映射和纹理贴图之中,我们可以直到,要在CG语言中实现相应效果,不可或缺的是纹理的坐标空间(是ObjectSpace还是WorldSpace?)。而对于法线纹理的坐标空间来说,有个直接点的想法是——将修改后的模型空间中的表面法线存储在一张纹理中。(这样一来就可以结合前面“纹理贴图”一样实现凹凸效果。代码部分会加以证明。)这种纹理被称为——模型空间的法线纹理(Object-Space Normal Map)。
然而,实际上我们真正使用的是——模型顶点的切线空间(Tangent Space)。具体解释如下:
对于模型的每一个顶点,都有属于自己的切线空间,这个切线空间的原点就是顶点本身。
Z轴
是顶点的法线
方向 n
X轴
是顶点的切线
方向 t
Y轴
是由顶点的切线和法线叉积
而得
代码开始
在切线空间下计算
① 在Properties属性下添加Bump法线纹理的属性,以及用于控制凹凸程度的属性
Properties{
_Color("Base Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white" {
}
_BumpMap("Normal Map", 2D) = "bump"{
}
_BumpScale ("Bump Scale",Float) = 1.0
_Specular("Specular",Color) = (1,1,1,