第15章 光照与阴影源码

第15章 光照与阴影源码

理论讲解

15.1 ForwardLights.cs光照计算

ForwardLights是URP中处理前向渲染光照计算的核心类,它负责计算主光源和附加光源对物体的光照影响。

ForwardLights的主要职责:

  1. 主光源计算:计算场景中最亮的光源(通常是太阳光)
  2. 附加光源计算:计算其他光源的光照效果
  3. 光照数据打包:将光照数据打包成GPU友好的格式
  4. 光照剔除:执行光照剔除以优化性能

光照计算流程:

  1. 光源排序:根据亮度对光源进行排序
  2. 光照剔除:确定哪些光源影响当前渲染的物体
  3. 数据打包:将光照数据打包到常量缓冲区
  4. GPU传递:将光照数据传递给着色器

光照数据结构:

PerObjectLightData:每个物体的光照数据

  • 主光源索引
  • 附加光源索引列表
  • 光源数量统计

LightData:光源数据结构

  • 光源位置/方向
  • 光源颜色
  • 光源范围
  • 光源类型

15.2 MainLightShadowCasterPass实现

MainLightShadowCasterPass是负责渲染主光源阴影贴图的渲染通道。

主要功能:

  1. 阴影贴图生成:为最亮的光源生成阴影贴图
  2. 阴影投射物体识别:确定哪些物体会投射阴影
  3. 阴影参数设置:配置阴影偏移、软阴影参数等
  4. 阴影渲染执行:执行阴影渲染命令

阴影渲染流程:

  1. 视锥体计算:计算阴影贴图的视锥体
  2. 物体筛选:筛选需要投射阴影的物体
  3. 渲染执行:使用阴影投射材质渲染物体
  4. 贴图生成:生成最终的阴影贴图

15.3 AdditionalLightsShadowCasterPass

AdditionalLightsShadowCasterPass负责渲染附加光源的阴影贴图。

附加光源阴影特点:

  1. 多光源支持:支持多个附加光源的阴影
  2. 性能优化:对附加光源阴影进行优化
  3. 资源管理:有效管理多个阴影贴图
  4. 剔除优化:优化附加光源的阴影剔除

附加光源阴影计算:

  1. 光源分组:将附加光源分组处理
  2. 阴影贴图分配:为每个光源分配阴影贴图
  3. 并行渲染:可能并行渲染多个阴影贴图
  4. 数据整合:整合多个光源的阴影数据

15.4 ShadowUtils工具类解析

ShadowUtils是URP中处理阴影相关计算的工具类。

主要功能:

  1. 阴影矩阵计算:计算阴影变换矩阵
  2. 阴影采样函数:提供阴影采样相关函数
  3. 软阴影计算:实现软阴影算法
  4. 阴影参数处理:处理阴影偏移、强度等参数

阴影采样技术:

PCF(Percentage Closer Filtering):基本的软阴影技术

  • 通过多个样本点计算阴影
  • 产生平滑的阴影边缘

PCSS(Percentage Closer Soft Shadows):更高级的软阴影技术

  • 根据遮挡物距离计算阴影半影
  • 产生更真实的软阴影效果

15.5 光照数据打包与传递

URP通过高效的数据打包机制将光照信息传递给GPU。

数据打包策略:

  1. 结构体优化:使用GPU友好的数据结构
  2. 内存对齐:确保数据在GPU内存中正确对齐
  3. 批量处理:批量处理多个光源数据
  4. 缓存优化:优化GPU缓存使用

光照数据传递流程:

  1. CPU端计算:在CPU端计算光照参数
  2. 数据序列化:将光照数据序列化
  3. GPU缓冲区更新:更新GPU常量缓冲区
  4. 着色器访问:着色器访问光照数据

15.6 Shader中的光照接收

在URP的着色器中,光照数据通过特定的函数和变量接收和处理。

光照函数:

Lightmapping:处理光照贴图相关计算
LightTransport:处理光传输计算
BRDF:处理双向反射分布函数计算

光照变量:

_MainLight:主光源数据
_AdditionalLights:附加光源数组
_ShadowMap:阴影贴图
_LightIndicesMap:光源索引图

代码示例

15.7 光照数据处理器

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

public class LightDataProcessor : MonoBehaviour
{
[Header("Light Analysis Settings")]
public bool enableLightAnalysis = true;
public bool logLightData = true;
public bool visualizeLightBounds = true;

[Header("Performance Thresholds")]
public int maxAdditionalLights = 4;
public float maxLightRange = 100f;
public float warningLightIntensity = 2f;

private List<Light> m_SceneLights = new List<Light>();
private Dictionary<Light, LightAnalysisData> m_LightAnalysisData = new Dictionary<Light, LightAnalysisData>();

[System.Serializable]
public class LightAnalysisData
{
public Light light;
public int index;
public LightType type;
public Vector3 position;
public Vector3 direction;
public float range;
public float intensity;
public Color color;
public bool castsShadows;
public LightShadows shadowType;
public float innerSpotAngle;
public float outerSpotAngle;
public bool enabled;
public float distanceToCamera;
public bool affectsCurrentRender;
public Bounds bounds;
public float luminance;
}

[System.Serializable]
public class LightSummary
{
public int totalLights;
public int directionalLights;
public int pointLights;
public int spotLights;
public int areaLights;
public int lightsWithShadows;
public float averageIntensity;
public float maxIntensity;
public int additionalLightsCount;
public bool supportsMainLight;
}

public LightSummary summary = new LightSummary();

void Start()
{
if (enableLightAnalysis)
{
UpdateLightData();
}
}

void Update()
{
if (enableLightAnalysis)
{
UpdateLightData();
}
}

private void UpdateLightData()
{
m_SceneLights.Clear();
m_LightAnalysisData.Clear();

// 获取场景中的所有光源
var lights = FindObjectsOfType<Light>();
m_SceneLights.AddRange(lights);

// 分析每个光源
for (int i = 0; i < lights.Length; i++)
{
var light = lights[i];
var analysisData = AnalyzeLight(light, i);
m_LightAnalysisData[light] = analysisData;
}

GenerateSummary();

if (logLightData)
{
LogLightAnalysis();
}
}

private LightAnalysisData AnalyzeLight(Light light, int index)
{
var analysisData = new LightAnalysisData
{
light = light,
index = index,
type = light.type,
position = light.transform.position,
direction = light.transform.forward,
range = light.range,
intensity = light.intensity,
color = light.color,
castsShadows = light.shadows != LightShadows.None,
shadowType = light.shadows,
innerSpotAngle = light.innerSpotAngle,
outerSpotAngle = light.spotAngle,
enabled = light.enabled,
distanceToCamera = GetDistanceToMainCamera(light),
affectsCurrentRender = true, // 简化处理,实际需要更复杂的判断
luminance = CalculateLuminance(light.color, light.intensity)
};

// 计算光源影响范围
switch (light.type)
{
case LightType.Directional:
analysisData.bounds = new Bounds(light.transform.position, Vector3.one * maxLightRange);
break;
case LightType.Point:
analysisData.bounds = new Bounds(light.transform.position, Vector3.one * light.range * 2f);
break;
case LightType.Spot:
// 计算圆锥体的大致包围盒
float maxRadius = light.range * Mathf.Tan(light.spotAngle * Mathf.Deg2Rad / 2f);
analysisData.bounds = new Bounds(
light.transform.position + light.transform.forward * light.range / 2f,
new Vector3(maxRadius * 2f, maxRadius * 2f, light.range)
);
break;
default:
analysisData.bounds = new Bounds(light.transform.position, Vector3.one * light.range);
break;
}

return analysisData;
}

private float GetDistanceToMainCamera(Light light)
{
var mainCamera = Camera.main;
if (mainCamera != null)
{
return Vector3.Distance(light.transform.position, mainCamera.transform.position);
}
return 0f;
}

private float CalculateLuminance(Color color, float intensity)
{
// 简化的亮度计算
return (0.2126f * color.r + 0.7152f * color.g + 0.0722f * color.b) * intensity;
}

private void GenerateSummary()
{
summary.totalLights = m_SceneLights.Count;
summary.directionalLights = 0;
summary.pointLights = 0;
summary.spotLights = 0;
summary.areaLights = 0;
summary.lightsWithShadows = 0;
summary.averageIntensity = 0f;
summary.maxIntensity = 0f;

foreach (var light in m_SceneLights)
{
switch (light.type)
{
case LightType.Directional:
summary.directionalLights++;
break;
case LightType.Point:
summary.pointLights++;
break;
case LightType.Spot:
summary.spotLights++;
break;
case LightType.Rectangle:
case LightType.Disc:
summary.areaLights++;
break;
}

if (light.shadows != LightShadows.None)
{
summary.lightsWithShadows++;
}

summary.averageIntensity += light.intensity;
if (light.intensity > summary.maxIntensity)
{
summary.maxIntensity = light.intensity;
}
}

if (summary.totalLights > 0)
{
summary.averageIntensity /= summary.totalLights;
}

// 确定主光源(最亮的平行光或点光源)
summary.supportsMainLight = summary.directionalLights > 0 || summary.pointLights > 0;
summary.additionalLightsCount = Mathf.Max(0, summary.totalLights - 1); // 假设一个主光源
}

private void LogLightAnalysis()
{
var log = new System.Text.StringBuilder();
log.AppendLine("=== Light Data Analysis ===");
log.AppendLine($"Total Lights: {summary.totalLights}");
log.AppendLine($"Directional Lights: {summary.directionalLights}");
log.AppendLine($"Point Lights: {summary.pointLights}");
log.AppendLine($"Spot Lights: {summary.spotLights}");
log.AppendLine($"Area Lights: {summary.areaLights}");
log.AppendLine($"Lights with Shadows: {summary.lightsWithShadows}");
log.AppendLine($"Average Intensity: {summary.averageIntensity:F2}");
log.AppendLine($"Max Intensity: {summary.maxIntensity:F2}");
log.AppendLine($"Additional Lights Count: {summary.additionalLightsCount}");
log.AppendLine($"Supports Main Light: {summary.supportsMainLight}");

log.AppendLine("\nDetailed Light Information:");
foreach (var kvp in m_LightAnalysisData)
{
var data = kvp.Value;
log.AppendLine($" [{data.index}] {data.light.name}: {data.type} " +
$"(Intensity: {data.intensity:F2}, Range: {data.range:F1}, " +
$"Shadows: {data.castsShadows}, Distance: {data.distanceToCamera:F1})");
}

Debug.Log(log.ToString());
}

// 获取特定光源的分析数据
public LightAnalysisData GetLightAnalysisData(Light light)
{
if (m_LightAnalysisData.ContainsKey(light))
{
return m_LightAnalysisData[light];
}
return null;
}

// 获取光源列表
public List<Light> GetSceneLights()
{
return new List<Light>(m_SceneLights);
}

// 获取光源分析数据列表
public List<LightAnalysisData> GetLightAnalysisDataList()
{
return new List<LightAnalysisData>(m_LightAnalysisData.Values);
}

// 获取光源摘要
public LightSummary GetLightSummary()
{
return summary;
}

// 检查光源配置是否合理
public bool ValidateLightConfiguration()
{
bool isValid = true;
var issues = new List<string>();

if (summary.additionalLightsCount > maxAdditionalLights)
{
issues.Add($"Too many additional lights: {summary.additionalLightsCount} (max: {maxAdditionalLights})");
isValid = false;
}

if (summary.maxIntensity > warningLightIntensity)
{
issues.Add($"High light intensity detected: {summary.maxIntensity:F2} (warning: {warningLightIntensity})");
}

foreach (var kvp in m_LightAnalysisData)
{
var data = kvp.Value;
if (data.range > maxLightRange)
{
issues.Add($"Light '{data.light.name}' has large range: {data.range:F1} (max: {maxLightRange})");
}
}

if (issues.Count > 0 && logLightData)
{
foreach (var issue in issues)
{
Debug.LogWarning($"[Light Validation] {issue}");
}
}

return isValid;
}

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

if (summary.additionalLightsCount > 4)
{
suggestions.Add("Consider reducing additional lights for better performance");
}

if (summary.lightsWithShadows > 4)
{
suggestions.Add("Consider using fewer shadow-casting lights or baked lighting");
}

if (summary.maxIntensity > 2.0f)
{
suggestions.Add("Reduce light intensities to improve performance and visual quality");
}

return suggestions;
}

// 绘制光源影响范围(用于调试)
void OnDrawGizmos()
{
if (!visualizeLightBounds || !enableLightAnalysis) return;

foreach (var kvp in m_LightAnalysisData)
{
var data = kvp.Value;
if (!data.enabled) continue;

Gizmos.color = data.color;
Gizmos.matrix = Matrix4x4.TRS(data.position, Quaternion.LookRotation(data.direction), Vector3.one);

switch (data.type)
{
case LightType.Directional:
// 平行光用箭头表示方向
Gizmos.DrawRay(Vector3.zero, Vector3.forward * 5f);
break;
case LightType.Point:
Gizmos.DrawWireSphere(Vector3.zero, data.range);
break;
case LightType.Spot:
// 绘制圆锥体
float angle = data.outerSpotAngle * Mathf.Deg2Rad;
float radius = data.range * Mathf.Tan(angle / 2f);
Gizmos.DrawFrustum(Vector3.zero, angle, data.range, 0, radius / data.range);
break;
}
}
}
}

15.8 阴影数据处理器

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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class ShadowDataProcessor : MonoBehaviour
{
[Header("Shadow Analysis Settings")]
public bool enableShadowAnalysis = true;
public bool logShadowData = true;
public bool visualizeShadowMaps = true;

[Header("Shadow Configuration")]
public float shadowDistance = 50f;
public int shadowResolution = 1024;
public float shadowBias = 0.05f;
public float shadowNormalBias = 0.4f;
public float shadowNearPlane = 0.2f;

[Header("Performance Thresholds")]
public int maxShadowCastingRenderers = 50;
public int maxShadowLights = 4;
public float maxShadowDistance = 100f;

private List<Light> m_ShadowLights = new List<Light>();
private List<Renderer> m_ShadowCastingRenderers = new List<Renderer>();
private Dictionary<Light, ShadowLightData> m_ShadowLightData = new Dictionary<Light, ShadowLightData>();
private Dictionary<Renderer, ShadowRendererData> m_ShadowRendererData = new Dictionary<Renderer, ShadowRendererData>();

[System.Serializable]
public class ShadowLightData
{
public Light light;
public int index;
public bool supportsShadows;
public LightShadows shadowType;
public float shadowStrength;
public float shadowResolution;
public float shadowDistance;
public float shadowBias;
public float shadowNormalBias;
public float shadowNearPlane;
public int shadowCascadeCount;
public float shadowCascadeSplit;
public Vector3 shadowDirection;
public Matrix4x4 shadowMatrix;
public RenderTexture shadowMap;
public bool enabled;
}

[System.Serializable]
public class ShadowRendererData
{
public Renderer renderer;
public bool castsShadows;
public ShadowCastingMode shadowMode;
public bool receivesShadows;
public Vector3 boundsCenter;
public Vector3 boundsSize;
public Material[] materials;
public int subMeshCount;
public bool enabled;
public float distanceToMainLight;
public bool isVisibleToShadowMap;
}

[System.Serializable]
public class ShadowSummary
{
public int totalShadowLights;
public int directionalShadowLights;
public int pointShadowLights;
public int spotShadowLights;
public int totalShadowCasters;
public int shadowCastersWithReceive;
public float averageShadowDistance;
public int maxShadowResolution;
public bool supportsMainLightShadows;
public bool supportsAdditionalLightShadows;
public int cascadedShadowMapsCount;
}

public ShadowSummary summary = new ShadowSummary();

void Start()
{
if (enableShadowAnalysis)
{
UpdateShadowData();
}
}

void Update()
{
if (enableShadowAnalysis)
{
UpdateShadowData();
}
}

private void UpdateShadowData()
{
m_ShadowLights.Clear();
m_ShadowCastingRenderers.Clear();
m_ShadowLightData.Clear();
m_ShadowRendererData.Clear();

// 获取阴影光源
var allLights = FindObjectsOfType<Light>();
foreach (var light in allLights)
{
if (light.shadows != LightShadows.None)
{
m_ShadowLights.Add(light);
var lightData = AnalyzeShadowLight(light, m_ShadowLights.Count - 1);
m_ShadowLightData[light] = lightData;
}
}

// 获取阴影投射渲染器
var allRenderers = FindObjectsOfType<Renderer>();
foreach (var renderer in allRenderers)
{
if (renderer.shadowCastingMode != ShadowCastingMode.Off)
{
m_ShadowCastingRenderers.Add(renderer);
var rendererData = AnalyzeShadowRenderer(renderer);
m_ShadowRendererData[renderer] = rendererData;
}
}

GenerateSummary();

if (logShadowData)
{
LogShadowAnalysis();
}
}

private ShadowLightData AnalyzeShadowLight(Light light, int index)
{
var lightData = new ShadowLightData
{
light = light,
index = index,
supportsShadows = light.shadows != LightShadows.None,
shadowType = light.shadows,
shadowStrength = light.shadowStrength,
shadowResolution = light.shadowResolution == LightShadowResolution.Low ? 512 :
light.shadowResolution == LightShadowResolution.Medium ? 1024 :
light.shadowResolution == LightShadowResolution.High ? 2048 : 4096,
shadowDistance = light.shadowDistance,
shadowBias = light.shadowBias,
shadowNormalBias = light.shadowNormalBias,
shadowNearPlane = light.shadowNearPlane,
shadowCascadeCount = light.type == LightType.Directional ? 4 : 1, // 简化处理
shadowCascadeSplit = 0.1f, // 简化处理
shadowDirection = light.transform.forward,
enabled = light.enabled
};

return lightData;
}

private ShadowRendererData AnalyzeShadowRenderer(Renderer renderer)
{
var rendererData = new ShadowRendererData
{
renderer = renderer,
castsShadows = renderer.shadowCastingMode != ShadowCastingMode.Off,
shadowMode = renderer.shadowCastingMode,
receivesShadows = renderer.receiveShadows,
boundsCenter = renderer.bounds.center,
boundsSize = renderer.bounds.size,
materials = renderer.sharedMaterials,
subMeshCount = GetSubMeshCount(renderer),
enabled = renderer.enabled,
distanceToMainLight = GetDistanceToMainLight(renderer),
isVisibleToShadowMap = true // 简化处理
};

return rendererData;
}

private int GetSubMeshCount(Renderer renderer)
{
var meshFilter = renderer.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.sharedMesh != null)
{
return meshFilter.sharedMesh.subMeshCount;
}
return 1;
}

private float GetDistanceToMainLight(Renderer renderer)
{
// 简化:计算到主光源的距离
var mainLight = GetMainLight();
if (mainLight != null)
{
return Vector3.Distance(renderer.bounds.center, mainLight.transform.position);
}
return float.MaxValue;
}

private Light GetMainLight()
{
// 简化:返回场景中第一个平行光或最亮的光
var lights = FindObjectsOfType<Light>();
foreach (var light in lights)
{
if (light.type == LightType.Directional && light.enabled)
{
return light;
}
}

// 如果没有平行光,返回最亮的光
Light brightest = null;
float maxIntensity = 0f;
foreach (var light in lights)
{
if (light.intensity > maxIntensity)
{
maxIntensity = light.intensity;
brightest = light;
}
}

return brightest;
}

private void GenerateSummary()
{
summary.totalShadowLights = m_ShadowLights.Count;
summary.directionalShadowLights = 0;
summary.pointShadowLights = 0;
summary.spotShadowLights = 0;
summary.totalShadowCasters = m_ShadowCastingRenderers.Count;
summary.shadowCastersWithReceive = 0;
summary.averageShadowDistance = 0f;
summary.maxShadowResolution = 0;
summary.cascadedShadowMapsCount = 0;

foreach (var light in m_ShadowLights)
{
switch (light.type)
{
case LightType.Directional:
summary.directionalShadowLights++;
summary.cascadedShadowMapsCount++;
break;
case LightType.Point:
summary.pointShadowLights++;
break;
case LightType.Spot:
summary.spotShadowLights++;
break;
}

summary.averageShadowDistance += light.shadowDistance;
if (light.shadowResolution > (LightShadowResolution)summary.maxShadowResolution)
{
summary.maxShadowResolution = (int)light.shadowResolution;
}
}

if (summary.totalShadowLights > 0)
{
summary.averageShadowDistance /= summary.totalShadowLights;
}

foreach (var renderer in m_ShadowCastingRenderers)
{
if (renderer.receiveShadows)
{
summary.shadowCastersWithReceive++;
}
}

summary.supportsMainLightShadows = summary.directionalShadowLights > 0;
summary.supportsAdditionalLightShadows = summary.totalShadowLights > 1;
}

private void LogShadowAnalysis()
{
var log = new System.Text.StringBuilder();
log.AppendLine("=== Shadow Data Analysis ===");
log.AppendLine($"Total Shadow Lights: {summary.totalShadowLights}");
log.AppendLine($"Directional Shadow Lights: {summary.directionalShadowLights}");
log.AppendLine($"Point Shadow Lights: {summary.pointShadowLights}");
log.AppendLine($"Spot Shadow Lights: {summary.spotShadowLights}");
log.AppendLine($"Total Shadow Casters: {summary.totalShadowCasters}");
log.AppendLine($"Shadow Casters with Receive: {summary.shadowCastersWithReceive}");
log.AppendLine($"Average Shadow Distance: {summary.averageShadowDistance:F2}");
log.AppendLine($"Max Shadow Resolution: {summary.maxShadowResolution}");
log.AppendLine($"Cascaded Shadow Maps: {summary.cascadedShadowMapsCount}");
log.AppendLine($"Supports Main Light Shadows: {summary.supportsMainLightShadows}");
log.AppendLine($"Supports Additional Light Shadows: {summary.supportsAdditionalLightShadows}");

log.AppendLine("\nShadow Light Details:");
foreach (var kvp in m_ShadowLightData)
{
var data = kvp.Value;
log.AppendLine($" [{data.index}] {data.light.name}: {data.shadowType} " +
$"(Resolution: {(int)data.shadowResolution}, Distance: {data.shadowDistance:F1}, " +
$"Bias: {data.shadowBias:F3}, Normal Bias: {data.shadowNormalBias:F3})");
}

log.AppendLine("\nShadow Renderer Details:");
foreach (var kvp in m_ShadowRendererData)
{
var data = kvp.Value;
log.AppendLine($" {data.renderer.name}: Casts={data.castsShadows}, Mode={data.shadowMode}, " +
$"Receives={data.receivesShadows}, Distance={data.distanceToMainLight:F1}");
}

Debug.Log(log.ToString());
}

// 获取阴影光源列表
public List<Light> GetShadowLights()
{
return new List<Light>(m_ShadowLights);
}

// 获取阴影投射渲染器列表
public List<Renderer> GetShadowCastingRenderers()
{
return new List<Renderer>(m_ShadowCastingRenderers);
}

// 获取阴影光源数据
public ShadowLightData GetShadowLightData(Light light)
{
if (m_ShadowLightData.ContainsKey(light))
{
return m_ShadowLightData[light];
}
return null;
}

// 获取阴影渲染器数据
public ShadowRendererData GetShadowRendererData(Renderer renderer)
{
if (m_ShadowRendererData.ContainsKey(renderer))
{
return m_ShadowRendererData[renderer];
}
return null;
}

// 获取阴影摘要
public ShadowSummary GetShadowSummary()
{
return summary;
}

// 验证阴影配置
public bool ValidateShadowConfiguration()
{
bool isValid = true;
var issues = new List<string>();

if (summary.totalShadowCasters > maxShadowCastingRenderers)
{
issues.Add($"Too many shadow casting renderers: {summary.totalShadowCasters} (max: {maxShadowCastingRenderers})");
isValid = false;
}

if (summary.totalShadowLights > maxShadowLights)
{
issues.Add($"Too many shadow lights: {summary.totalShadowLights} (max: {maxShadowLights})");
isValid = false;
}

if (summary.averageShadowDistance > maxShadowDistance)
{
issues.Add($"Large average shadow distance: {summary.averageShadowDistance:F1} (max: {maxShadowDistance})");
}

if (issues.Count > 0 && logShadowData)
{
foreach (var issue in issues)
{
Debug.LogWarning($"[Shadow Validation] {issue}");
}
}

return isValid;
}

// 获取阴影性能建议
public List<string> GetShadowPerformanceSuggestions()
{
var suggestions = new List<string>();

if (summary.totalShadowCasters > 30)
{
suggestions.Add("Reduce number of shadow casting objects for better performance");
}

if (summary.totalShadowLights > 4)
{
suggestions.Add("Consider using fewer shadow casting lights");
}

if (summary.maxShadowResolution > 2) // High
{
suggestions.Add("Consider lowering shadow resolution for better performance");
}

if (summary.averageShadowDistance > 50f)
{
suggestions.Add("Reduce shadow distance to improve performance");
}

return suggestions;
}

// 绘制阴影相关可视化(用于调试)
void OnDrawGizmos()
{
if (!visualizeShadowMaps || !enableShadowAnalysis) return;

// 可视化阴影投射渲染器
Gizmos.color = Color.yellow;
foreach (var rendererData in m_ShadowRendererData.Values)
{
if (rendererData.castsShadows)
{
Gizmos.DrawWireCube(rendererData.boundsCenter, rendererData.boundsSize);
}
}

// 可视化阴影光源影响范围
foreach (var lightData in m_ShadowLightData.Values)
{
if (!lightData.enabled) continue;

Gizmos.color = Color.red;
switch (lightData.light.type)
{
case LightType.Directional:
// 平行光影响整个场景
break;
case LightType.Point:
Gizmos.DrawWireSphere(lightData.light.transform.position, lightData.light.range);
break;
case LightType.Spot:
// 绘制聚光灯锥体
Gizmos.matrix = Matrix4x4.TRS(
lightData.light.transform.position,
lightData.light.transform.rotation,
Vector3.one
);
float angle = lightData.light.spotAngle * Mathf.Deg2Rad;
float radius = lightData.light.range * Mathf.Tan(angle / 2f);
Gizmos.DrawFrustum(Vector3.zero, angle, lightData.light.range, 0, radius / lightData.light.range);
break;
}
}
}
}

15.9 光照与阴影优化器

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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class LightingShadowOptimizer : MonoBehaviour
{
[Header("Optimization Settings")]
public bool enableAutomaticOptimization = false;
public bool enableLODForLights = true;
public bool enableShadowCulling = true;
public bool enableLightBakingSuggestion = true;

[Header("Optimization Thresholds")]
public float maxLightRangeMultiplier = 2.0f;
public float minLightIntensityThreshold = 0.1f;
public int maxShadowResolution = 2048;
public float maxShadowDistance = 50f;
public int maxAdditionalLights = 4;

[Header("Performance Targets")]
public float targetFrameRate = 60f;
public int targetShadowCasters = 30;
public int targetLights = 8;

private LightDataProcessor m_LightDataProcessor;
private ShadowDataProcessor m_ShadowDataProcessor;
private List<OptimizationSuggestion> m_Suggestions = new List<OptimizationSuggestion>();

[System.Serializable]
public class OptimizationSuggestion
{
public string category; // "Light", "Shadow", "Performance"
public string targetName;
public string issue;
public string solution;
public float priority; // 0-1, 1 is highest priority
public bool isCritical;
public System.Action applyAction;
}

[System.Serializable]
public class OptimizationStats
{
public int totalSuggestions;
public int criticalSuggestions;
public int appliedSuggestions;
public float performanceScore; // 0-100
public float estimatedFPSImprovement;
public int estimatedDrawCallReduction;
}

public OptimizationStats stats = new OptimizationStats();

void Start()
{
m_LightDataProcessor = GetComponent<LightDataProcessor>();
if (m_LightDataProcessor == null)
{
m_LightDataProcessor = gameObject.AddComponent<LightDataProcessor>();
}

m_ShadowDataProcessor = GetComponent<ShadowDataProcessor>();
if (m_ShadowDataProcessor == null)
{
m_ShadowDataProcessor = gameObject.AddComponent<ShadowDataProcessor>();
}

if (enableAutomaticOptimization)
{
InvokeRepeating("RunOptimizationAnalysis", 0f, 5f); // 每5秒运行一次
}
}

void RunOptimizationAnalysis()
{
AnalyzeOptimizationOpportunities();

if (enableAutomaticOptimization)
{
ApplyHighPrioritySuggestions();
}
}

public void AnalyzeOptimizationOpportunities()
{
m_Suggestions.Clear();

// 分析光源优化机会
AnalyzeLightOptimizations();

// 分析阴影优化机会
AnalyzeShadowOptimizations();

// 分析整体性能优化机会
AnalyzePerformanceOptimizations();

UpdateStats();

// 输出分析结果
LogOptimizationAnalysis();
}

private void AnalyzeLightOptimizations()
{
var lightSummary = m_LightDataProcessor.GetLightSummary();
var lightDataList = m_LightDataProcessor.GetLightAnalysisDataList();

// 检查光源数量
if (lightSummary.additionalLightsCount > maxAdditionalLights)
{
var suggestion = new OptimizationSuggestion
{
category = "Light",
targetName = "Scene Lights",
issue = $"Too many additional lights: {lightSummary.additionalLightsCount} (max: {maxAdditionalLights})",
solution = "Consider using fewer real-time lights or switching some to baked lighting",
priority = 0.9f,
isCritical = true,
applyAction = () => ReduceAdditionalLights()
};
m_Suggestions.Add(suggestion);
}

// 检查高亮度光源
if (lightSummary.maxIntensity > targetFrameRate / 10f) // 简化的性能关联
{
var suggestion = new OptimizationSuggestion
{
category = "Light",
targetName = "High Intensity Lights",
issue = $"High light intensity may impact performance: {lightSummary.maxIntensity:F2}",
solution = "Reduce light intensities or use area lights instead of point lights",
priority = 0.7f,
isCritical = false,
applyAction = () => ReduceLightIntensities()
};
m_Suggestions.Add(suggestion);
}

// 检查远距离光源
foreach (var lightData in lightDataList)
{
if (lightData.range > 100f && lightData.intensity < 1f)
{
var suggestion = new OptimizationSuggestion
{
category = "Light",
targetName = lightData.light.name,
issue = $"Large range with low intensity: {lightData.range:F1} units, {lightData.intensity:F2}",
solution = "Reduce range or increase intensity for better efficiency",
priority = 0.5f,
isCritical = false,
applyAction = () => OptimizeLightRange(lightData.light)
};
m_Suggestions.Add(suggestion);
}
}
}

private void AnalyzeShadowOptimizations()
{
var shadowSummary = m_ShadowDataProcessor.GetShadowSummary();
var shadowLightData = m_ShadowDataProcessor.GetShadowLights();

// 检查阴影投射物体数量
if (shadowSummary.totalShadowCasters > targetShadowCasters)
{
var suggestion = new OptimizationSuggestion
{
category = "Shadow",
targetName = "Shadow Casters",
issue = $"Too many shadow casting objects: {shadowSummary.totalShadowCasters} (target: {targetShadowCasters})",
solution = "Change some objects to receive-only shadows or disable shadow casting",
priority = 0.8f,
isCritical = true,
applyAction = () => ReduceShadowCasters()
};
m_Suggestions.Add(suggestion);
}

// 检查阴影分辨率
if (shadowSummary.maxShadowResolution > 2) // High
{
var suggestion = new OptimizationSuggestion
{
category = "Shadow",
targetName = "Shadow Resolution",
issue = $"High shadow resolution may impact performance",
solution = "Reduce shadow resolution in Quality Settings",
priority = 0.6f,
isCritical = false,
applyAction = () => ReduceShadowResolution()
};
m_Suggestions.Add(suggestion);
}

// 检查阴影距离
if (shadowSummary.averageShadowDistance > maxShadowDistance)
{
var suggestion = new OptimizationSuggestion
{
category = "Shadow",
targetName = "Shadow Distance",
issue = $"Large shadow distance: {shadowSummary.averageShadowDistance:F1}",
solution = "Reduce shadow distance in Quality Settings",
priority = 0.7f,
isCritical = false,
applyAction = () => ReduceShadowDistance()
};
m_Suggestions.Add(suggestion);
}
}

private void AnalyzePerformanceOptimizations()
{
var lightSummary = m_LightDataProcessor.GetLightSummary();
var shadowSummary = m_ShadowDataProcessor.GetShadowSummary();

// 整体性能建议
if (lightSummary.totalLights + shadowSummary.totalShadowCasters > targetLights + targetShadowCasters)
{
var suggestion = new OptimizationSuggestion
{
category = "Performance",
targetName = "Overall Scene",
issue = $"Scene may be too complex for target performance",
solution = "Consider using LODs, occlusion culling, or reducing scene complexity",
priority = 0.8f,
isCritical = true,
applyAction = () => ApplySceneOptimizations()
};
m_Suggestions.Add(suggestion);
}
}

private void UpdateStats()
{
stats.totalSuggestions = m_Suggestions.Count;
stats.criticalSuggestions = m_Suggestions.FindAll(s => s.isCritical).Count;
stats.appliedSuggestions = 0; // 这个会在应用建议时更新

// 计算性能评分 (简化计算)
float lightScore = Mathf.Clamp01(1.0f - (m_LightDataProcessor.GetLightSummary().additionalLightsCount / (float)maxAdditionalLights));
float shadowScore = Mathf.Clamp01(1.0f - (m_ShadowDataProcessor.GetShadowSummary().totalShadowCasters / (float)targetShadowCasters));
stats.performanceScore = (lightScore + shadowScore) * 50f; // 转换为0-100分

stats.estimatedFPSImprovement = stats.criticalSuggestions * 5f; // 每个关键问题约提升5FPS
stats.estimatedDrawCallReduction = stats.criticalSuggestions * 10; // 每个关键问题约减少10个Draw Call
}

private void LogOptimizationAnalysis()
{
var log = new System.Text.StringBuilder();
log.AppendLine("=== Lighting & Shadow Optimization Analysis ===");
log.AppendLine($"Total Suggestions: {stats.totalSuggestions}");
log.AppendLine($"Critical Suggestions: {stats.criticalSuggestions}");
log.AppendLine($"Performance Score: {stats.performanceScore:F1}/100");
log.AppendLine($"Estimated FPS Improvement: {stats.estimatedFPSImprovement:F1}");
log.AppendLine($"Estimated Draw Call Reduction: {stats.estimatedDrawCallReduction}");

if (m_Suggestions.Count > 0)
{
log.AppendLine("\nSuggested Optimizations:");
var sortedSuggestions = new List<OptimizationSuggestion>(m_Suggestions);
sortedSuggestions.Sort((a, b) => b.priority.CompareTo(a.priority));

foreach (var suggestion in sortedSuggestions)
{
var priorityText = suggestion.priority > 0.8f ? "HIGH" :
suggestion.priority > 0.5f ? "MEDIUM" : "LOW";
log.AppendLine($"[{priorityText}] {suggestion.category}: {suggestion.targetName} - {suggestion.issue}");
log.AppendLine($" Solution: {suggestion.solution}");
}
}
else
{
log.AppendLine("\nNo optimizations suggested - current configuration appears optimal!");
}

Debug.Log(log.ToString());
}

// 应用高优先级建议
public void ApplyHighPrioritySuggestions()
{
var highPrioritySuggestions = m_Suggestions.FindAll(s => s.priority > 0.7f && s.applyAction != null);

foreach (var suggestion in highPrioritySuggestions)
{
if (suggestion.applyAction != null)
{
suggestion.applyAction();
stats.appliedSuggestions++;
Debug.Log($"[Optimizer] Applied suggestion: {suggestion.issue}");
}
}

// 重新分析以获取新建议
Invoke("AnalyzeOptimizationOpportunities", 1f);
}

// 应用所有建议
public void ApplyAllSuggestions()
{
foreach (var suggestion in m_Suggestions)
{
if (suggestion.applyAction != null)
{
suggestion.applyAction();
stats.appliedSuggestions++;
}
}

Debug.Log($"[Optimizer] Applied {stats.appliedSuggestions} suggestions");
}

// 具体的优化方法
private void ReduceAdditionalLights()
{
var lights = m_LightDataProcessor.GetSceneLights();
var lightsToDisable = new List<Light>();

// 找到强度最低的额外光源并禁用
var additionalLights = lights.FindAll(l => l.type != LightType.Directional);
additionalLights.Sort((a, b) => a.intensity.CompareTo(b.intensity));

int lightsToDisableCount = Mathf.Max(0, additionalLights.Count - maxAdditionalLights);
for (int i = 0; i < lightsToDisableCount && i < additionalLights.Count; i++)
{
lightsToDisable.Add(additionalLights[i]);
}

foreach (var light in lightsToDisable)
{
light.enabled = false;
Debug.Log($"[Optimizer] Disabled light '{light.name}' due to optimization");
}
}

private void ReduceLightIntensities()
{
var lights = m_LightDataProcessor.GetSceneLights();

foreach (var light in lights)
{
if (light.intensity > 2f) // 高强度光源
{
light.intensity = Mathf.Clamp(light.intensity * 0.8f, minLightIntensityThreshold, light.intensity);
}
}
}

private void OptimizeLightRange(Light light)
{
if (light != null && light.range > 50f)
{
light.range = Mathf.Clamp(light.range * 0.7f, 5f, light.range);
}
}

private void ReduceShadowCasters()
{
var shadowRenderers = m_ShadowDataProcessor.GetShadowCastingRenderers();
var renderersToChange = new List<Renderer>();

// 优先改变距离相机最远的阴影投射物体
var rendererDataList = new List<ShadowDataProcessor.ShadowRendererData>();
foreach (var renderer in shadowRenderers)
{
var data = m_ShadowDataProcessor.GetShadowRendererData(renderer);
if (data != null)
{
rendererDataList.Add(data);
}
}

rendererDataList.Sort((a, b) => b.distanceToMainLight.CompareTo(a.distanceToMainLight));

int renderersToChangeCount = Mathf.Max(0, shadowRenderers.Count - targetShadowCasters);
for (int i = 0; i < renderersToChangeCount && i < rendererDataList.Count; i++)
{
renderersToChange.Add(rendererDataList[i].renderer);
}

foreach (var renderer in renderersToChange)
{
renderer.shadowCastingMode = ShadowCastingMode.ShadowsOnly; // 或者 Off
Debug.Log($"[Optimizer] Changed shadow mode for '{renderer.name}'");
}
}

private void ReduceShadowResolution()
{
// 这需要修改Quality Settings,这里只是记录
Debug.Log("[Optimizer] Consider reducing shadow resolution in Quality Settings for better performance");
}

private void ReduceShadowDistance()
{
var shadowLights = m_ShadowDataProcessor.GetShadowLights();

foreach (var light in shadowLights)
{
if (light.shadowDistance > maxShadowDistance)
{
light.shadowDistance = maxShadowDistance;
}
}
}

private void ApplySceneOptimizations()
{
// 应用综合场景优化
Debug.Log("[Optimizer] Applying scene-wide optimizations...");

// 可以在这里添加更多综合优化措施
// 比如启用LOD、设置遮挡剔除等
}

// 获取优化建议列表
public List<OptimizationSuggestion> GetOptimizationSuggestions()
{
return new List<OptimizationSuggestion>(m_Suggestions);
}

// 获取特定类别的建议
public List<OptimizationSuggestion> GetSuggestionsByCategory(string category)
{
return m_Suggestions.FindAll(s => s.category == category);
}

// 获取性能报告
public string GetPerformanceReport()
{
var report = new System.Text.StringBuilder();
report.AppendLine("=== Lighting & Shadow Performance Report ===");
report.AppendLine($"Performance Score: {stats.performanceScore:F1}/100");
report.AppendLine($"Total Issues Found: {stats.totalSuggestions}");
report.AppendLine($"Critical Issues: {stats.criticalSuggestions}");
report.AppendLine($"Applied Optimizations: {stats.appliedSuggestions}");
report.AppendLine($"Estimated Performance Gain: {stats.estimatedFPSImprovement:F1} FPS");

return report.ToString();
}
}

实践练习

15.10 练习1:光照数据处理分析

目标:创建一个工具来分析场景中的光照数据

步骤

  1. 创建LightDataProcessor类
  2. 实现光源数据收集
  3. 分析光源属性和影响
  4. 生成光照摘要报告
  5. 测试分析工具的功能

实现要点

  • 收集各种类型的光源
  • 计算光源影响范围
  • 分析光源性能影响

15.11 练习2:阴影数据处理

目标:实现阴影数据的全面分析

步骤

  1. 创建ShadowDataProcessor类
  2. 收集阴影光源信息
  3. 分析阴影投射物体
  4. 生成阴影配置报告
  5. 验证阴影性能数据

分析维度

  • 阴影贴图分辨率
  • 阴影距离设置
  • 阴影偏移参数

15.12 练习3:光照性能优化

目标:创建光照性能优化工具

步骤

  1. 实现光照配置分析
  2. 识别性能瓶颈
  3. 提供优化建议
  4. 自动应用优化
  5. 验证优化效果

优化策略

  • 光源数量控制
  • 光照强度调整
  • 光照范围优化

15.13 练习4:阴影性能优化

目标:实现阴影性能优化

步骤

  1. 分析阴影配置
  2. 识别阴影性能问题
  3. 提供阴影优化方案
  4. 实现自动优化
  5. 测试性能改进

优化方面

  • 阴影投射物体控制
  • 阴影分辨率调整
  • 阴影距离优化

15.14 练习5:综合优化系统

目标:创建综合的光照阴影优化系统

步骤

  1. 集成光照和阴影分析
  2. 实现统一优化接口
  3. 提供实时优化建议
  4. 实现自动化优化
  5. 生成综合性能报告

系统特性

  • 实时监控
  • 自动优化
  • 性能预测

总结

第15章深入分析了URP中光照与阴影的源码实现,包括ForwardLights的光照计算、各种阴影渲染通道的实现以及相关的工具类。光照和阴影是渲染管线中的核心功能,直接影响视觉效果和性能表现。

关键要点总结:

  1. 光照计算:ForwardLights负责主光源和附加光源的计算
  2. 阴影渲染:MainLightShadowCasterPass和AdditionalLightsShadowCasterPass处理阴影
  3. 数据传递:光照数据通过优化的结构传递给GPU
  4. 性能优化:合理的光照和阴影配置对性能至关重要
  5. 工具支持:ShadowUtils等工具类提供阴影计算支持

光照与阴影系统的优化是URP性能调优的重点,需要在视觉质量和性能之间找到平衡点。理解其内部实现有助于更好地配置和优化光照阴影效果。

下一章将探讨深度与颜色纹理处理的实现原理,深入了解URP中深度纹理和颜色纹理的生成与使用。