第6章 URP阴影系统

第6章 URP阴影系统

理论讲解

级联阴影贴图(Cascade Shadow Maps)

级联阴影贴图(Cascaded Shadow Maps, CSM)是现代渲染管线中处理大范围定向光源阴影的重要技术。在URP中,CSM被用于主光源(通常是太阳光)的阴影渲染,以在不同距离上提供高质量的阴影效果。

级联阴影的工作原理

级联阴影贴图将相机的视锥体分割成多个区域(通常是2个或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
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class CascadeShadowMapController : MonoBehaviour
{
[Header("Cascade Shadow Configuration")]
public Light mainLight;
public int cascadeCount = 4; // 2 or 4 cascades
[Range(0.01f, 1.0f)]
public float cascadeSplitLambda = 0.5f; // 0.5 is a good balance between PSSM and CSF
[Range(0.0f, 1.0f)]
public float shadowDistance = 50.0f;

[Header("Cascade Visualization")]
public bool visualizeCascades = false;
public Material cascadeVisualizationMaterial;

private UniversalRenderPipelineAsset urpAsset;
private float[] cascadeSplits = new float[4];

void Start()
{
SetupCascadeShadows();
}

void SetupCascadeShadows()
{
// 获取URP资源
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 配置阴影距离
urpAsset.shadowDistance = shadowDistance;

// 配置级联数量
urpAsset.shadowCascadeOption =
cascadeCount == 2 ? ShadowCascadeOption.TwoCascades : ShadowCascadeOption.FourCascades;

// 配置级联分割比例
urpAsset.shadowCascadeSplit = cascadeSplitLambda;
}

if(mainLight != null)
{
// 确保主光源启用阴影
mainLight.shadows = LightShadows.Soft;
}
}

// 计算级联分割
public void CalculateCascadeSplits(Camera camera)
{
if(camera == null) return;

// 计算级联分割距离
float cameraNear = camera.nearClipPlane;
float cameraFar = Mathf.Min(camera.farClipPlane, shadowDistance);

// 使用指数和线性混合的方式计算分割
float t0 = cameraNear;
float t1 = cameraFar;

// 计算每个级联的分割点
for(int i = 0; i < cascadeCount - 1; i++)
{
float fraction = (float)(i + 1) / cascadeCount;
float logSplit = Mathf.Lerp(t0, t1, Mathf.Pow(fraction, cascadeSplitLambda));
float uniformSplit = Mathf.Lerp(t0, t1, fraction);

cascadeSplits[i] = Mathf.Lerp(logSplit, uniformSplit, 0.5f);
}

cascadeSplits[cascadeCount - 1] = cameraFar;
}

// 获取级联信息
public string GetCascadeInfo()
{
string info = "Cascade Shadow Information:\n";
info += $"Cascade Count: {cascadeCount}\n";
info += $"Split Lambda: {cascadeSplitLambda:F3}\n";
info += $"Shadow Distance: {shadowDistance:F1}\n";

for(int i = 0; i < cascadeCount - 1; i++)
{
info += $"Cascade {i + 1} Split: {cascadeSplits[i]:F1}\n";
}

return info;
}

// 动态调整级联参数
public void AdjustCascadeParameters(int newCascadeCount, float newSplitLambda)
{
cascadeCount = Mathf.Clamp(newCascadeCount, 2, 4);
cascadeSplitLambda = Mathf.Clamp01(newSplitLambda);

if(urpAsset != null)
{
urpAsset.shadowCascadeOption =
cascadeCount == 2 ? ShadowCascadeOption.TwoCascades : ShadowCascadeOption.FourCascades;
urpAsset.shadowCascadeSplit = cascadeSplitLambda;
}
}
}

阴影距离与分辨率配置

阴影系统中的距离和分辨率配置直接影响性能和视觉质量。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
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class ShadowDistanceController : MonoBehaviour
{
[Header("Shadow Distance Configuration")]
public float shadowDistance = 50.0f;
public float shadowFadeStartDistance = 40.0f;
[Range(0.0f, 1.0f)]
public float shadowFadeStrength = 1.0f;

[Header("Performance Settings")]
public int shadowResolution = 2048;
public int shadowAtlasResolution = 2048;

private UniversalRenderPipelineAsset urpAsset;

void Start()
{
ConfigureShadowDistance();
}

void ConfigureShadowDistance()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 设置阴影距离
urpAsset.shadowDistance = shadowDistance;

// 注意:URP中阴影淡入淡出是自动处理的
// 基于阴影距离和相机远裁剪面的距离比例
}
}

// 动态调整阴影距离
public void SetShadowDistance(float distance)
{
shadowDistance = Mathf.Max(1.0f, distance);

if(urpAsset != null)
{
urpAsset.shadowDistance = shadowDistance;
}
}

// 获取阴影距离信息
public string GetShadowDistanceInfo()
{
return $"Shadow Distance: {shadowDistance:F1}\n" +
$"Fade Start Distance: {shadowFadeStartDistance:F1}\n" +
$"Fade Strength: {shadowFadeStrength:F2}\n" +
$"Shadow Resolution: {shadowResolution}\n" +
$"Atlas Resolution: {shadowAtlasResolution}";
}

// 优化阴影距离基于相机设置
public void OptimizeShadowDistance(Camera camera)
{
if(camera != null)
{
// 基于相机远裁剪面设置阴影距离
float optimizedDistance = Mathf.Min(camera.farClipPlane * 0.8f, 100.0f);
SetShadowDistance(optimizedDistance);

// 调整淡入距离为阴影距离的80%
shadowFadeStartDistance = shadowDistance * 0.8f;
}
}
}

阴影分辨率配置

阴影分辨率影响阴影的清晰度和性能。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
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class ShadowResolutionController : MonoBehaviour
{
[Header("Shadow Resolution Settings")]
public LightShadowResolution resolution = LightShadowResolution.Medium;
public int customResolution = 1024;
public bool useCustomResolution = false;

[Header("Atlas Configuration")]
public int atlasWidth = 1024;
public int atlasHeight = 1024;
public float atlasPadding = 2.0f;

private UniversalRenderPipelineAsset urpAsset;

void Start()
{
ConfigureShadowResolution();
}

void ConfigureShadowResolution()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 设置阴影图集分辨率
switch(resolution)
{
case LightShadowResolution.Low:
urpAsset.shadowAtlasResolution = 256;
break;
case LightShadowResolution.Medium:
urpAsset.shadowAtlasResolution = 512;
break;
case LightShadowResolution.High:
urpAsset.shadowAtlasResolution = 1024;
break;
case LightShadowResolution.VeryHigh:
urpAsset.shadowAtlasResolution = 2048;
break;
}

if(useCustomResolution)
{
urpAsset.shadowAtlasResolution = customResolution;
}
}
}

// 设置阴影分辨率
public void SetShadowResolution(LightShadowResolution res)
{
resolution = res;
ConfigureShadowResolution();
}

// 设置自定义分辨率
public void SetCustomResolution(int res)
{
customResolution = Mathf.ClosestPowerOfTwo(Mathf.Clamp(res, 16, 4096));
useCustomResolution = true;
ConfigureShadowResolution();
}

// 获取分辨率信息
public string GetResolutionInfo()
{
return $"Resolution Setting: {resolution}\n" +
$"Custom Resolution: {(useCustomResolution ? customResolution.ToString() : "None")}\n" +
$"Atlas Size: {atlasWidth}x{atlasHeight}\n" +
$"Padding: {atlasPadding}";
}

// 根据性能预算自动选择分辨率
public void AutoSelectResolution(float performanceBudget = 1.0f)
{
if(performanceBudget > 0.8f)
{
SetShadowResolution(LightShadowResolution.VeryHigh);
}
else if(performanceBudget > 0.5f)
{
SetShadowResolution(LightShadowResolution.High);
}
else if(performanceBudget > 0.3f)
{
SetShadowResolution(LightShadowResolution.Medium);
}
else
{
SetShadowResolution(LightShadowResolution.Low);
}
}
}

Shadow Bias参数调优

Shadow Bias(阴影偏移)是解决阴影渲染中常见问题(如阴影痤疮和彼得潘效应)的重要参数。

阴影痤疮(Shadow Acne)

阴影痤疮是由于深度比较精度问题导致的伪影,表现为物体表面出现不自然的条纹或斑点。

彼得潘效应(Peter Panning)

彼得潘效应是由于过大的阴影偏移导致阴影与物体分离的现象。

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

public class ShadowBiasController : MonoBehaviour
{
[Header("Shadow Bias Configuration")]
[Range(0.0f, 2.0f)]
public float shadowDepthBias = 1.0f;
[Range(0.0f, 2.0f)]
public float shadowNormalBias = 1.0f;
[Range(0.0f, 1.0f)]
public float shadowNearPlaneOffset = 0.2f;

[Header("Bias Adjustment")]
public float biasMultiplier = 1.0f;
public float normalBiasMultiplier = 1.0f;

private UniversalRenderPipelineAsset urpAsset;

void Start()
{
ConfigureShadowBias();
}

void ConfigureShadowBias()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 设置阴影深度偏移
urpAsset.shadowDepthBias = shadowDepthBias * biasMultiplier;

// 设置阴影法线偏移
urpAsset.shadowNormalBias = shadowNormalBias * normalBiasMultiplier;

// 设置近平面偏移
urpAsset.shadowNearPlane = shadowNearPlaneOffset;
}
}

// 动态调整偏移参数
public void AdjustBiasParameters(float depthBias, float normalBias)
{
shadowDepthBias = Mathf.Clamp(depthBias, 0.0f, 2.0f);
shadowNormalBias = Mathf.Clamp(normalBias, 0.0f, 2.0f);

ConfigureShadowBias();
}

// 针对特定场景优化偏移
public void OptimizeBiasForScene(float sceneScale = 1.0f)
{
// 根据场景规模调整偏移
float scaleAdjustment = Mathf.Log(sceneScale, 10.0f) + 1.0f;

shadowDepthBias = Mathf.Clamp(1.0f * scaleAdjustment, 0.1f, 2.0f);
shadowNormalBias = Mathf.Clamp(0.5f * scaleAdjustment, 0.1f, 2.0f);

ConfigureShadowBias();
}

// 获取偏移信息
public string GetBiasInfo()
{
return $"Depth Bias: {shadowDepthBias:F3}\n" +
$"Normal Bias: {shadowNormalBias:F3}\n" +
$"Near Plane Offset: {shadowNearPlaneOffset:F3}\n" +
$"Bias Multiplier: {biasMultiplier:F2}\n" +
$"Normal Bias Multiplier: {normalBiasMultiplier:F2}";
}

// 阴影质量测试
public void TestShadowQuality()
{
// 这里可以实现阴影质量的自动测试
Debug.Log("Testing shadow quality with current bias settings...");

// 检查常见的阴影问题
CheckForShadowAcne();
CheckForPeterPanning();
}

void CheckForShadowAcne()
{
// 检查是否有阴影痤疮的迹象
Debug.Log("Checking for shadow acne...");
// 实际实现会需要分析渲染结果
}

void CheckForPeterPanning()
{
// 检查是否有彼得潘效应
Debug.Log("Checking for Peter Panning effect...");
// 实际实现会需要分析渲染结果
}
}

软阴影实现原理

软阴影通过模拟真实世界中光源具有一定尺寸的效果,使阴影边缘呈现柔和的过渡效果。

PCF(Percentage Closer Filtering)

PCF是最常用的软阴影技术之一,通过对阴影贴图进行多次采样并计算平均值来实现软阴影效果。

VSM(Variance Shadow Maps)

VSM使用统计方法来计算软阴影,可以产生更自然的软阴影效果。

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

public class SoftShadowController : MonoBehaviour
{
[Header("Soft Shadow Configuration")]
public Light lightWithSoftShadows;
public float softShadowQuality = 1.0f; // 0.0 to 1.0
public float shadowSoftness = 0.5f; // 0.0 to 1.0
public int sampleCount = 4; // 采样数量

[Header("Advanced Soft Shadow Settings")]
public float shadowRadius = 1.0f;
public float lightSize = 0.5f;
public float distanceScale = 1.0f;

void Start()
{
ConfigureSoftShadows();
}

void ConfigureSoftShadows()
{
if(lightWithSoftShadows != null)
{
// 启用软阴影
lightWithSoftShadows.shadows = LightShadows.Soft;

// 配置阴影参数
lightWithSoftShadows.shadowRadius = shadowRadius;
lightWithSoftShadows.shadowAngle = lightSize;
}
}

// 动态调整软阴影参数
public void AdjustSoftShadowParameters(float softness, int samples)
{
shadowSoftness = Mathf.Clamp01(softness);
sampleCount = Mathf.Clamp(samples, 1, 16);

// 在URP中,软阴影参数通常通过材质或Shader控制
UpdateSoftShadowSettings();
}

void UpdateSoftShadowSettings()
{
if(lightWithSoftShadows != null)
{
// 软阴影质量通常由URP管线设置控制
// 这里我们通过调整光源参数来影响软阴影效果
lightWithSoftShadows.shadowRadius = shadowRadius * shadowSoftness;
}
}

// 获取软阴影信息
public string GetSoftShadowInfo()
{
return $"Soft Shadow Quality: {softShadowQuality:F2}\n" +
$"Shadow Softness: {shadowSoftness:F2}\n" +
$"Sample Count: {sampleCount}\n" +
$"Shadow Radius: {shadowRadius:F2}\n" +
$"Light Size: {lightSize:F2}";
}

// 优化软阴影性能
public void OptimizeSoftShadowsForPerformance()
{
// 根据性能需求调整软阴影质量
if(PerformanceCheck.IsLowEndDevice())
{
shadowSoftness = 0.3f;
sampleCount = 2;
}
else if(PerformanceCheck.IsMidRangeDevice())
{
shadowSoftness = 0.6f;
sampleCount = 4;
}
else
{
shadowSoftness = 1.0f;
sampleCount = 8;
}

UpdateSoftShadowSettings();
}
}

// 性能检查辅助类
public static class PerformanceCheck
{
public static bool IsLowEndDevice()
{
// 根据设备规格判断
return SystemInfo.systemMemorySize < 4000 || // 4GB RAM
SystemInfo.graphicsMemorySize < 1000; // 1GB VRAM
}

public static bool IsMidRangeDevice()
{
return SystemInfo.systemMemorySize >= 4000 && SystemInfo.systemMemorySize < 8000 &&
SystemInfo.graphicsMemorySize >= 1000 && SystemInfo.graphicsMemorySize < 4000;
}

public static bool IsHighEndDevice()
{
return SystemInfo.systemMemorySize >= 8000 &&
SystemInfo.graphicsMemorySize >= 4000;
}
}

附加光源阴影处理

除了主光源的级联阴影外,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
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class AdditionalLightsShadowController : MonoBehaviour
{
[Header("Additional Lights Configuration")]
public Light[] additionalLights;
public int maxAdditionalLights = 4;
public float additionalShadowDistance = 20.0f;
public LightShadows additionalShadowType = LightShadows.Soft;

[Header("Shadow Resolution for Additional Lights")]
public LightShadowResolution additionalShadowResolution = LightShadowResolution.Medium;

[Header("Performance Settings")]
public bool enableAdditionalLightShadows = true;
public float shadowFadeDistance = 15.0f;

private UniversalRenderPipelineAsset urpAsset;

void Start()
{
ConfigureAdditionalLightShadows();
}

void ConfigureAdditionalLightShadows()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 设置最大附加光源数量
urpAsset.maxAdditionalLightsCount = maxAdditionalLights;
}

// 配置附加光源
foreach(var light in additionalLights)
{
if(light != null)
{
ConfigureAdditionalLight(light);
}
}
}

void ConfigureAdditionalLight(Light light)
{
if(!enableAdditionalLightShadows)
{
light.shadows = LightShadows.None;
return;
}

// 设置阴影类型
light.shadows = additionalShadowType;

// 设置阴影分辨率
light.shadowResolution = additionalShadowResolution;

// 对于点光源,设置阴影强度
if(light.type == LightType.Point)
{
light.shadowStrength = 1.0f;
}
}

// 动态管理附加光源
public void ManageAdditionalLights(Camera mainCamera)
{
if(mainCamera == null) return;

Vector3 cameraPos = mainCamera.transform.position;

int activeLightCount = 0;

foreach(var light in additionalLights)
{
if(light != null && activeLightCount < maxAdditionalLights)
{
float distance = Vector3.Distance(light.transform.position, cameraPos);

if(distance <= additionalShadowDistance)
{
// 启用光源和阴影
light.enabled = true;

if(distance > shadowFadeDistance)
{
// 距离较远时降低阴影质量
light.shadowResolution = LightShadowResolution.Low;
light.shadowStrength = Mathf.InverseLerp(additionalShadowDistance, shadowFadeDistance, distance);
}
else
{
// 距离较近时使用高质量阴影
light.shadowResolution = additionalShadowResolution;
light.shadowStrength = 1.0f;
}

activeLightCount++;
}
else
{
// 距离太远时禁用光源
light.enabled = false;
}
}
else if(light != null)
{
// 超过最大数量时禁用光源
light.enabled = false;
}
}
}

// 获取附加光源信息
public string GetAdditionalLightsInfo()
{
int enabledLights = 0;
int shadowLights = 0;

foreach(var light in additionalLights)
{
if(light != null && light.enabled)
{
enabledLights++;
if(light.shadows != LightShadows.None)
{
shadowLights++;
}
}
}

return $"Additional Lights Configuration:\n" +
$"Max Lights: {maxAdditionalLights}\n" +
$"Enabled Lights: {enabledLights}\n" +
$"Lights with Shadows: {shadowLights}\n" +
$"Shadow Distance: {additionalShadowDistance:F1}\n" +
$"Shadow Resolution: {additionalShadowResolution}";
}

// 优化附加光源性能
public void OptimizeAdditionalLightsPerformance()
{
if(PerformanceCheck.IsLowEndDevice())
{
maxAdditionalLights = 2;
additionalShadowType = LightShadows.Hard;
additionalShadowResolution = LightShadowResolution.Low;
}
else if(PerformanceCheck.IsMidRangeDevice())
{
maxAdditionalLights = 4;
additionalShadowType = LightShadows.Soft;
additionalShadowResolution = LightShadowResolution.Medium;
}
else
{
maxAdditionalLights = 8;
additionalShadowType = LightShadows.Soft;
additionalShadowResolution = LightShadowResolution.High;
}

ConfigureAdditionalLightShadows();
}
}

移动平台阴影优化

移动平台由于硬件限制,需要特别的阴影优化策略。

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

public class MobileShadowOptimization : MonoBehaviour
{
[Header("Mobile Shadow Settings")]
public bool optimizeForMobile = true;
[Range(1, 4)]
public int mobileCascadeCount = 2;
public LightShadowResolution mobileShadowResolution = LightShadowResolution.Low;
public float mobileShadowDistance = 25.0f;

[Header("Mobile-Specific Optimizations")]
public bool useShadowMask = false;
public bool reduceShadowQualityWithDistance = true;
public float distanceQualityReductionFactor = 0.5f;

private UniversalRenderPipelineAsset urpAsset;
private bool isMobilePlatform = false;

void Start()
{
DetectPlatformAndOptimize();
}

void DetectPlatformAndOptimize()
{
isMobilePlatform = Application.isMobilePlatform;

if(optimizeForMobile && isMobilePlatform)
{
ApplyMobileOptimizations();
}
}

void ApplyMobileOptimizations()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

if(urpAsset != null)
{
// 减少级联数量
urpAsset.shadowCascadeOption =
mobileCascadeCount == 2 ? ShadowCascadeOption.TwoCascades : ShadowCascadeOption.FourCascades;

// 降低阴影距离
urpAsset.shadowDistance = mobileShadowDistance;

// 降低阴影分辨率
int resolutionValue = 0;
switch(mobileShadowResolution)
{
case LightShadowResolution.Low:
resolutionValue = 256;
break;
case LightShadowResolution.Medium:
resolutionValue = 512;
break;
case LightShadowResolution.High:
resolutionValue = 1024;
break;
case LightShadowResolution.VeryHigh:
resolutionValue = 2048;
break;
}
urpAsset.shadowAtlasResolution = resolutionValue;

// 优化阴影偏移以适应移动设备
urpAsset.shadowDepthBias = Mathf.Max(urpAsset.shadowDepthBias, 1.5f);
urpAsset.shadowNormalBias = Mathf.Max(urpAsset.shadowNormalBias, 1.0f);
}

Debug.Log("Applied mobile shadow optimizations");
}

// 根据设备性能动态调整
public void AdjustForDevicePerformance()
{
if(!isMobilePlatform) return;

// 根据设备内存和GPU能力调整设置
if(SystemInfo.systemMemorySize < 3000) // 3GB以下内存
{
mobileShadowDistance = 15.0f;
mobileShadowResolution = LightShadowResolution.Low;
mobileCascadeCount = 2;
}
else if(SystemInfo.systemMemorySize < 6000) // 3-6GB内存
{
mobileShadowDistance = 25.0f;
mobileShadowResolution = LightShadowResolution.Medium;
mobileCascadeCount = 2;
}
else // 6GB以上内存
{
mobileShadowDistance = 35.0f;
mobileShadowResolution = LightShadowResolution.High;
mobileCascadeCount = 4;
}

ApplyMobileOptimizations();
}

// 获取移动优化信息
public string GetMobileOptimizationInfo()
{
return $"Mobile Optimization Status:\n" +
$"Is Mobile: {isMobilePlatform}\n" +
$"Cascade Count: {mobileCascadeCount}\n" +
$"Shadow Resolution: {mobileShadowResolution}\n" +
$"Shadow Distance: {mobileShadowDistance:F1}\n" +
$"Distance Quality Reduction: {reduceShadowQualityWithDistance}";
}

// 切换优化状态
public void ToggleMobileOptimization(bool enable)
{
optimizeForMobile = enable;
DetectPlatformAndOptimize();
}
}

代码示例

完整的阴影管理系统

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

public class AdvancedShadowSystem : MonoBehaviour
{
[System.Serializable]
public class ShadowQualitySettings
{
public string qualityName;
public int cascadeCount = 4;
public LightShadowResolution resolution = LightShadowResolution.Medium;
public float shadowDistance = 50.0f;
public float depthBias = 1.0f;
public float normalBias = 0.5f;
public LightShadows shadowType = LightShadows.Soft;
public int maxAdditionalLights = 4;
}

[Header("Shadow System Configuration")]
public List<ShadowQualitySettings> qualityPresets = new List<ShadowQualitySettings>();
public Light mainDirectionalLight;
public Light[] additionalLights;

[Header("Performance Monitoring")]
public bool enablePerformanceMonitoring = true;
public float performanceCheckInterval = 1.0f;
private float lastPerformanceCheck = 0f;

[Header("Current Settings")]
public int currentQualityLevel = 0;
private UniversalRenderPipelineAsset urpAsset;
private Dictionary<string, ShadowQualitySettings> qualityMap = new Dictionary<string, ShadowQualitySettings>();

void Start()
{
InitializeShadowSystem();
}

void Update()
{
if(enablePerformanceMonitoring &&
Time.time - lastPerformanceCheck >= performanceCheckInterval)
{
MonitorPerformance();
lastPerformanceCheck = Time.time;
}
}

void InitializeShadowSystem()
{
// 构建质量预设映射
foreach(var preset in qualityPresets)
{
if(!qualityMap.ContainsKey(preset.qualityName))
{
qualityMap[preset.qualityName] = preset;
}
}

// 获取URP资源
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

// 应用当前质量级别
ApplyQualitySettings(currentQualityLevel);
}

public void ApplyQualitySettings(int level)
{
if(level < 0 || level >= qualityPresets.Count)
{
Debug.LogError($"Invalid quality level: {level}");
return;
}

currentQualityLevel = level;
var settings = qualityPresets[level];

if(urpAsset != null)
{
// 配置级联设置
urpAsset.shadowCascadeOption =
settings.cascadeCount == 2 ? ShadowCascadeOption.TwoCascades :
ShadowCascadeOption.FourCascades;

// 配置阴影距离
urpAsset.shadowDistance = settings.shadowDistance;

// 配置阴影偏移
urpAsset.shadowDepthBias = settings.depthBias;
urpAsset.shadowNormalBias = settings.normalBias;

// 配置阴影分辨率
int resolutionValue = GetResolutionValue(settings.resolution);
urpAsset.shadowAtlasResolution = resolutionValue;

// 配置附加光源
urpAsset.maxAdditionalLightsCount = settings.maxAdditionalLights;
}

// 配置主光源
if(mainDirectionalLight != null)
{
mainDirectionalLight.shadows = settings.shadowType;
}

// 配置附加光源
ConfigureAdditionalLights(settings);

Debug.Log($"Applied shadow quality: {settings.qualityName}");
}

int GetResolutionValue(LightShadowResolution resolution)
{
switch(resolution)
{
case LightShadowResolution.Low:
return 256;
case LightShadowResolution.Medium:
return 512;
case LightShadowResolution.High:
return 1024;
case LightShadowResolution.VeryHigh:
return 2048;
default:
return 512;
}
}

void ConfigureAdditionalLights(ShadowQualitySettings settings)
{
foreach(var light in additionalLights)
{
if(light != null)
{
light.shadows = settings.shadowType;
light.shadowResolution = settings.resolution;
}
}
}

public void ApplyQualityByName(string qualityName)
{
if(qualityMap.ContainsKey(qualityName))
{
int index = qualityPresets.IndexOf(qualityMap[qualityName]);
if(index >= 0)
{
ApplyQualitySettings(index);
}
}
}

// 性能监控
void MonitorPerformance()
{
// 计算当前帧率
float currentFPS = 1.0f / Time.unscaledDeltaTime;

// 检查是否需要调整阴影质量
if(currentFPS < 30f && currentQualityLevel > 0)
{
// 性能不足,降低阴影质量
int newQuality = Mathf.Max(0, currentQualityLevel - 1);
ApplyQualitySettings(newQuality);
Debug.LogWarning($"Performance warning! Lowered shadow quality to maintain FPS. Current FPS: {currentFPS:F1}");
}
else if(currentFPS > 50f && currentQualityLevel < qualityPresets.Count - 1)
{
// 性能良好,提高阴影质量
int newQuality = Mathf.Min(qualityPresets.Count - 1, currentQualityLevel + 1);
ApplyQualitySettings(newQuality);
Debug.Log($"Performance good! Increased shadow quality. Current FPS: {currentFPS:F1}");
}
}

// 获取当前阴影系统信息
public string GetShadowSystemInfo()
{
if(urpAsset != null && qualityPresets.Count > currentQualityLevel)
{
var currentSettings = qualityPresets[currentQualityLevel];
float currentFPS = 1.0f / Time.unscaledDeltaTime;

return $"Shadow System Information:\n" +
$"Current Quality: {currentSettings.qualityName}\n" +
$"Cascade Count: {currentSettings.cascadeCount}\n" +
$"Shadow Distance: {currentSettings.shadowDistance:F1}\n" +
$"Shadow Resolution: {currentSettings.resolution}\n" +
$"Max Additional Lights: {currentSettings.maxAdditionalLights}\n" +
$"Current FPS: {currentFPS:F1}\n" +
$"Main Light Shadows: {(mainDirectionalLight != null && mainDirectionalLight.shadows != LightShadows.None ? "Enabled" : "Disabled")}";
}

return "Shadow system not properly initialized";
}

// 添加新的质量预设
public void AddQualityPreset(ShadowQualitySettings preset)
{
if(!qualityMap.ContainsKey(preset.qualityName))
{
qualityPresets.Add(preset);
qualityMap[preset.qualityName] = preset;
}
}

// 切换到下一个质量级别
public void NextQualityLevel()
{
int nextLevel = (currentQualityLevel + 1) % qualityPresets.Count;
ApplyQualitySettings(nextLevel);
}

// 切换到上一个质量级别
public void PreviousQualityLevel()
{
int prevLevel = (currentQualityLevel - 1 + qualityPresets.Count) % qualityPresets.Count;
ApplyQualitySettings(prevLevel);
}

// 自动选择最适合的阴影质量
public void AutoSelectQuality()
{
if(PerformanceCheck.IsLowEndDevice())
{
ApplyQualityByName("Low");
}
else if(PerformanceCheck.IsMidRangeDevice())
{
ApplyQualityByName("Medium");
}
else
{
ApplyQualityByName("High");
}
}
}

实践练习

练习1:创建阴影质量动态调整系统

目标:创建一个根据性能表现自动调整阴影质量的系统

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

public class DynamicShadowQualityController : MonoBehaviour
{
[System.Serializable]
public class PerformanceBasedSettings
{
public string levelName;
public float minFPS = 30f;
public float maxFPS = 60f;
public int cascadeCount = 2;
public LightShadowResolution resolution = LightShadowResolution.Medium;
public float shadowDistance = 30f;
public int maxAdditionalLights = 4;
}

[Header("Dynamic Quality Configuration")]
public PerformanceBasedSettings[] performanceLevels;
public float qualityAdjustmentDelay = 2.0f;
public float performanceCheckInterval = 0.5f;

[Header("Current State")]
public int currentLevelIndex = 0;
private float averageFPS = 0f;
private float[] fpsSamples = new float[10];
private int sampleIndex = 0;
private bool isAdjustingQuality = false;

private UniversalRenderPipelineAsset urpAsset;
private Coroutine qualityAdjustmentCoroutine;

void Start()
{
InitializeSystem();
}

void InitializeSystem()
{
urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;

// 初始化FPS采样数组
for(int i = 0; i < fpsSamples.Length; i++)
{
fpsSamples[i] = 60f; // 假设初始为60FPS
}

// 应用初始设置
ApplyCurrentSettings();
}

void Update()
{
UpdateFPSSampling();

if(!isAdjustingQuality)
{
CheckAndAdjustQuality();
}
}

void UpdateFPSSampling()
{
float currentFPS = 1.0f / Time.unscaledDeltaTime;
fpsSamples[sampleIndex] = currentFPS;
sampleIndex = (sampleIndex + 1) % fpsSamples.Length;

// 计算平均FPS
float sum = 0f;
for(int i = 0; i < fpsSamples.Length; i++)
{
sum += fpsSamples[i];
}
averageFPS = sum / fpsSamples.Length;
}

void CheckAndAdjustQuality()
{
if(performanceLevels.Length == 0) return;

// 寻找最适合的性能级别
int targetLevel = FindBestPerformanceLevel();

if(targetLevel != currentLevelIndex)
{
StartCoroutine(AdjustQualityWithDelay(targetLevel));
}
}

int FindBestPerformanceLevel()
{
// 从最高质量开始向下查找
for(int i = performanceLevels.Length - 1; i >= 0; i--)
{
var level = performanceLevels[i];
if(averageFPS >= level.minFPS)
{
return i;
}
}

// 如果所有级别都无法满足最低要求,返回最低质量
return 0;
}

IEnumerator AdjustQualityWithDelay(int targetLevel)
{
isAdjustingQuality = true;

// 等待一段时间以确保FPS稳定
yield return new WaitForSeconds(qualityAdjustmentDelay);

// 重新计算平均FPS
UpdateFPSSampling();

// 再次检查是否需要调整
int finalTargetLevel = FindBestPerformanceLevel();

if(finalTargetLevel != currentLevelIndex)
{
ApplyPerformanceLevel(finalTargetLevel);
Debug.Log($"Adjusted shadow quality from {performanceLevels[currentLevelIndex].levelName} " +
$"to {performanceLevels[finalTargetLevel].levelName}. Avg FPS: {averageFPS:F1}");
}

isAdjustingQuality = false;
}

void ApplyPerformanceLevel(int levelIndex)
{
if(levelIndex < 0 || levelIndex >= performanceLevels.Length) return;

var level = performanceLevels[levelIndex];
currentLevelIndex = levelIndex;

if(urpAsset != null)
{
// 应用级联设置
urpAsset.shadowCascadeOption =
level.cascadeCount == 2 ? ShadowCascadeOption.TwoCascades :
ShadowCascadeOption.FourCascades;

// 应用阴影距离
urpAsset.shadowDistance = level.shadowDistance;

// 应用附加光源限制
urpAsset.maxAdditionalLightsCount = level.maxAdditionalLights;

// 应用阴影分辨率
int resolutionValue = GetResolutionValue(level.resolution);
urpAsset.shadowAtlasResolution = resolutionValue;
}
}

int GetResolutionValue(LightShadowResolution resolution)
{
switch(resolution)
{
case LightShadowResolution.Low: return 256;
case LightShadowResolution.Medium: return 512;
case LightShadowResolution.High: return 1024;
case LightShadowResolution.VeryHigh: return 2048;
default: return 512;
}
}

void ApplyCurrentSettings()
{
if(currentLevelIndex >= 0 && currentLevelIndex < performanceLevels.Length)
{
ApplyPerformanceLevel(currentLevelIndex);
}
}

// 手动设置性能级别
public void SetPerformanceLevel(int levelIndex)
{
if(levelIndex >= 0 && levelIndex < performanceLevels.Length)
{
StopAdjustmentCoroutine();
ApplyPerformanceLevel(levelIndex);
}
}

void StopAdjustmentCoroutine()
{
if(qualityAdjustmentCoroutine != null)
{
StopCoroutine(qualityAdjustmentCoroutine);
}
}

// 获取系统信息
public string GetSystemInfo()
{
if(currentLevelIndex >= 0 && currentLevelIndex < performanceLevels.Length)
{
var currentLevel = performanceLevels[currentLevelIndex];
return $"Dynamic Shadow Quality System:\n" +
$"Current Level: {currentLevel.levelName}\n" +
$"Average FPS: {averageFPS:F1}\n" +
$"Target FPS Range: {currentLevel.minFPS:F0} - {currentLevel.maxFPS:F0}\n" +
$"Cascade Count: {currentLevel.cascadeCount}\n" +
$"Shadow Distance: {currentLevel.shadowDistance:F1}\n" +
$"Resolution: {currentLevel.resolution}\n" +
$"Adjusting Quality: {isAdjustingQuality}";
}

return "System not initialized properly";
}

// 强制重新评估性能
public void ForcePerformanceReevaluation()
{
StopAdjustmentCoroutine();
CheckAndAdjustQuality();
}

// 添加性能级别
public void AddPerformanceLevel(PerformanceBasedSettings level)
{
System.Array.Resize(ref performanceLevels, performanceLevels.Length + 1);
performanceLevels[performanceLevels.Length - 1] = level;
}
}

练习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
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
using UnityEngine;
using UnityEngine.Rendering.Universal;
using System.Collections.Generic;

public class ShadowDiagnosticTool : MonoBehaviour
{
[Header("Diagnostic Settings")]
public bool enableRealTimeDiagnostics = true;
public float diagnosticInterval = 2.0f;
private float lastDiagnosticTime = 0f;

[Header("Shadow Problem Detection")]
public float acneDetectionThreshold = 0.1f;
public float panningDetectionThreshold = 0.5f;
public float resolutionDetectionThreshold = 0.05f;

[Header("Test Objects")]
public GameObject[] testObjects;
public Light testLight;
public Camera diagnosticCamera;

[Header("Results")]
public List<string> detectedIssues = new List<string>();
public bool hasShadowProblems = false;

private UniversalRenderPipelineAsset urpAsset;
private Dictionary<string, ShadowProblemData> problemData = new Dictionary<string, ShadowProblemData>();

[System.Serializable]
public class ShadowProblemData
{
public string problemType;
public string description;
public float severity;
public float lastDetected;
public bool isFixed;
public List<string> suggestedSolutions = new List<string>();
}

void Update()
{
if(enableRealTimeDiagnostics &&
Time.time - lastDiagnosticTime >= diagnosticInterval)
{
RunShadowDiagnostics();
lastDiagnosticTime = Time.time;
}
}

public void RunShadowDiagnostics()
{
detectedIssues.Clear();
problemData.Clear();
hasShadowProblems = false;

// 检查阴影痤疮
CheckForShadowAcne();

// 检查彼得潘效应
CheckForPeterPanning();

// 检查阴影分辨率问题
CheckShadowResolutionIssues();

// 检查阴影距离设置
CheckShadowDistanceIssues();

// 检查性能问题
CheckShadowPerformanceIssues();

if(detectedIssues.Count > 0)
{
hasShadowProblems = true;
Debug.LogWarning($"Shadow Diagnostic: Found {detectedIssues.Count} issues");
foreach(var issue in detectedIssues)
{
Debug.LogWarning($" - {issue}");
}
}
else
{
Debug.Log("Shadow Diagnostic: No issues detected");
}
}

void CheckForShadowAcne()
{
// 模拟阴影痤疮检测
// 在实际应用中,这需要分析渲染纹理或深度缓冲区
if(urpAsset != null)
{
// 如果偏移设置过小,可能导致阴影痤疮
if(urpAsset.shadowDepthBias < 0.5f)
{
var problem = new ShadowProblemData
{
problemType = "Shadow Acne",
description = "Shadow acne detected due to low depth bias",
severity = 0.7f,
lastDetected = Time.time,
suggestedSolutions = {
"Increase shadow depth bias",
"Adjust normal bias",
"Check material properties"
}
};

problemData["ShadowAcne"] = problem;
detectedIssues.Add("Shadow Acne: Depth bias too low");
}
}
}

void CheckForPeterPanning()
{
// 模拟彼得潘效应检测
if(urpAsset != null)
{
// 如果偏移设置过大,可能导致彼得潘效应
if(urpAsset.shadowDepthBias > 2.0f)
{
var problem = new ShadowProblemData
{
problemType = "Peter Panning",
description = "Peter Panning effect detected due to high depth bias",
severity = 0.6f,
lastDetected = Time.time,
suggestedSolutions = {
"Decrease shadow depth bias",
"Adjust normal bias",
"Check light distance"
}
};

problemData["PeterPanning"] = problem;
detectedIssues.Add("Peter Panning: Depth bias too high");
}
}
}

void CheckShadowResolutionIssues()
{
if(urpAsset != null)
{
// 检查阴影分辨率是否过低
if(urpAsset.shadowAtlasResolution < 512)
{
var problem = new ShadowProblemData
{
problemType = "Low Resolution",
description = "Shadow resolution too low causing pixelated shadows",
severity = 0.4f,
lastDetected = Time.time,
suggestedSolutions = {
"Increase shadow atlas resolution",
"Balance quality with performance",
"Consider target platform capabilities"
}
};

problemData["LowResolution"] = problem;
detectedIssues.Add("Low Resolution: Shadow atlas resolution too low");
}
}
}

void CheckShadowDistanceIssues()
{
if(urpAsset != null && Camera.main != null)
{
// 检查阴影距离是否设置合理
if(urpAsset.shadowDistance > Camera.main.farClipPlane * 1.5f)
{
var problem = new ShadowProblemData
{
problemType = "Shadow Distance",
description = "Shadow distance set too high relative to camera range",
severity = 0.3f,
lastDetected = Time.time,
suggestedSolutions = {
"Reduce shadow distance",
"Match to camera far clip plane",
"Consider performance impact"
}
};

problemData["ShadowDistance"] = problem;
detectedIssues.Add("Shadow Distance: Set too high");
}
}
}

void CheckShadowPerformanceIssues()
{
// 检查可能的性能问题
if(urpAsset != null)
{
// 检查级联数量
int cascadeCount = urpAsset.shadowCascadeOption == ShadowCascadeOption.FourCascades ? 4 : 2;
if(cascadeCount == 4 && SystemInfo.graphicsMemorySize < 2000)
{
var problem = new ShadowProblemData
{
problemType = "Performance",
description = "4-cascade shadows on low-end hardware",
severity = 0.5f,
lastDetected = Time.time,
suggestedSolutions = {
"Reduce to 2-cascade shadows",
"Lower shadow resolution",
"Optimize for target hardware"
}
};

problemData["Performance"] = problem;
detectedIssues.Add("Performance: 4-cascade shadows on low-end hardware");
}
}
}

// 获取诊断结果
public string GetDiagnosticReport()
{
var report = new System.Text.StringBuilder();

report.AppendLine("=== Shadow Diagnostic Report ===");
report.AppendLine($"Total Issues Found: {detectedIssues.Count}");
report.AppendLine($"Last Run: {System.DateTime.Now:HH:mm:ss}");
report.AppendLine();

if(detectedIssues.Count > 0)
{
report.AppendLine("Detected Issues:");
foreach(var issue in detectedIssues)
{
report.AppendLine($" • {issue}");
}
report.AppendLine();
}

if(problemData.Count > 0)
{
report.AppendLine("Detailed Problem Analysis:");
foreach(var kvp in problemData)
{
var problem = kvp.Value;
report.AppendLine($"Problem: {problem.problemType}");
report.AppendLine($" Description: {problem.description}");
report.AppendLine($" Severity: {problem.severity * 100:F0}%");
report.AppendLine($" Solutions:");
foreach(var solution in problem.suggestedSolutions)
{
report.AppendLine($" - {solution}");
}
report.AppendLine();
}
}
else
{
report.AppendLine("No shadow problems detected. System appears to be configured correctly.");
}

return report.ToString();
}

// 应用推荐的修复方案
public void ApplyRecommendedFixes()
{
int fixesApplied = 0;

foreach(var kvp in problemData)
{
var problem = kvp.Value;

if(!problem.isFixed)
{
switch(problem.problemType)
{
case "Shadow Acne":
if(urpAsset != null)
{
urpAsset.shadowDepthBias = Mathf.Clamp(urpAsset.shadowDepthBias + 0.2f, 0.5f, 2.0f);
problem.isFixed = true;
fixesApplied++;
}
break;

case "Peter Panning":
if(urpAsset != null)
{
urpAsset.shadowDepthBias = Mathf.Clamp(urpAsset.shadowDepthBias - 0.2f, 0.1f, 1.5f);
problem.isFixed = true;
fixesApplied++;
}
break;

case "Low Resolution":
if(urpAsset != null)
{
urpAsset.shadowAtlasResolution = Mathf.Min(urpAsset.shadowAtlasResolution * 2, 2048);
problem.isFixed = true;
fixesApplied++;
}
break;
}
}
}

if(fixesApplied > 0)
{
Debug.Log($"Applied {fixesApplied} recommended fixes");
}
else
{
Debug.Log("No fixes were applied");
}
}

// 重置诊断
public void ResetDiagnostics()
{
detectedIssues.Clear();
problemData.Clear();
hasShadowProblems = false;
}

// 手动运行诊断
public void ManualRunDiagnostics()
{
RunShadowDiagnostics();
}

// 获取性能建议
public string GetPerformanceRecommendations()
{
var recommendations = new List<string>();

if(SystemInfo.graphicsMemorySize < 2000)
{
recommendations.Add("• Use lower shadow resolution (256-512)");
recommendations.Add("• Limit to 2-cascade shadows");
recommendations.Add("• Reduce shadow distance");
}

if(SystemInfo.systemMemorySize < 4000)
{
recommendations.Add("• Reduce max additional lights count");
recommendations.Add("• Use simpler shadow receiver materials");
}

if(recommendations.Count == 0)
{
recommendations.Add("• Current settings appear appropriate for hardware");
}

return "Performance Recommendations:\n" + string.Join("\n", recommendations);
}
}

总结

本章详细介绍了URP阴影系统的各个方面,包括级联阴影贴图的工作原理、阴影距离与分辨率配置、Shadow Bias参数调优、软阴影实现原理、附加光源阴影处理,以及移动平台阴影优化策略。

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

  1. 级联阴影贴图:理解了CSM技术如何在不同距离上提供高质量阴影,以及如何配置级联参数以平衡性能和视觉质量。

  2. 阴影配置:掌握了阴影距离、分辨率等关键参数的设置方法,以及如何根据场景需求进行优化。

  3. Shadow Bias调优:学会了如何正确设置深度偏移和法线偏移,以避免阴影痤疮和彼得潘效应等常见问题。

  4. 软阴影技术:了解了软阴影的实现原理和配置方法,以及如何在质量和性能之间找到平衡。

  5. 附加光源阴影:掌握了如何配置和优化附加光源的阴影效果。

  6. 移动平台优化:学习了针对移动设备的特殊阴影优化策略。

  7. 实践应用:通过创建动态阴影质量调整系统和阴影问题诊断工具,将理论知识转化为实际可用的解决方案。

阴影系统是渲染管线中的重要组成部分,正确配置和优化阴影系统对于创建高质量的视觉效果和保持良好性能至关重要。在下一章中,我们将深入探讨URP后处理系统,了解各种后处理效果的实现原理和使用方法。