unity-shader-tutorial-texture

buildin效果图

urp效果图

概念

法线贴图

模型空间下的法线纹理,法线是在模型空间下的坐标系为参考系,存储在法线纹理贴图里面的。非常直观,但是也存在一些缺点,以下是优缺点列表:

  • 实现简单
  • 纹理坐标的缝合处和尖锐的边角处突变缝隙比较少,更加平滑
  • 只能对于指定的模型进行贴图,没法很好的重用,因为法线信息是以绝对的方式被记录的,而且模型还不能随意变形。

切线空间下的法线纹理以下是优缺点列表:

  • 自由度高
  • 可以进行uv动画
  • 可以重用
  • 可以被压缩
  • 纹理坐标的缝合处和尖锐的边角处突变缝隙比较多,不够平滑,但是这个误差一般都在人眼可接受的范围内

Texture.shader

Texture.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
Shader "HHF/Tutorial/Texture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
[KeywordEnum(Repeat, Clamp)]_WrapMode("WrapMode",int) = 0
[IntRange]_Mipmap("Mipmap",Range(0, 12)) = 0

[Toggle]_NormalTexEnable("NormalTexEnabled", int) = 0
[Normal]_NormalTex("NormalTex", 2D) = "bump"{}

[Toggle]_CubeTexEnable("CubeTexEnabled",int) = 0
_CubeTex("CubeTex", Cube) = "white"{}

[Toggle]_ReflectionProbeEnable("ReflectionProbeEnabled",int) = 0
}

SubShader
{
Tags { "RenderPipeline"="UniversalPipeline" }

Pass
{
Tags {"LightMode" = "UniversalForward"}

HLSLPROGRAM
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _WRAPMODE_REPEAT _WRAPMODE_CLAMP
#pragma shader_feature _ _NORMALTEXENABLE_ON
#pragma shader_feature _ _CUBETEXENABLE_ON
#pragma shader_feature _ _REFLECTIONPROBEENABLE_ON

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
float3 positionWS:TEXCOORD1;
half3 normalWS:TEXCOORD2;

// 三个float3向量组合成我们的切线转置矩阵
float3 tSpace0:TEXCOORD4;
float3 tSpace1:TEXCOORD5;
float3 tSpace2:TEXCOORD6;
};

CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half _Mipmap;
CBUFFER_END

TEXTURE2D (_MainTex);SAMPLER(sampler_MainTex);
TEXTURE2D (_NormalTex);SAMPLER(sampler_NormalTex);
TEXTURECUBE (_CubeTex);SAMPLER(sampler_CubeTex);

v2f vert (appdata v)
{
v2f o;
o.positionCS = TransformObjectToHClip(v.vertex.xyz);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.positionWS = TransformObjectToWorld(v.vertex.xyz);// mul(unity_ObjectToWorld, v.vertex);
o.normalWS = TransformObjectToWorldNormal(v.normal);

// 切线转置矩阵的计算
// 切线从本地空间转换到世界空间
half3 worldTangent = TransformObjectToWorldDir(v.tangent.xyz);
// 由v.tangent.w决定我们副切线的方向
half sign = v.tangent.w * GetOddNegativeScale();
// 由叉积计算出副切线
half3 worldBinormal = cross(o.normalWS, worldTangent) * sign;
o.tSpace0 = float3(worldTangent.x,worldBinormal.x,o.normalWS.x);
o.tSpace1 = float3(worldTangent.y,worldBinormal.y,o.normalWS.y);
o.tSpace2 = float3(worldTangent.z,worldBinormal.z,o.normalWS.z);
return o;
}

half4 frag (v2f i) : SV_Target
{
// WrapMode
#if _WRAPMODE_REPEAT
i.uv = frac(i.uv);
#elif _WRAPMODE_CLAMP
// 方法一
// i.uv = clamp(i.uv,0,1);
// 方法二
i.uv = saturate (i.uv);
#endif

// 多级渐远Mipmap
half4 c = SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, i.uv, _Mipmap);

half3 worldNormal = i.normalWS;

// 法线纹理
#if _NORMALTEXENABLE_ON
Light mainLight = GetMainLight();
half3 normalTex = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex, sampler_NormalTex, i.uv), 1);
half3 N1 = normalize(normalTex);
half3 L = mainLight.direction;
// 通过矩阵相乘(利用点积来进行计算)得出世界空间下的法线(法线纹理)
worldNormal = half3(dot(i.tSpace0, normalTex), dot(i.tSpace1,normalTex), dot(i.tSpace2,normalTex));
half3 diffuse = mainLight.color * max(0, dot(N1, L));
c.rgb += diffuse;
#endif

// V,N,R
// half3 V = normalize(UnityWorldSpaceViewDir(i.positionWS));
half3 V = normalize(GetWorldSpaceViewDir(i.positionWS));
half3 N = normalize(worldNormal);
half3 R = reflect(-V, N);

#if _CUBETEXENABLE_ON
// CubeMap
half4 cubemap = SAMPLE_TEXTURECUBE(_CubeTex, sampler_CubeTex, R);
c += cubemap;
#endif // _CUBEMAPENABLE_ON

#if _REFLECTIONPROBEENABLE_ON
// 反射探针中当前激活的CubeMap存储在unity_SpecCube0当中,需要使用 UNITY_SAMPLE_TEXCUBE 或 SAMPLE_TEXTURECUBE_LOD 然后需要对其进行解码
half4 cubemapProbe = SAMPLE_TEXTURECUBE (unity_SpecCube0, samplerunity_SpecCube0, R);
half3 probeColor = DecodeHDREnvironment (cubemapProbe, unity_SpecCube0_HDR);
c.rgb += probeColor;
#endif

return c;
}

ENDHLSL
}
}

SubShader
{
Tags { "RenderType"="Opaque" }

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _WRAPMODE_REPEAT _WRAPMODE_CLAMP
#pragma shader_feature _ _NORMALTEXENABLE_ON
#pragma shader_feature _ _CUBETEXENABLE_ON
#pragma shader_feature _ _REFLECTIONPROBEENABLE_ON

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

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
float3 positionWS:TEXCOORD1;
half3 normalWS:TEXCOORD2;

// 三个float3向量组合成我们的切线转置矩阵
float3 tSpace0:TEXCOORD4;
float3 tSpace1:TEXCOORD5;
float3 tSpace2:TEXCOORD6;
};

sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NormalTex;
half _Mipmap;
samplerCUBE _CubeTex;

v2f vert (appdata v)
{
v2f o;
o.positionCS = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.positionWS = mul(unity_ObjectToWorld,v.vertex);
o.normalWS = UnityObjectToWorldNormal(v.normal);

// 切线转置矩阵的计算
// 切线从本地空间转换到世界空间
half3 worldTangent = UnityObjectToWorldDir(v.tangent);
// 由v.tangent.w决定我们副切线的方向
half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
// 由叉积计算出副切线
half3 worldBinormal = cross(o.normalWS, worldTangent) * tangentSign;
o.tSpace0 = float3(worldTangent.x,worldBinormal.x,o.normalWS.x);
o.tSpace1 = float3(worldTangent.y,worldBinormal.y,o.normalWS.y);
o.tSpace2 = float3(worldTangent.z,worldBinormal.z,o.normalWS.z);
return o;
}

half4 frag (v2f i) : SV_Target
{
// WrapMode
#if _WRAPMODE_REPEAT
i.uv = frac(i.uv);
#elif _WRAPMODE_CLAMP
// 方法一
// i.uv = clamp(i.uv,0,1);
// 方法二
i.uv = saturate (i.uv);
#endif

// 多级渐远Mipmap
// tex2Dlod的第二个参数是4维向量,最后一个分量w表示的是采样级别
float4 uvMipmap = float4(i.uv, 0, _Mipmap);
half4 c = tex2Dlod(_MainTex, uvMipmap);

half3 worldNormal = i.normalWS;

// 法线纹理
#if _NORMALTEXENABLE_ON
half3 normalTex = UnpackNormal(tex2D(_NormalTex, i.uv));
half3 N1 = normalize(normalTex);
half3 L = _WorldSpaceLightPos0.xyz;
// 通过矩阵相乘(利用点积来进行计算)得出世界空间下的法线(法线纹理)
half3 diffuse = _LightColor0.rgb * max(0, dot(N1, L));
worldNormal = half3(dot(i.tSpace0, normalTex), dot(i.tSpace1,normalTex), dot(i.tSpace2,normalTex));
c.rgb += diffuse;
#endif

// V,N,R
half3 V = normalize(UnityWorldSpaceViewDir(i.positionWS));
half3 N = normalize(worldNormal);
half3 R = reflect(-V, N);

#if _CUBETEXENABLE_ON
// CubeMap
half4 cubemap = texCUBE(_CubeTex, R);
c += cubemap;
#endif // _CUBEMAPENABLE_ON

#if _REFLECTIONPROBEENABLE_ON
// 反射探针中当前激活的CubeMap存储在unity_SpecCube0当中,必须要用UNITY_SAMPLE_TEXCUBE进行采样,然后需要对其进行解码
half4 cubemapProbe = UNITY_SAMPLE_TEXCUBE (unity_SpecCube0, R);
half3 probeColor = DecodeHDR (cubemapProbe, unity_SpecCube0_HDR);
c.rgb += probeColor;
#endif

return c;
}
ENDCG
}
}
}