一、简介

  法线贴图是凸凹贴图(Bump mapping)的一种常见应用,简单说就是在不增加模型多边形数量的前提下,通过渲染暗部和亮部的不同颜色深度,来为原来的贴图和模型增加视觉细节和真实效果

简单原理是在普通的贴图的基础上,再另外提供一张对应原来贴图的,可以表示渲染浓淡的贴图。通过将这张附加的表示表面凸凹的贴图的因素于实际的原贴图进行运算后,可以得到新的细节更加丰富富有立体感的渲染效果

大多数法线图都是一张以蓝紫色为主的图。这张法线图其实是一张RGB贴图,其中红、绿、蓝三个通道分别表示由高度图转换而来的该点的法线指向:Nx、Ny、Nz,其中绝大部分点的法线都指向z方向,因此图更偏向于蓝色

 o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump);

 UnpackNormal定义在UnityCG.cginc文件中

UnpackNormal接受一个fixed4的输入,并将其转换为所对应的法线值(fixed3)。在解包得到这个值之后,将其赋给输出的Normal,就可以参与到光线运算中完成接下来的渲染工作了

 二、准备工作

     创建一个新的材质和着色器,在场景视图中将它们设置到一个新物体上。这样,我们就拥有了一个干净的工作空间,然后就可以在场景中看看我们的法线贴图技术。


  一张纹理图


unity blendshapes法线导致描边异常 unity 法线贴图_贴图


三、如何操作


1.在Properties块中添加一个颜色和纹理属性


Properties 
{
	_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
	_NormalTex ("Normal Map", 2D) = "bump" {}
		
}


2.在CGPROGRAM 描述语句下的subshader内声明相应的属性,以遍我们在CG程序片段中能够访问上述的两个属性



CGPROGRAM
 #pragma surface surf Lambert
 sampler2D _NormalTex;
 float4 _MainTint;


3.然后,我们需要确定我们已经使用合适的变量对Input结构体进行了更新,这样我们就可以将模型的UV应用到法线贴图上了

struct Input 
 {
      float2 uv_NormalTex;
 };


4.最后,我们使用Unity内置的UnpackNormal()函数从法线贴图当中提取法线信息。然后你只需将这些新的法线信息应用到Surface Shader的输出即可:

float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
o.Normal = normalMap.rgb;



下面展示的是应用法线贴图渲染之后的结果



 四、实现原理

  如果你查看Unity自带的UnityCG.cginc文件,你就会找到UnpackNormal()函数的定义。当你在着色器内声明该函数时,Unity将对你提供的法线贴图进行处理,并将运算后的正确数据直接返回给你,这样你就可以逐像素将法线信息应用到光照函数内了。这对我们来说节省了不少时间!UnpackNormal()函数对法线贴图进行处理后,你会将处理后的值返回到SurfaceOutput结构体内,这样它就可以在光照函数中进行使用了。这一步工作由代码o.Normal = normalMap.rgb;来完成


 五、更多内容


 你也可以在你的法线贴图着色器中添加一些控件,以便让用户可以自行调整法线贴图的强度。我们可以很容易的通过修改法线贴图变量的x和y坐标来完成,然后将修改后的值返回到计算中。

Properties块中添加一个属性,将其命名_NormalMapIntensity,如下代码所示:


Properties 
{
		
	_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
	_NormalTex ("Normal Map", 2D) = "bump" {}
	_NormalIntensity ("Normal Map Intensity", Range(0,2)) = 1
}



2.确保SubShader函数中也声明了这个属性

sampler2D _NormalTex;
float4 _MainTint;
float _NormalIntensity;





3.将经过解压后的法线贴图变量值x和y坐标均乘上_NormalMapIntensity,将计算后的值作为法线贴图的变量值。

float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
normalMap = float3(normalMap.x * _NormalIntensity, normalMap.y * _NormalIntensity, normalMap.z);





本章主要参考《Unity Shaders and Effects Cookbook》一书,感谢原书作者提供的学习资料