Lv.2 Unity主线:添加法线
法线贴图
通常模型都会有法线贴图,里面是关于模型法线的信息,对法线的处理需要在计算光照之前 
添加法线贴图
properties
一个是采样器,一个是法线强度的控制
1
2
3
4
5
_NormalMap("NormalMap", 2D) = "bump"{}
_NormalMapScale("NormalMapScale", Range(0,5)) = 1
----------
sampler2D _NormalMap;
half _NormalMapScale;
appdata
appdata需要读取模型原始法线和切线
1
2
3
4
5
6
7
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL; //法线
float4 tangent : TANGENT; //切线
float2 uv : TEXCOORD0;
};
v2f
v2f则需要接收处理的模型法线和切线以及副切线
1
2
3
4
5
6
7
8
9
10
struct v2f
{
float4 pos : SV_POSITION;
float4 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1; //法线
float3 tangentWS : TEXCOORD2; //切线
float3 bitangentWS : TEXCOORD3; //副切线
float2 texcoord : TEXCOORD4;
UNITY_SHADOW_COORDS(5)
};
vert 顶点着色器
处理数据参考
1
2
3
4
5
6
7
8
9
10
11
12
13
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.positionWS = mul(unity_ObjectToWorld, v.vertex);
o.normalWS = UnityObjectToWorldNormal(v.normal);
o.tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
//* v.tangent.w处理平台差异
o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w;
o.texcoord = TRANSFORM_TEX(v.uv,_MainTex);
TRANSFER_SHADOW(o);
return o;
}
frag片元着色器
法线贴图混合
对于法线贴图的混合,需要先构建TBN矩阵,其实就是把模型顶点法线,切线,副切线写成矩阵形式,注意需要归一化
1
float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS);
采样法线贴图需要额外的UnpackNormal,对于强度控制只需要控制xy分量,也就是切线和副切线,因为切线和副切线决定面的倾斜程度,不对法线操作是因为在归一化后会间接影响,如果同时都乘上这个因子,那么归一化相当于和原法线强度不变
1
2
float3 normalMapData = UnpackNormal(tex2D(_NormalMap, i.texcoord)).xyz;
normalMapData.xy *= _NormalMapScale;
最后就是混合阶段,直接使用矩阵乘法就行
1
2
float3 pixelNormal = normalize(mul(normalMapData, TBN));
float noL = max(0, dot(pixelNormal, lightWS));
当然不构建矩阵乘法其实还有一种写法,实际是一样的
1
2
3
float3 pixelNormal = normalize( normalMapData.x * tangentWS
+ normalMapData.y * bitangentWS
+ normalMapData.z * normalWS );
完整代码参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
float4 frag(v2f i) : SV_Target
{
//vectors
float3 positionWS = i.positionWS;
float3 normalWS = normalize(i.normalWS);
float3 tangentWS = normalize(i.tangentWS);
float3 bitangentWS = normalize(i.bitangentWS);
float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS);
half3 lightWS = normalize(_WorldSpaceLightPos0.xyz);
float3 viewDir = normalize(_WorldSpaceCameraPos - positionWS);
//tex
float3 lightColor = _LightColor0.xyz;
float4 baseCol = tex2D(_MainTex, i.texcoord);
float3 normalMapData = UnpackNormal(tex2D(_NormalMap, i.texcoord)).xyz;
normalMapData.xy *= _NormalMapScale;
//halfLambet
float3 pixelNormal = normalize(mul(normalMapData, TBN));
float noL = max(0, dot(pixelNormal, lightWS));
float halfLambet = noL*0.5 + 0.5;
//Shadow
float shadow = SHADOW_ATTENUATION(i);
//output
float3 finalColor = baseCol *halfLambet * lightColor *shadow;
return float4(finalColor, 1);
}
Enjoy Reading This Article?
Here are some more articles you might like to read next: