法线贴图在不提升三角面数的情况下提高模型细节画面精度很有用,三角面数的提升会造成性能下降
法线贴图是使用颜色贴图来表示材质表面的法线的方式,法线贴图分模型空间下的法线贴图和切线空间下的法线贴图,整体偏向于蓝色的贴图是切线空间下的法线贴图,取得这类贴图的法线值之后要计算需要进行模型空间向切线空间的转换或者切线空间向模型空间的转换。
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader"TsinNing/Normal"{
Properties{
_texPlusC("TexPlusC", COLOR) = (1,1,1,1)
//指定纹理贴图
_MainTex("Main Tex", 2D) = "white"{}
//法线贴图和纹理贴图有一定的区别
//法线贴图的bump表示如果没有赋值,顶点使用的是顶点数据里面自带的法线数据
_NormalMap("Normal Map", 2D) = "bump"{}
//凹凸参数
_BumpScale("Bump Scale", Range(0, 5)) = 1
}
SubShader{
Pass{
//需要定义正确的光照模式,才能得到正确的Unity内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//相当于c#里面的using 包含unity关于光照的文件
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _texPlusC;
sampler2D _MainTex;
sampler2D _NormalMap;
float4 _MainTex_ST;
float4 _NormalMap_ST;
float _BumpScale;
struct a2v {
float4 vertex:POSITION;
//在结构体a2v传入顶点函数的时候
//texcoord变量代表的是这个顶点对应贴图的uv坐标位置
//这个位置可以用于法线贴图也可以用于纹理贴图
//texcoord只能从顶点函数里面传入,传给片元函数需要顶点函数作为媒介
//texcoord在片源函数里面用于后面根据位置和贴图取得这个顶点的颜色
float4 texcoord:TEXCOORD0;
//这里定义传入法线和切线 用于后面使用宏得到模型空间转切线空间的矩阵
//宏里面使用到的法线变量名就是normal,所以normal名字不能更改,切线同理
float3 normal:NORMAL;
float4 tangent:TANGENT;//一个法线下的切线有两个方向,使用float4存储是为了让第四个数表示方向
};
struct v2f {
float4 position:SV_POSITION;
float4 uv : TEXCOORD0;
float3 lDir : COLOR0;
};
//因为使用到的宏里面对于顶点函数传进来的结构体的名字就叫做v,如果改成其他的
//可能会造成宏使用报错或返回不正确的结果
v2f vert(a2v v)
{
v2f f;
//将顶点位置从模型空间转换到剪裁空间
f.position = UnityObjectToClipPos(v.vertex);
//xy用于存储每个顶点自身的uv坐标被纹理贴图变量改变的位移和缩放后的结果
f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
//xy用于存储每个顶点自身的uv坐标与被法线贴图变量改变的位移和缩放后的结果
f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;
//unity内置宏 调用后得到一个名字叫做rotation的 正乘能够将一个方向向量从模型空间转换到剪裁空间的三乘三矩阵
TANGENT_SPACE_ROTATION;
float3 lDir = ObjSpaceLightDir(v.vertex);
f.lDir = normalize(mul(lDir, rotation));
return f;
}
fixed4 frag(v2f f) : SV_Target{
fixed3 texC = tex2D(_MainTex, f.uv.xy) * _texPlusC.rgb;
half4 norC = tex2D(_NormalMap, f.uv.zw);
//使用unity内置函数将颜色转换成切线空间下的法线
fixed3 tNor = normalize(UnpackNormal(norC));
//我们可以想象在顶点上面有个坐标系,其中z轴是刚好经过顶点垂直于经过顶点的切面的的轴
//x轴可以想象为经过顶点的切面上的一个方向,y轴是经过顶点的切面上一条与x轴垂直的轴
//所以能明白在模型本身的信息里面 这个顶点的法线的方向是(0,0,z),即完全垂直于切面
//在法线贴图的概念里法线贴图表达的法线向量的z其实和模型本身的是一致的
//法线贴图的作用在于改变了法线向量的x和y,使得法线不再是简单垂直与顶点所在的切面
//其实严格意义上说不是法线了,可以想象在z轴不变的情况下,x和y越大,向量就越倾斜
//法线贴图所需要表达的效果就越明显了 当是0的时候 就相当于没有赋值法线贴图了
tNor.xy = tNor.xy * _BumpScale;
//环境光*贴图颜色是为了能够减少环境光的影响程度
//因为贴图较暗的地方乘以环境光得到的结果还是比较暗
//较亮的地方乘以环境光得到的结果是比较亮的
fixed3 amb = UNITY_LIGHTMODEL_AMBIENT.rgb * texC;
//要加上环境光amb 不然逆直射光的区域会显得比较黑
fixed3 c = _LightColor0.rgb * texC * max(dot(tNor, f.lDir), 0) + amb;
return fixed4(c,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
效果图如下,自写的法线shader和标准的shader还是有点区别的