US3S2L10——渐变纹理综合实现

渐变纹理综合实现

接下来将渐变纹理的映射实现融合到之前实现的法线纹理 Shader 内,
这里复用之前实现的 切线空间下的法线纹理 Shader

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Shader "TeachShader/Lesson51"
{
Properties
{
_MainColor("MainColor", Color) = (1, 1, 1, 1)
_MainTex("MainTex", 2D) = ""{}
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
_SpecularNum("SpecularNum", Range(0, 20)) = 18
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "Lighting.cginc"

struct v2f
{
float4 pos: SV_POSITION;
//float2 uvTex: TEXCOORD0;
//float2 uvBump: TEXCOORD1; //可以使用两个float2来分别存储主要纹理的uv和法线纹理的uv
float4 uv: TEXCOORD0; //可以使用一个float4来同时存储主要纹理的uv(xy存储)和法线纹理的uv(zw存储)
float3 lightDir: TEXCOORD1; //相对于切线空间下的光的方向
float3 viewDir: TEXCOORD2; //相对于切线空间下的视角方向
};

float4 _MainColor; //漫反射颜色
sampler2D _MainTex; //颜色纹理
float4 _MainTex_ST; //颜色纹理的缩放和平移
sampler2D _BumpMap; //法线纹理
float4 _BumpMap_ST; //法线纹理的缩放和平移
float _BumpScale; //凹凸程度
float4 _SpecularColor; //高光颜色
fixed _SpecularNum; //光泽度

v2f vert (appdata_full v)
{
v2f data;

data.pos = UnityObjectToClipPos(v.vertex); //计算裁剪空间下顶点坐标
// 分别计算主纹理和法线纹理的缩放平移
data.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
data.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

// 计算副切线
float3 binormal = cross(normalize(v.tangent), normalize(v.normal)) * v.tangent.w;
// 得到模型空间到切线空间的转换矩阵
float3x3 rotation = float3x3(
v.tangent.xyz, //切线
binormal, //副切线
v.normal //法线
);

data.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); // 切线空间下的光的方向
data.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)); // 切线空间下的视角方向

return data;
}

fixed4 frag (v2f i) : SV_Target
{
float4 packedNormal = tex2D(_BumpMap, i.uv.zw); // 获取法线纹理的颜色数据
float3 tangentNormal = UnpackNormal(packedNormal); // 将颜色数据逆运算并解压缩,得到切线空间下法线数据
// 将法线数据的xy乘以凹凸系数,根据xy修正z,避免凹凸系数影响光照亮度
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

// 计算带纹理颜色的BlinnPhong光照计算,这里使用已经计算好的切线数据
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _MainColor.rgb; // 反射率

// 漫反射光照计算:这里需要使用已经计算完毕的切线数据和光照方向,注意!这里的 tangentNormal 无需归一化
fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(tangentNormal, normalize(i.lightDir)));
// 高光反射光照计算:这里需要使用已经计算完毕的切线数据和光照方向
float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir)); // 半角向量
fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum);
// 最终颜色计算
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

return fixed4(color.rgb, 1);
}
ENDCG
}
}
}

然后,修改上述代码的实现,将渐变纹理贴图计算融合到其中

关键点:将原本的兰伯特光照模型修改为渐变纹理的计算方式(半兰伯特光照模型映射渐变纹理颜色)

因此,属性需要添加渐变纹理 _RampTex​:

1
2
3
4
5
6
7
8
9
10
Properties
{
_MainColor("MainColor", Color) = (1, 1, 1, 1)
_MainTex("MainTex", 2D) = ""{}
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_RampTex("_RampTex", 2D) = ""{}
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
_SpecularNum("SpecularNum", Range(0, 20)) = 18
}

同时,片元着色器的漫反射计算部分也要修改为:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct v2f
{
float4 pos: SV_POSITION;
float4 uv: TEXCOORD0; //可以使用一个float4来同时存储主要纹理的uv(xy存储)和法线纹理的uv(zw存储)
float3 lightDir: TEXCOORD1; //相对于切线空间下的光的方向
float3 viewDir: TEXCOORD2; //相对于切线空间下的视角方向
};

float4 _MainColor; //漫反射颜色
sampler2D _MainTex; //颜色纹理
float4 _MainTex_ST; //颜色纹理的缩放和平移
sampler2D _BumpMap; //法线纹理
float4 _BumpMap_ST; //法线纹理的缩放和平移
sampler2D _RampTex; //渐变纹理
float4 _RampTex_ST; //渐变纹理的缩放和平移(很少使用)
float _BumpScale; //凹凸程度
float4 _SpecularColor; //高光颜色
fixed _SpecularNum; //光泽度

fixed4 frag (v2f i) : SV_Target
{
float4 packedNormal = tex2D(_BumpMap, i.uv.zw); // 获取法线纹理的颜色数据
float3 tangentNormal = UnpackNormal(packedNormal); // 将颜色数据逆运算并解压缩,得到切线空间下法线数据
// 将法线数据的xy乘以凹凸系数,根据xy修正z,避免凹凸系数影响光照亮度
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

// 计算带纹理颜色的BlinnPhong光照计算,这里使用已经计算好的切线数据
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _MainColor.rgb; // 反射率

// 漫反射光照计算:使用已经计算完毕的切线数据和光照方向,结合渐变纹理,先计算渐变纹理的uv坐标,再计算漫反射颜色
fixed halfLambertNum = dot(tangentNormal, normalize(i.lightDir)) * 0.5 + 0.5;
fixed3 diffuseColor = _LightColor0.rgb * albedo.rgb * tex2D(_RampTex, fixed2(halfLambertNum, halfLambertNum)).rgb;
// 高光反射光照计算:这里需要使用已经计算完毕的切线数据和光照方向
float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir)); // 半角向量
fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum);
// 最终颜色计算
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + diffuseColor + specularColor;

return fixed4(color.rgb, 1);
}

完整 Shader 代码如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
Shader "TeachShader/Lesson54"
{
Properties
{
_MainColor("MainColor", Color) = (1, 1, 1, 1)
_MainTex("MainTex", 2D) = ""{}
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_RampTex("_RampTex", 2D) = ""{}
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
_SpecularNum("SpecularNum", Range(0, 20)) = 18
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "Lighting.cginc"

struct v2f
{
float4 pos: SV_POSITION;
float4 uv: TEXCOORD0; //可以使用一个float4来同时存储主要纹理的uv(xy存储)和法线纹理的uv(zw存储)
float3 lightDir: TEXCOORD1; //相对于切线空间下的光的方向
float3 viewDir: TEXCOORD2; //相对于切线空间下的视角方向
};

float4 _MainColor; //漫反射颜色
sampler2D _MainTex; //颜色纹理
float4 _MainTex_ST; //颜色纹理的缩放和平移
sampler2D _BumpMap; //法线纹理
float4 _BumpMap_ST; //法线纹理的缩放和平移
sampler2D _RampTex; //渐变纹理
float4 _RampTex_ST; //渐变纹理的缩放和平移(很少使用)
float _BumpScale; //凹凸程度
float4 _SpecularColor; //高光颜色
fixed _SpecularNum; //光泽度

v2f vert (appdata_full v)
{
v2f data;

data.pos = UnityObjectToClipPos(v.vertex); //计算裁剪空间下顶点坐标
// 分别计算主纹理和法线纹理的缩放平移
data.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
data.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

// 计算副切线
float3 binormal = cross(normalize(v.tangent), normalize(v.normal)) * v.tangent.w;
// 得到模型空间到切线空间的转换矩阵
float3x3 rotation = float3x3(
v.tangent.xyz, //切线
binormal, //副切线
v.normal //法线
);

data.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); // 切线空间下的光的方向
data.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)); // 切线空间下的视角方向

return data;
}

fixed4 frag (v2f i) : SV_Target
{
float4 packedNormal = tex2D(_BumpMap, i.uv.zw); // 获取法线纹理的颜色数据
float3 tangentNormal = UnpackNormal(packedNormal); // 将颜色数据逆运算并解压缩,得到切线空间下法线数据
// 将法线数据的xy乘以凹凸系数,根据xy修正z,避免凹凸系数影响光照亮度
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

// 计算带纹理颜色的BlinnPhong光照计算,这里使用已经计算好的切线数据
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _MainColor.rgb; // 反射率

// 漫反射光照计算:使用已经计算完毕的切线数据和光照方向,结合渐变纹理,先计算渐变纹理的uv坐标,再计算漫反射颜色
fixed halfLambertNum = dot(tangentNormal, normalize(i.lightDir)) * 0.5 + 0.5;
fixed3 diffuseColor = _LightColor0.rgb * albedo.rgb * tex2D(_RampTex, fixed2(halfLambertNum, halfLambertNum)).rgb;
// 高光反射光照计算:这里需要使用已经计算完毕的切线数据和光照方向
float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir)); // 半角向量
fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, halfA)), _SpecularNum);
// 最终颜色计算
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + diffuseColor + specularColor;

return fixed4(color.rgb, 1);
}
ENDCG
}
}
}

显示效果(左侧直接使用BlinnPhong光照模型,右侧在BlinnPhong光照模型的基础上半兰伯特光照计算漫反射并使用渐变贴图):

image

可见,左侧的明暗变化更平滑,较为拟真,而右侧更明暗界线分明一些,更加卡通化