第4章 URP着色器基础

第4章 URP着色器基础

理论讲解

URP Shader结构与Built-in区别

Unity的Universal Render Pipeline (URP)着色器与传统的Built-in Render Pipeline着色器在结构和功能上存在显著差异。URP着色器是为Scriptable Render Pipeline (SRP)框架专门设计的,具有更好的性能和更灵活的扩展性。

Built-in Render Pipeline着色器特点:

  1. 固定渲染流程:渲染管线是硬编码在Unity引擎中的,开发者无法修改渲染流程
  2. 复杂的内置函数:使用大量的内置函数如Lighting.cginc中的函数
  3. 不支持SRP Batcher:无法利用URP的批处理优化
  4. 渲染状态管理复杂:渲染状态由引擎自动管理,难以精确控制
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
// Built-in Render Pipeline着色器示例
Shader "Custom/BuiltInExample"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0

sampler2D _MainTex;
fixed4 _Color;
half _Glossiness;
half _Metallic;

struct Input
{
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
}

URP Render Pipeline着色器特点:

  1. 可编程渲染管线:渲染流程可以通过C#脚本完全自定义
  2. 模块化设计:使用HLSL工具库,代码更清晰
  3. 支持SRP Batcher:能够利用批处理优化提升性能
  4. 渲染状态精确控制:可以精确控制渲染状态和流程
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
// URP Render Pipeline着色器示例
Shader "URP/Custom/URPExample"
{
Properties
{
_BaseMap("Texture", 2D) = "white" {}
_BaseColor("Color", Color) = (1, 1, 1, 1)
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
_Metallic("Metallic", Range(0.0, 1.0)) = 0.0
[Toggle(_SPECULAR_SETUP)] _SpecularSetup("Specular Setup", Float) = 0
}

SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
"UniversalMaterialType" = "Lit"
"IgnoreProjector" = "True"
}

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

Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5

#pragma vertex LitPassVertex
#pragma fragment LitPassFragment

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE

#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _ DYNAMICLIGHTMAP_ON

#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION

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

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
half _Smoothness;
half _Metallic;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
float2 lightmapUV : TEXCOORD1;
float4 shadowCoord : TEXCOORD2;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float4 tangentWS : TEXCOORD2;
float2 uv : TEXCOORD3;
float3 viewDirWS : TEXCOORD4;
float4 shadowCoord : TEXCOORD5;
float4 fogFactorAndVertexLight : TEXCOORD6;
};

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

Varyings LitPassVertex(Attributes input)
{
Varyings output = (Varyings)0;

VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);

output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.tangentWS = float4(normalInput.tangentWS, normalInput.sign);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.viewDirWS = viewDirWS;
output.shadowCoord = TransformWorldToShadowCoord(output.positionWS);

output.fogFactorAndVertexLight = half4(ComputeFogFactor(output.positionCS.z), 0, 0, 0);

return output;
}

half4 LitPassFragment(Varyings input) : SV_Target
{
SurfaceData surfaceData;
InitializeStandardLitSurfaceData(input.uv, surfaceData);

surfaceData.albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).rgb * _BaseColor.rgb;
surfaceData.alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a;
surfaceData.metallic = _Metallic;
surfaceData.smoothness = _Smoothness;
surfaceData.normalTS = float3(0.0h, 0.0h, 1.0h);
surfaceData.occlusion = 1.0h;
surfaceData.emission = half3(0.0, 0.0, 0.0);

InputData inputData;
InitializeInputData(input, surfaceData.normalTS, inputData);

half4 color = UniversalFragmentPBR(
inputData,
surfaceData.albedo,
surfaceData.metallic,
surfaceData.specular,
surfaceData.smoothness,
surfaceData.normalTS,
surfaceData.emission,
surfaceData.alpha
);

color.rgb = MixFog(color.rgb, inputData.fogCoord);
return color;
}
ENDHLSL
}
}

Fallback "Universal Render Pipeline/Lit"
}

Lit、SimpleLit、Unlit Shader详解

URP提供了三种主要的着色器类型,每种都有其特定的用途和性能特征:

1. Lit Shader

Lit Shader是URP中最完整的着色器,支持完整的PBR(Physically Based Rendering)光照模型,包括:

  • 主光源和附加光源
  • 阴影支持
  • 环境光遮蔽
  • 法线贴图
  • 金属度/光滑度工作流
  • 光照贴图支持
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
// Lit Shader的关键特性
Shader "Universal Render Pipeline/Lit"
{
Properties
{
// 主要材质属性
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)

_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

[HDR] _SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5

_BumpScale("Normal Scale", Range(0.0, 2.0)) = 1.0
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}

_EmissionColor("Emission", Color) = (0,0,0)
[HDR] _EmissionMap("Emission", 2D) = "white" {}

_OcclusionStrength("Occlusion", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}

_DetailMask("Detail Mask", 2D) = "white" {}
_DetailAlbedoMap("Detail Albedo x2", 2D) = "linearGrey" {}
_DetailNormalMapScale("Scale", Range(0.0, 2.0)) = 1.0
[Normal] _DetailNormalMap("Normal Map", 2D) = "bump" {}

[Enum(UV0, 0, UV1, 1)] _UVSec("UV Set for secondary textures", Float) = 0

[HideInInspector] _AlphaClip("Alpha Clipping", Float) = 0
[HideInInspector] _Blend("Blending State", Float) = 0
[HideInInspector] _SrcBlend("SrcBlend", Float) = 1.0
[HideInInspector] _DstBlend("DstBlend", Float) = 0.0
[HideInInspector] _ZWrite("ZWrite", Float) = 1.0
[HideInInspector] _Cull("Cull Mode", Float) = 2.0
[HideInInspector] _CastShadows("Cast Shadows", Float) = 1.0
}

SubShader
{
// 渲染标签,指定使用URP
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
"UniversalMaterialType" = "Lit"
}

// 多个Pass处理不同的渲染情况
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }

// 渲染状态设置
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
// 编译指令
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION

// 包含必要的头文件
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitForwardPass.hlsl"
ENDHLSL
}

// 阴影投射Pass
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }

ZWrite On
ZTest LEqual
ColorMask 0

HLSLPROGRAM
#pragma multi_compile _ _CASTING_PUNCTUAL_LIGHT_SHADOW
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShadowCasterPass.hlsl"
ENDHLSL
}

// 深度预通道Pass
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }

ZWrite On
ColorMask 0

HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DepthOnlyPass.hlsl"
ENDHLSL
}
}
}

2. SimpleLit Shader

SimpleLit Shader是简化版的光照着色器,相比Lit 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
// SimpleLit Shader示例
Shader "Universal Render Pipeline/SimpleLit"
{
Properties
{
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)

_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

_SpecColor("Specular", Color) = (0.5, 0.5, 0.5)
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
_SpecularHighlights("Specular Highlights", Float) = 1.0

[ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0

_BumpScale("Normal Scale", Range(0.0, 2.0)) = 1.0
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}

_EmissionColor("Emission", Color) = (0,0,0)
[HDR] _EmissionMap("Emission", 2D) = "white" {}
}

SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
"UniversalMaterialType" = "SimpleLit"
}

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

Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
// 简化的多编译指令
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _SHADOWS_SOFT

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SimpleLitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SimpleLitForwardPass.hlsl"
ENDHLSL
}
}
}

3. Unlit Shader

Unlit Shader是最简单的着色器类型,不进行任何光照计算:

  • 无光照计算
  • 最高性能
  • 适合UI元素、特效、不需要光照的物体
  • 支持透明度和颜色调整
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
// Unlit Shader示例
Shader "Universal Render Pipeline/Unlit"
{
Properties
{
[MainTexture] _BaseMap("Texture", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)

_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

[Toggle(_ALPHATEST_ON)] _AlphaTest("Alpha Test", Float) = 0.0
[Toggle(_ALPHAPREMULTIPLY_ON)] _SrcBlend("SrcBlend", Float) = 1.0
[Toggle(_ALPHAMODULATE_ON)] _DstBlend("DstBlend", Float) = 0.0
[Toggle(_ZWRITE_ON)] _ZWrite("ZWrite", Float) = 1.0
}

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

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

Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
#pragma multi_compile _ _ALPHATEST_ON
#pragma multi_compile _ _ALPHAPREMULTIPLY_ON
#pragma multi_compile _ _ALPHAMODULATE_ON
#pragma multi_compile _ _ZWRITE_ON

#pragma vertex UnlitVertex
#pragma fragment UnlitFragment

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
CBUFFER_END

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};

Varyings UnlitVertex(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
return output;
}

half4 UnlitFragment(Varyings input) : SV_Target
{
half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
half4 color = texColor * _BaseColor;

#ifdef _ALPHATEST_ON
clip(color.a - _Cutoff);
#endif

return color;
}
ENDHLSL
}
}
}

ShaderGraph在URP中的应用

ShaderGraph是Unity提供的可视化着色器编辑工具,特别适合URP使用。它允许开发者通过节点图的方式创建着色器,无需编写代码。

ShaderGraph在URP中的优势:

  1. 可视化编辑:通过拖拽节点创建着色器逻辑
  2. 实时预览:即时查看着色器效果
  3. 易于修改:快速调整参数和效果
  4. 性能优化:自动生成优化的着色器代码

创建URP ShaderGraph的步骤:

  1. 创建Shader Graph:右键Assets → Create → Shader → Universal Render Pipeline → Lit Shader Graph
  2. 配置节点:添加和连接各种节点
  3. 设置材质属性:通过Property节点暴露参数
  4. 连接输出:将结果连接到Master节点
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
// ShaderGraph在代码中的使用示例
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class ShaderGraphExample : MonoBehaviour
{
[Header("Material References")]
public Material litShaderGraphMaterial;
public Material unlitShaderGraphMaterial;

[Header("Animation Parameters")]
public float pulseSpeed = 2.0f;
public float pulseAmount = 0.5f;

private float baseEmission;
private MaterialPropertyBlock propertyBlock;

void Start()
{
propertyBlock = new MaterialPropertyBlock();

if(litShaderGraphMaterial != null)
{
// 获取基础发射值
baseEmission = litShaderGraphMaterial.GetFloat("_Emission");
}
}

void Update()
{
AnimateMaterials();
}

void AnimateMaterials()
{
if(litShaderGraphMaterial != null)
{
// 通过材质属性块更新Shader Graph参数
float emission = baseEmission + Mathf.Sin(Time.time * pulseSpeed) * pulseAmount;
litShaderGraphMaterial.SetFloat("_Emission", emission);

// 或者使用MaterialPropertyBlock
propertyBlock.SetFloat("_Emission", emission);
propertyBlock.SetColor("_Tint", Color.red);
GetComponent<Renderer>().SetPropertyBlock(propertyBlock);
}
}

// 动态切换Shader Graph材质
public void SwitchToUnlit()
{
if(GetComponent<Renderer>() != null && unlitShaderGraphMaterial != null)
{
GetComponent<Renderer>().material = unlitShaderGraphMaterial;
}
}

public void SwitchToLit()
{
if(GetComponent<Renderer>() != null && litShaderGraphMaterial != null)
{
GetComponent<Renderer>().material = litShaderGraphMaterial;
}
}
}

常用URP Shader属性与关键字

URP着色器使用特定的属性名称和关键字来确保与渲染管线的兼容性:

常用属性名称:

  • _BaseMap / _MainTex:基础贴图
  • _BaseColor / _Color:基础颜色
  • _BumpMap:法线贴图
  • _Metallic:金属度
  • _Smoothness:光滑度
  • _EmissionMap:发射贴图
  • _OcclusionMap:遮蔽贴图

常用编译关键字:

  • _MAIN_LIGHT_SHADOWS:主光源阴影
  • _MAIN_LIGHT_SHADOWS_CASCADE:级联阴影
  • _ADDITIONAL_LIGHTS:附加光源
  • _SHADOWS_SOFT:软阴影
  • _SCREEN_SPACE_OCCLUSION:屏幕空间遮蔽

URP材质工作流程

URP的材质工作流程与传统管线有所不同,主要体现在:

  1. 材质创建:使用URP特定的着色器
  2. 参数映射:URP使用标准化的参数名称
  3. 渲染队列:通过渲染标签控制渲染顺序
  4. 材质变体:根据启用的功能生成不同的着色器变体
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
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class URPMaterialWorkflow : MonoBehaviour
{
[Header("Material Configuration")]
public Shader urpLitShader;
public Shader urpUnlitShader;

[Header("Material Properties")]
public Color baseColor = Color.white;
public Texture2D baseTexture;
public float metallic = 0.0f;
public float smoothness = 0.5f;
public Texture2D normalMap;

private Material dynamicMaterial;

void Start()
{
CreateDynamicMaterial();
}

void CreateDynamicMaterial()
{
if(urpLitShader != null)
{
dynamicMaterial = new Material(urpLitShader);

// 设置URP标准材质属性
dynamicMaterial.SetColor("_BaseColor", baseColor);
dynamicMaterial.SetTexture("_BaseMap", baseTexture);
dynamicMaterial.SetFloat("_Metallic", metallic);
dynamicMaterial.SetFloat("_Smoothness", smoothness);

if(normalMap != null)
{
dynamicMaterial.SetTexture("_BumpMap", normalMap);
dynamicMaterial.EnableKeyword("_NORMALMAP");
}

// 应用材质到渲染器
var renderer = GetComponent<Renderer>();
if(renderer != null)
{
renderer.material = dynamicMaterial;
}
}
}

// 动态修改材质属性
public void UpdateMaterialProperties()
{
if(dynamicMaterial != null)
{
dynamicMaterial.SetColor("_BaseColor", baseColor);
dynamicMaterial.SetFloat("_Metallic", metallic);
dynamicMaterial.SetFloat("_Smoothness", smoothness);

// 根据参数启用/禁用着色器关键字
if(metallic > 0.0f)
{
dynamicMaterial.EnableKeyword("_METALLICSPECGLOSSMAP");
}
else
{
dynamicMaterial.DisableKeyword("_METALLICSPECGLOSSMAP");
}
}
}

// 切换渲染队列
public void SetRenderQueue(int queue)
{
if(dynamicMaterial != null)
{
dynamicMaterial.renderQueue = queue;
}
}

// 启用透明度
public void SetTransparent(bool transparent)
{
if(dynamicMaterial != null)
{
if(transparent)
{
dynamicMaterial.SetFloat("_Surface", 1); // 透明度模式
dynamicMaterial.SetOverrideTag("RenderType", "Transparent");
dynamicMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
dynamicMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
dynamicMaterial.SetInt("_ZWrite", 0);
dynamicMaterial.DisableKeyword("_ALPHATEST_ON");
dynamicMaterial.EnableKeyword("_ALPHAPREMULTIPLY_ON");
}
else
{
dynamicMaterial.SetFloat("_Surface", 0); // 不透明模式
dynamicMaterial.SetOverrideTag("RenderType", "Opaque");
dynamicMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
dynamicMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
dynamicMaterial.SetInt("_ZWrite", 1);
dynamicMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
}
}
}
}

代码示例

自定义URP着色器实现

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
// 自定义URP Toon着色器
Shader "URP/Custom/ToonShader"
{
Properties
{
_BaseMap ("Texture", 2D) = "white" {}
_BaseColor ("Color", Color) = (1, 1, 1, 1)
_Ramp ("Ramp Texture", 2D) = "white" {}
_RampThreshold ("Ramp Threshold", Range(0, 1)) = 0.5
_RampSmooth ("Ramp Smooth", Range(0, 1)) = 0.1
_OutlineWidth ("Outline Width", Range(0, 1)) = 0.1
_OutlineColor ("Outline Color", Color) = (0, 0, 0, 1)
_SpecularThreshold ("Specular Threshold", Range(0, 1)) = 0.8
}

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

// 轮廓Pass
Pass
{
Name "Outline"
Tags { "LightMode" = "UniversalForward" }

Cull Front
ZWrite On

HLSLPROGRAM
#pragma vertex OutlineVertex
#pragma fragment OutlineFragment
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
};

float _OutlineWidth;
float4 _OutlineColor;

Varyings OutlineVertex(Attributes input)
{
Varyings output;

float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
float3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionOS.xyz);

float3 outlineOffset = normalWS * _OutlineWidth;
float4 posWS = TransformObjectToWorld(input.positionOS.xyz);
posWS.xyz += outlineOffset;

output.positionCS = TransformWorldToHClip(posWS.xyz);
return output;
}

half4 OutlineFragment(Varyings input) : SV_Target
{
return _OutlineColor;
}
ENDHLSL
}

// 主渲染Pass
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }

Cull Back
ZWrite On

HLSLPROGRAM
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS

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

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
TEXTURE2D(_Ramp);
SAMPLER(sampler_Ramp);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
float _RampThreshold;
float _RampSmooth;
float _SpecularThreshold;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float2 uv : TEXCOORD2;
};

Varyings LitPassVertex(Attributes input)
{
Varyings output;

VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, float4(0, 0, 0, 0));

output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);

return output;
}

half4 LitPassFragment(Varyings input) : SV_Target
{
// 采样基础贴图
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;

// 计算光照
float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
float3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);

// 主光源
Light mainLight = GetMainLight();
float3 lightDir = mainLight.direction;

// 计算漫反射
float NdotL = dot(normalWS, lightDir);
NdotL = saturate(NdotL);

// 使用渐变贴图创建卡通效果
float ramp = smoothstep(_RampThreshold - _RampSmooth, _RampThreshold + _RampSmooth, NdotL);
ramp = SAMPLE_TEXTURE2D(_Ramp, sampler_Ramp, float2(ramp, 0)).r;

// 计算镜面反射
float3 reflectDir = reflect(-lightDir, normalWS);
float specular = pow(max(dot(viewDirWS, reflectDir), 0.0), 32.0);
specular = step(_SpecularThreshold, specular);

// 组合最终颜色
half3 finalColor = baseColor.rgb * mainLight.color.rgb * ramp;
finalColor += specular * mainLight.color.rgb;

return half4(finalColor, baseColor.a);
}
ENDHLSL
}
}

Fallback "Universal Render Pipeline/Lit"
}

材质切换和管理脚本

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
using UnityEngine;
using UnityEngine.Rendering.Universal;
using System.Collections.Generic;

public class URPShaderManager : MonoBehaviour
{
[System.Serializable]
public class MaterialPreset
{
public string presetName;
public Material material;
public Shader shader;
public Color baseColor = Color.white;
public float metallic = 0.0f;
public float smoothness = 0.5f;
public Texture2D texture;
}

[Header("Material Presets")]
public List<MaterialPreset> materialPresets = new List<MaterialPreset>();

[Header("Current Material")]
public int currentPresetIndex = 0;

private Renderer targetRenderer;
private Material currentMaterial;
private Dictionary<string, Material> materialCache = new Dictionary<string, Material>();

void Start()
{
targetRenderer = GetComponent<Renderer>();
if(targetRenderer != null)
{
ApplyMaterialPreset(currentPresetIndex);
}
}

public void ApplyMaterialPreset(int index)
{
if(index < 0 || index >= materialPresets.Count)
{
Debug.LogError($"Invalid preset index: {index}");
return;
}

var preset = materialPresets[index];
currentPresetIndex = index;

if(targetRenderer != null)
{
// 尝试从缓存获取材质
if(materialCache.ContainsKey(preset.presetName))
{
currentMaterial = materialCache[preset.presetName];
}
else
{
// 创建新材质
if(preset.material != null)
{
currentMaterial = new Material(preset.material);
}
else if(preset.shader != null)
{
currentMaterial = new Material(preset.shader);
}
else
{
Debug.LogError("No material or shader specified for preset: " + preset.presetName);
return;
}

// 应用属性
ApplyMaterialProperties(currentMaterial, preset);

// 缓存材质
materialCache[preset.presetName] = currentMaterial;
}

targetRenderer.material = currentMaterial;
Debug.Log($"Applied material preset: {preset.presetName}");
}
}

void ApplyMaterialProperties(Material material, MaterialPreset preset)
{
if(material == null) return;

// 应用URP标准属性
if(material.HasProperty("_BaseColor"))
{
material.SetColor("_BaseColor", preset.baseColor);
}
if(material.HasProperty("_Color"))
{
material.SetColor("_Color", preset.baseColor);
}

if(material.HasProperty("_Metallic"))
{
material.SetFloat("_Metallic", preset.metallic);
}

if(material.HasProperty("_Smoothness"))
{
material.SetFloat("_Smoothness", preset.smoothness);
}

if(material.HasProperty("_BaseMap") && preset.texture != null)
{
material.SetTexture("_BaseMap", preset.texture);
}

if(material.HasProperty("_MainTex") && preset.texture != null)
{
material.SetTexture("_MainTex", preset.texture);
}

// 根据参数启用相应的着色器关键字
if(preset.metallic > 0.0f)
{
material.EnableKeyword("_METALLICSPECGLOSSMAP");
}
else
{
material.DisableKeyword("_METALLICSPECGLOSSMAP");
}
}

// 动态修改当前材质属性
public void ModifyCurrentMaterial(float metallic, float smoothness)
{
if(currentMaterial != null)
{
currentMaterial.SetFloat("_Metallic", metallic);
currentMaterial.SetFloat("_Smoothness", smoothness);

if(metallic > 0.0f)
{
currentMaterial.EnableKeyword("_METALLICSPECGLOSSMAP");
}
else
{
currentMaterial.DisableKeyword("_METALLICSPECGLOSSMAP");
}
}
}

// 获取当前材质信息
public string GetCurrentMaterialInfo()
{
if(currentMaterial != null)
{
return $"Material: {currentMaterial.name}\n" +
$"Shader: {currentMaterial.shader.name}\n" +
$"Render Queue: {currentMaterial.renderQueue}";
}

return "No material assigned";
}

// 切换到下一个材质预设
public void NextPreset()
{
currentPresetIndex = (currentPresetIndex + 1) % materialPresets.Count;
ApplyMaterialPreset(currentPresetIndex);
}

// 切换到上一个材质预设
public void PreviousPreset()
{
currentPresetIndex = (currentPresetIndex - 1 + materialPresets.Count) % materialPresets.Count;
ApplyMaterialPreset(currentPresetIndex);
}
}

实践练习

练习1:创建自定义URP透明着色器

目标:创建一个支持透明度和裁剪的URP着色器

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
Shader "URP/Custom/TransparentCutout"
{
Properties
{
_BaseMap ("Texture", 2D) = "white" {}
_BaseColor ("Color", Color) = (1, 1, 1, 1)
_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Transparency ("Transparency", Range(0.0, 1.0)) = 0.5
[Toggle(_ALPHA_CLIP)] _UseAlphaClip ("Alpha Clip", Float) = 0.0
}

SubShader
{
Tags
{
"RenderType" = "TransparentCutout"
"RenderPipeline" = "UniversalPipeline"
"IgnoreProjector" = "True"
}

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

Blend SrcAlpha OneMinusSrcAlpha
ZWrite[_ZWrite]
Cull[_Cull]

HLSLPROGRAM
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _SHADOWS_SOFT

#pragma shader_feature_local _ALPHA_CLIP

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

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
half _Transparency;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float2 uv : TEXCOORD2;
};

Varyings LitPassVertex(Attributes input)
{
Varyings output;

VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, float4(0, 0, 0, 0));

output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);

return output;
}

half4 LitPassFragment(Varyings input) : SV_Target
{
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;

// 应用透明度
baseColor.a *= (1.0 - _Transparency);

// Alpha裁剪
#ifdef _ALPHA_CLIP
clip(baseColor.a - _Cutoff);
#endif

// 如果没有启用Alpha裁剪,使用透明度混合
#ifndef _ALPHA_CLIP
if (baseColor.a < _Cutoff)
discard;
#endif

// 简单的光照计算
float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
Light mainLight = GetMainLight();
float3 lightDir = mainLight.direction;
float NdotL = saturate(dot(normalWS, lightDir));

half3 finalColor = baseColor.rgb * mainLight.color.rgb * NdotL;

return half4(finalColor, baseColor.a);
}
ENDHLSL
}
}

Fallback "Universal Render Pipeline/Unlit"
}

练习2:创建材质切换系统

目标:创建一个完整的材质切换和管理系统

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
using UnityEngine;
using UnityEngine.Rendering.Universal;
using System.Collections.Generic;
using System.Linq;

public class AdvancedMaterialSystem : MonoBehaviour
{
[System.Serializable]
public class MaterialConfiguration
{
public string configName;
public Shader shader;
public Dictionary<string, object> properties = new Dictionary<string, object>();
public List<string> enabledKeywords = new List<string>();
}

[Header("Material Configurations")]
public List<MaterialConfiguration> configurations = new List<MaterialConfiguration>();

[Header("Animation Settings")]
public bool animateProperties = false;
public float animationSpeed = 1.0f;

private Renderer targetRenderer;
private Material currentMaterial;
private int currentConfigIndex = 0;
private float animationTime = 0f;

void Start()
{
targetRenderer = GetComponent<Renderer>();
if(targetRenderer != null && configurations.Count > 0)
{
ApplyConfiguration(0);
}
}

void Update()
{
if(animateProperties && currentMaterial != null)
{
AnimateProperties();
}
}

public void ApplyConfiguration(int index)
{
if(index < 0 || index >= configurations.Count)
{
Debug.LogError($"Invalid configuration index: {index}");
return;
}

var config = configurations[index];
currentConfigIndex = index;

if(targetRenderer != null)
{
// 创建或更新材质
if(currentMaterial == null || currentMaterial.shader != config.shader)
{
currentMaterial = new Material(config.shader);
}

// 应用属性
ApplyConfigurationProperties(currentMaterial, config);

// 应用关键字
ApplyKeywords(currentMaterial, config.enabledKeywords);

targetRenderer.material = currentMaterial;

Debug.Log($"Applied configuration: {config.configName}");
}
}

void ApplyConfigurationProperties(Material material, MaterialConfiguration config)
{
foreach(var property in config.properties)
{
if(property.Value is Color)
{
material.SetColor(property.Key, (Color)property.Value);
}
else if(property.Value is float)
{
material.SetFloat(property.Key, (float)property.Value);
}
else if(property.Value is Vector4)
{
material.SetVector(property.Key, (Vector4)property.Value);
}
else if(property.Value is Texture)
{
material.SetTexture(property.Key, (Texture)property.Value);
}
}
}

void ApplyKeywords(Material material, List<string> keywords)
{
// 清除所有关键字
var currentKeywords = material.shaderKeywords.ToList();
foreach(var keyword in currentKeywords)
{
material.DisableKeyword(keyword);
}

// 启用指定关键字
foreach(var keyword in keywords)
{
material.EnableKeyword(keyword);
}
}

void AnimateProperties()
{
animationTime += Time.deltaTime * animationSpeed;

if(currentMaterial != null)
{
// 示例:动画化颜色和参数
Color animatedColor = Color.HSVToRGB(
(Mathf.Sin(animationTime) + 1) * 0.5f,
1.0f,
1.0f
);

currentMaterial.SetColor("_BaseColor", animatedColor);
currentMaterial.SetFloat("_Smoothness", (Mathf.Sin(animationTime * 2) + 1) * 0.25f + 0.25f);
}
}

// 添加配置
public void AddConfiguration(MaterialConfiguration config)
{
configurations.Add(config);
}

// 获取当前配置信息
public string GetCurrentConfigurationInfo()
{
if(currentConfigIndex >= 0 && currentConfigIndex < configurations.Count)
{
var config = configurations[currentConfigIndex];
return $"Configuration: {config.configName}\n" +
$"Shader: {config.shader.name}\n" +
$"Properties: {config.properties.Count}\n" +
$"Keywords: {config.enabledKeywords.Count}";
}

return "No configuration applied";
}

// 切换到下一个配置
public void NextConfiguration()
{
int nextIndex = (currentConfigIndex + 1) % configurations.Count;
ApplyConfiguration(nextIndex);
}

// 切换到上一个配置
public void PreviousConfiguration()
{
int prevIndex = (currentConfigIndex - 1 + configurations.Count) % configurations.Count;
ApplyConfiguration(prevIndex);
}

// 保存当前材质状态为新配置
public void SaveCurrentAsConfiguration(string name)
{
if(currentMaterial != null)
{
var newConfig = new MaterialConfiguration
{
configName = name,
shader = currentMaterial.shader
};

// 保存当前材质属性(这里简化处理,实际可能需要更复杂的属性提取)
newConfig.properties.Add("_BaseColor", currentMaterial.GetColor("_BaseColor"));
newConfig.properties.Add("_Metallic", currentMaterial.GetFloat("_Metallic"));
newConfig.properties.Add("_Smoothness", currentMaterial.GetFloat("_Smoothness"));

// 保存启用的关键字
foreach(var keyword in currentMaterial.shaderKeywords)
{
newConfig.enabledKeywords.Add(keyword);
}

configurations.Add(newConfig);
Debug.Log($"Saved current material as configuration: {name}");
}
}
}

总结

本章详细介绍了URP着色器的基础知识,包括URP Shader与Built-in Shader的区别、三种主要Shader类型(Lit、SimpleLit、Unlit)的特点和用途、ShaderGraph在URP中的应用,以及常用的URP Shader属性和关键字。

通过理论讲解、代码示例和实践练习,我们学习了:

  1. URP Shader结构:理解了URP着色器与传统Built-in着色器在结构和功能上的差异,URP着色器利用了SRP框架的优势,提供了更好的性能和更灵活的扩展性。

  2. Shader类型对比:掌握了Lit、SimpleLit和Unlit三种主要Shader类型的特点和适用场景,能够根据项目需求选择合适的Shader类型。

  3. ShaderGraph应用:了解了ShaderGraph在URP中的优势和使用方法,这是一种强大的可视化着色器创建工具。

  4. URP材质工作流程:学习了URP中材质的创建、配置和管理方法,包括属性映射、渲染队列控制和材质变体管理。

  5. 实践应用:通过创建自定义着色器和材质管理系统,将理论知识转化为实际应用。

URP着色器是实现高质量视觉效果的关键组件,掌握URP着色器的使用对于开发优秀的Unity项目至关重要。在下一章中,我们将深入探讨URP光照系统,了解如何在URP中实现各种光照效果。