/
AdditionalLightsShadowCasterPass.cs
943 lines (793 loc) · 55.8 KB
/
AdditionalLightsShadowCasterPass.cs
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
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Renders a shadow map atlas for additional shadow-casting Lights.
/// </summary>
public partial class AdditionalLightsShadowCasterPass : ScriptableRenderPass
{
private static class AdditionalShadowsConstantBuffer
{
public static int _AdditionalLightsWorldToShadow;
public static int _AdditionalShadowParams;
public static int _AdditionalShadowOffset0;
public static int _AdditionalShadowOffset1;
public static int _AdditionalShadowFadeParams;
public static int _AdditionalShadowmapSize;
}
/// <summary>
/// x is used in RenderAdditionalShadowMapAtlas to skip shadow map rendering for non-shadow-casting lights.
/// w is perLightFirstShadowSliceIndex, used in Lighting shader to find if Additional light casts shadows.
/// </summary>
readonly static Vector4 c_DefaultShadowParams = new Vector4(0, 0, 0, -1);
static int m_AdditionalLightsWorldToShadow_SSBO;
static int m_AdditionalShadowParams_SSBO;
bool m_UseStructuredBuffer;
const int k_ShadowmapBufferBits = 16;
private int m_AdditionalLightsShadowmapID;
internal RTHandle m_AdditionalLightsShadowmapHandle;
private bool m_CreateEmptyShadowmap;
private bool m_EmptyShadowmapNeedsClear = false;
private RTHandle m_EmptyAdditionalLightShadowmapTexture;
private const int k_EmptyShadowMapDimensions = 1;
private const string k_EmptyShadowMapName = "_EmptyAdditionalLightShadowmapTexture";
internal static Vector4[] s_EmptyAdditionalLightIndexToShadowParams = null;
float m_MaxShadowDistanceSq;
float m_CascadeBorder;
ShadowSliceData[] m_AdditionalLightsShadowSlices = null;
int[] m_VisibleLightIndexToAdditionalLightIndex = null; // maps a "global" visible light index (index to lightData.visibleLights) to an "additional light index" (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...), or -1 if it is not an additional light (i.e if it is the main light)
int[] m_AdditionalLightIndexToVisibleLightIndex = null; // maps additional light index (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...) to its "global" visible light index (index to lightData.visibleLights)
List<int> m_ShadowSliceToAdditionalLightIndex = new List<int>(); // For each shadow slice, store the "additional light indices" of the punctual light that casts it
List<int> m_GlobalShadowSliceIndexToPerLightShadowSliceIndex = new List<int>(); // For each shadow slice, store its "per-light shadow slice index" in the punctual light that casts it (can be up to 5 for point lights)
Vector4[] m_AdditionalLightIndexToShadowParams = null; // per-additional-light shadow info passed to the lighting shader (x: shadowStrength, y: softShadows, z: light type, w: perLightFirstShadowSliceIndex)
Matrix4x4[] m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = null; // per-shadow-slice info passed to the lighting shader
int renderTargetWidth;
int renderTargetHeight;
ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Additional Shadows");
private PassData m_PassData;
/// <summary>
/// Creates a new <c>AdditionalLightsShadowCasterPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <seealso cref="RenderPassEvent"/>
public AdditionalLightsShadowCasterPass(RenderPassEvent evt)
{
base.profilingSampler = new ProfilingSampler(nameof(AdditionalLightsShadowCasterPass));
renderPassEvent = evt;
m_PassData = new PassData();
AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow = Shader.PropertyToID("_AdditionalLightsWorldToShadow");
AdditionalShadowsConstantBuffer._AdditionalShadowParams = Shader.PropertyToID("_AdditionalShadowParams");
AdditionalShadowsConstantBuffer._AdditionalShadowOffset0 = Shader.PropertyToID("_AdditionalShadowOffset0");
AdditionalShadowsConstantBuffer._AdditionalShadowOffset1 = Shader.PropertyToID("_AdditionalShadowOffset1");
AdditionalShadowsConstantBuffer._AdditionalShadowFadeParams = Shader.PropertyToID("_AdditionalShadowFadeParams");
AdditionalShadowsConstantBuffer._AdditionalShadowmapSize = Shader.PropertyToID("_AdditionalShadowmapSize");
m_AdditionalLightsShadowmapID = Shader.PropertyToID("_AdditionalLightsShadowmapTexture");
m_AdditionalLightsWorldToShadow_SSBO = Shader.PropertyToID("_AdditionalLightsWorldToShadow_SSBO");
m_AdditionalShadowParams_SSBO = Shader.PropertyToID("_AdditionalShadowParams_SSBO");
m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
// Preallocated a fixed size. CommandBuffer.SetGlobal* does allow this data to grow.
int maxVisibleAdditionalLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
const int maxMainLights = 1;
int maxVisibleLights = maxVisibleAdditionalLights + maxMainLights;
int maxAdditionalLightShadowParams = m_UseStructuredBuffer ? maxVisibleLights : Math.Min(maxVisibleLights, maxVisibleAdditionalLights);
// These array sizes should be as big as ScriptableCullingParameters.maximumVisibleLights (that is defined during ScriptableRenderer.SetupCullingParameters).
// We initialize these array sizes with the number of visible lights allowed by the UniversalRenderer.
// The number of visible lights can become much higher when using the Deferred rendering path, we resize the arrays during Setup() if required.
m_AdditionalLightIndexToVisibleLightIndex = new int[maxAdditionalLightShadowParams];
m_VisibleLightIndexToAdditionalLightIndex = new int[maxVisibleLights];
m_AdditionalLightIndexToShadowParams = new Vector4[maxAdditionalLightShadowParams];
s_EmptyAdditionalLightIndexToShadowParams = new Vector4[maxAdditionalLightShadowParams];
for (int i = 0; i < s_EmptyAdditionalLightIndexToShadowParams.Length; i++)
s_EmptyAdditionalLightIndexToShadowParams[i] = c_DefaultShadowParams;
if (!m_UseStructuredBuffer)
{
// Uniform buffers are faster on some platforms, but they have stricter size limitations
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[maxVisibleAdditionalLights];
}
m_EmptyAdditionalLightShadowmapTexture = ShadowUtils.AllocShadowRT(k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions, k_ShadowmapBufferBits, 1, 0, name: k_EmptyShadowMapName);
m_EmptyShadowmapNeedsClear = true;
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
m_AdditionalLightsShadowmapHandle?.Release();
m_EmptyAdditionalLightShadowmapTexture?.Release();
}
// Magic numbers used to identify light type when rendering shadow receiver.
// Keep in sync with AdditionalLightRealtimeShadow code in com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
private const float LightTypeIdentifierInShadowParams_Spot = 0;
private const float LightTypeIdentifierInShadowParams_Point = 1;
// Returns the guard angle that must be added to a frustum angle covering a projection map of resolution sliceResolutionInTexels,
// in order to also cover a guard band of size guardBandSizeInTexels around the projection map.
// Formula illustrated in https://i.ibb.co/wpW5Mnf/Calc-Guard-Angle.png
internal static float CalcGuardAngle(float frustumAngleInDegrees, float guardBandSizeInTexels, float sliceResolutionInTexels)
{
float frustumAngle = frustumAngleInDegrees * Mathf.Deg2Rad;
float halfFrustumAngle = frustumAngle / 2;
float tanHalfFrustumAngle = Mathf.Tan(halfFrustumAngle);
float halfSliceResolution = sliceResolutionInTexels / 2;
float halfGuardBand = guardBandSizeInTexels / 2;
float factorBetweenAngleTangents = 1 + halfGuardBand / halfSliceResolution;
float tanHalfGuardAnglePlusHalfFrustumAngle = tanHalfFrustumAngle * factorBetweenAngleTangents;
float halfGuardAnglePlusHalfFrustumAngle = Mathf.Atan(tanHalfGuardAnglePlusHalfFrustumAngle);
float halfGuardAngleInRadian = halfGuardAnglePlusHalfFrustumAngle - halfFrustumAngle;
float guardAngleInRadian = 2 * halfGuardAngleInRadian;
float guardAngleInDegree = guardAngleInRadian * Mathf.Rad2Deg;
return guardAngleInDegree;
}
// Returns the guard angle that must be added to a point light shadow face frustum angle
// in order to avoid shadows missing at the boundaries between cube faces.
internal static float GetPointLightShadowFrustumFovBiasInDegrees(int shadowSliceResolution, bool shadowFiltering)
{
// Commented-out code below uses the theoretical formula to compute the required guard angle based on the number of additional
// texels that the projection should cover. It is close to HDRP's HDShadowUtils.CalcGuardAnglePerspective method.
// However, due to precision issues or other filterings performed at lighting for example, this formula also still requires a fudge factor.
// Since we only handle a fixed number of resolutions, we use empirical values instead.
#if false
float fudgeFactor = 1.5f;
return fudgeFactor * CalcGuardAngle(90, shadowFiltering ? 5 : 1, shadowSliceResolution);
#endif
float fovBias = 4.00f;
// Empirical value found to remove gaps between point light shadow faces in test scenes.
// We can see that the guard angle is roughly proportional to the inverse of resolution https://docs.google.com/spreadsheets/d/1QrIZJn18LxVKq2-K1XS4EFRZcZdZOJTTKKhDN8Z1b_s
if (shadowSliceResolution <= ShadowUtils.kMinimumPunctualLightHardShadowResolution)
{
#if DEVELOPMENT_BUILD
if (!m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall)
{
Debug.LogWarning("Too many additional punctual lights shadows, increase shadow atlas size or remove some shadowed lights");
m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
}
#endif
}
else if (shadowSliceResolution <= 16)
fovBias = 43.0f;
else if (shadowSliceResolution <= 32)
fovBias = 18.55f;
else if (shadowSliceResolution <= 64)
fovBias = 8.63f;
else if (shadowSliceResolution <= 128)
fovBias = 4.13f;
else if (shadowSliceResolution <= 256)
fovBias = 2.03f;
else if (shadowSliceResolution <= 512)
fovBias = 1.00f;
else if (shadowSliceResolution <= 1024)
fovBias = 0.50f;
else if (shadowSliceResolution <= 2048)
fovBias = 0.25f;
if (shadowFiltering)
{
if (shadowSliceResolution <= ShadowUtils.kMinimumPunctualLightSoftShadowResolution)
{
#if DEVELOPMENT_BUILD
if (!m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall)
{
Debug.LogWarning("Too many additional punctual lights shadows to use Soft Shadows. Increase shadow atlas size, remove some shadowed lights or use Hard Shadows.");
// With such small resolutions no fovBias can give good visual results
m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
}
#endif
}
else if (shadowSliceResolution <= 32)
fovBias += 9.35f;
else if (shadowSliceResolution <= 64)
fovBias += 4.07f;
else if (shadowSliceResolution <= 128)
fovBias += 1.77f;
else if (shadowSliceResolution <= 256)
fovBias += 0.85f;
else if (shadowSliceResolution <= 512)
fovBias += 0.39f;
else if (shadowSliceResolution <= 1024)
fovBias += 0.17f;
else if (shadowSliceResolution <= 2048)
fovBias += 0.074f;
// These values were verified to work on untethered devices for which m_SupportsBoxFilterForShadows is true.
// TODO: Investigate finer-tuned values for those platforms. Soft shadows are implemented differently for them.
}
return fovBias;
}
#if DEVELOPMENT_BUILD
private bool m_IssuedMessageAboutShadowSlicesTooMany = false;
private bool m_IssuedMessageAboutShadowMapsRescale = false;
private bool m_IssuedMessageAboutShadowMapsTooBig = false;
private bool m_IssuedMessageAboutRemovedShadowSlices = false;
private static bool m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
private static bool m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
#endif
Dictionary<int, ulong> m_ShadowRequestsHashes = new Dictionary<int, ulong>(); // used to keep track of changes in the shadow requests and shadow atlas configuration (per camera)
ulong ResolutionLog2ForHash(int resolution)
{
switch (resolution)
{
case 4096: return 12;
case 2048: return 11;
case 1024: return 10;
case 0512: return 09;
}
return 08;
}
ulong ComputeShadowRequestHash(UniversalLightData lightData, UniversalShadowData shadowData)
{
ulong numberOfShadowedPointLights = 0;
ulong numberOfSoftShadowedLights = 0;
ulong numberOfShadowsWithResolution0128 = 0;
ulong numberOfShadowsWithResolution0256 = 0;
ulong numberOfShadowsWithResolution0512 = 0;
ulong numberOfShadowsWithResolution1024 = 0;
ulong numberOfShadowsWithResolution2048 = 0;
ulong numberOfShadowsWithResolution4096 = 0;
var visibleLights = lightData.visibleLights;
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
{
if (!ShadowUtils.IsValidShadowCastingLight(lightData, visibleLightIndex))
continue;
ref VisibleLight vl = ref visibleLights.UnsafeElementAt(visibleLightIndex);
if (vl.lightType == LightType.Point)
++numberOfShadowedPointLights;
if (vl.light.shadows == LightShadows.Soft)
++numberOfSoftShadowedLights;
if (shadowData.resolution[visibleLightIndex] == 0128)
++numberOfShadowsWithResolution0128;
if (shadowData.resolution[visibleLightIndex] == 0256)
++numberOfShadowsWithResolution0256;
if (shadowData.resolution[visibleLightIndex] == 0512)
++numberOfShadowsWithResolution0512;
if (shadowData.resolution[visibleLightIndex] == 1024)
++numberOfShadowsWithResolution1024;
if (shadowData.resolution[visibleLightIndex] == 2048)
++numberOfShadowsWithResolution2048;
if (shadowData.resolution[visibleLightIndex] == 4096)
++numberOfShadowsWithResolution4096;
}
ulong shadowRequestsHash = ResolutionLog2ForHash(shadowData.additionalLightsShadowmapWidth) - 8; // bits [00~02]
shadowRequestsHash |= numberOfShadowedPointLights << 03; // bits [03~10]
shadowRequestsHash |= numberOfSoftShadowedLights << 11; // bits [11~18]
shadowRequestsHash |= numberOfShadowsWithResolution0128 << 19; // bits [19~26]
shadowRequestsHash |= numberOfShadowsWithResolution0256 << 27; // bits [27~34]
shadowRequestsHash |= numberOfShadowsWithResolution0512 << 35; // bits [35~42]
shadowRequestsHash |= numberOfShadowsWithResolution1024 << 43; // bits [43~49]
shadowRequestsHash |= numberOfShadowsWithResolution2048 << 50; // bits [50~56]
shadowRequestsHash |= numberOfShadowsWithResolution4096 << 57; // bits [57~63]
return shadowRequestsHash;
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData"></param>
/// <returns></returns>
public bool Setup(ref RenderingData renderingData)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
return Setup(universalRenderingData, cameraData, lightData, shadowData);
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData">Data containing rendering settings.</param>
/// <param name="cameraData">Data containing camera settings.</param>
/// <param name="lightData">Data containing light settings.</param>
/// <param name="shadowData">Data containing shadow settings.</param>
/// <returns>Returns true if additonal shadows are enabled otherwise false.</returns>
public bool Setup(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
{
using var profScope = new ProfilingScope(m_ProfilingSetupSampler);
if (!shadowData.additionalLightShadowsEnabled)
return false;
if (!shadowData.supportsAdditionalLightShadows)
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants, shadowData);
Clear();
renderTargetWidth = shadowData.additionalLightsShadowmapWidth;
renderTargetHeight = shadowData.additionalLightsShadowmapHeight;
var visibleLights = lightData.visibleLights;
ref AdditionalLightsShadowAtlasLayout atlasLayout = ref shadowData.shadowAtlasLayout;
#if DEVELOPMENT_BUILD
// Check changes in the shadow requests and shadow atlas configuration - compute shadow request/configuration hash
if (!cameraData.isPreviewCamera)
{
ulong newShadowRequestHash = ComputeShadowRequestHash(lightData, shadowData);
ulong oldShadowRequestHash = 0;
m_ShadowRequestsHashes.TryGetValue(cameraData.camera.GetHashCode(), out oldShadowRequestHash);
if (oldShadowRequestHash != newShadowRequestHash)
{
m_ShadowRequestsHashes[cameraData.camera.GetHashCode()] = newShadowRequestHash;
// config changed ; reset error message flags as we might need to issue those messages again
m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
m_IssuedMessageAboutShadowMapsRescale = false;
m_IssuedMessageAboutShadowMapsTooBig = false;
m_IssuedMessageAboutShadowSlicesTooMany = false;
m_IssuedMessageAboutRemovedShadowSlices = false;
}
}
#endif
if (m_VisibleLightIndexToAdditionalLightIndex.Length < visibleLights.Length)
{
// Array "visibleLights" is returned by ScriptableRenderContext.Cull()
// The maximum number of "visibleLights" that ScriptableRenderContext.Cull() should return, is defined by parameter ScriptableCullingParameters.maximumVisibleLights
// Universal RP sets this "ScriptableCullingParameters.maximumVisibleLights" value during ScriptableRenderer.SetupCullingParameters.
// When using Deferred rendering, it is possible to specify a very high number of visible lights.
m_VisibleLightIndexToAdditionalLightIndex = new int[visibleLights.Length];
}
int maxAdditionalLightShadowParams = m_UseStructuredBuffer ? visibleLights.Length : Math.Min(visibleLights.Length, UniversalRenderPipeline.maxVisibleAdditionalLights);
if (m_AdditionalLightIndexToVisibleLightIndex.Length < maxAdditionalLightShadowParams)
{
m_AdditionalLightIndexToVisibleLightIndex = new int[maxAdditionalLightShadowParams];
m_AdditionalLightIndexToShadowParams = new Vector4[maxAdditionalLightShadowParams];
}
int totalShadowSlicesCount = atlasLayout.GetTotalShadowSlicesCount();
int totalShadowResolutionRequestCount = atlasLayout.GetTotalShadowResolutionRequestCount();
int shadowSlicesScaleFactor = atlasLayout.GetShadowSlicesScaleFactor();
bool hasTooManyShadowMaps = atlasLayout.HasTooManyShadowMaps();
int atlasSize = atlasLayout.GetAtlasSize();
#if DEVELOPMENT_BUILD
if (totalShadowSlicesCount < totalShadowResolutionRequestCount)
{
if (!m_IssuedMessageAboutRemovedShadowSlices)
{
Debug.LogWarning($"Too many additional punctual lights shadows to look good, URP removed {totalShadowResolutionRequestCount - totalShadowSlicesCount } shadow maps to make the others fit in the shadow atlas. To avoid this, increase shadow atlas size, remove some shadowed lights, replace soft shadows by hard shadows ; or replace point lights by spot lights");
m_IssuedMessageAboutRemovedShadowSlices = true; // Only output this once per shadow requests configuration
}
}
if (!m_IssuedMessageAboutShadowMapsTooBig && hasTooManyShadowMaps)
{
Debug.LogWarning($"Too many additional punctual lights shadows. URP tried reducing shadow resolutions by {shadowSlicesScaleFactor} but it was still too much. Increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame (currently was {totalShadowSlicesCount}).");
m_IssuedMessageAboutShadowMapsTooBig = true; // Only output this once per shadow requests configuration
}
if (!m_IssuedMessageAboutShadowMapsRescale && shadowSlicesScaleFactor > 1)
{
Debug.Log($"Reduced additional punctual light shadows resolution by {shadowSlicesScaleFactor} to make {totalShadowSlicesCount} shadow maps fit in the {atlasSize}x{atlasSize} shadow atlas. To avoid this, increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame");
m_IssuedMessageAboutShadowMapsRescale = true; // Only output this once per shadow requests configuration
}
#endif
if (m_AdditionalLightsShadowSlices == null || m_AdditionalLightsShadowSlices.Length < totalShadowSlicesCount)
m_AdditionalLightsShadowSlices = new ShadowSliceData[totalShadowSlicesCount];
if (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix == null ||
(m_UseStructuredBuffer && (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length < totalShadowSlicesCount))) // m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix can be resized when using SSBO to pass shadow data (no size limitation)
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[totalShadowSlicesCount];
// initialize _AdditionalShadowParams
for (int i = 0; i < maxAdditionalLightShadowParams; ++i)
m_AdditionalLightIndexToShadowParams[i] = c_DefaultShadowParams;
int validShadowCastingLightsCount = 0;
bool supportsSoftShadows = shadowData.supportsSoftShadows;
int additionalLightCount = 0;
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
{
ref VisibleLight shadowLight = ref visibleLights.UnsafeElementAt(visibleLightIndex);
// Skip main directional light as it is not packed into the shadow atlas
if (visibleLightIndex == lightData.mainLightIndex)
{
m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = -1;
continue;
}
int additionalLightIndex = additionalLightCount++;
if (additionalLightIndex >= m_AdditionalLightIndexToVisibleLightIndex.Length)
continue;
// We need to always set these indices, even if the light is not shadow casting or doesn't fit in the shadow slices (UUM-46577)
m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex] = visibleLightIndex;
m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = additionalLightIndex;
if (m_ShadowSliceToAdditionalLightIndex.Count >= totalShadowSlicesCount || additionalLightIndex >= maxAdditionalLightShadowParams)
continue;
LightType lightType = shadowLight.lightType;
int perLightShadowSlicesCount = ShadowUtils.GetPunctualLightShadowSlicesCount(lightType);
if ((m_ShadowSliceToAdditionalLightIndex.Count + perLightShadowSlicesCount) > totalShadowSlicesCount && ShadowUtils.IsValidShadowCastingLight(lightData, visibleLightIndex))
{
#if DEVELOPMENT_BUILD
if (!m_IssuedMessageAboutShadowSlicesTooMany)
{
// This case can especially happen in Deferred, where there can be a high number of visibleLights
Debug.Log($"There are too many shadowed additional punctual lights active at the same time, URP will not render all the shadows. To ensure all shadows are rendered, reduce the number of shadowed additional lights in the scene ; make sure they are not active at the same time ; or replace point lights by spot lights (spot lights use less shadow maps than point lights).");
m_IssuedMessageAboutShadowSlicesTooMany = true; // Only output this once
}
#endif
break;
}
int perLightFirstShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
bool isValidShadowCastingLight = false;
for (int perLightShadowSlice = 0; perLightShadowSlice < perLightShadowSlicesCount; ++perLightShadowSlice)
{
int globalShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
bool lightRangeContainsShadowCasters = renderingData.cullResults.GetShadowCasterBounds(visibleLightIndex, out var shadowCastersBounds);
if (lightRangeContainsShadowCasters)
{
// We need to iterate the lights even though additional lights are disabled because
// cullResults.GetShadowCasterBounds() does the fence sync for the shadow culling jobs.
if (!shadowData.supportsAdditionalLightShadows)
continue;
if (ShadowUtils.IsValidShadowCastingLight(lightData, visibleLightIndex))
{
if (!atlasLayout.HasSpaceForLight(visibleLightIndex))
{
// We could not find place in the shadow atlas for shadow maps of this light.
// Skip it.
}
else if (lightType == LightType.Spot)
{
ref readonly URPLightShadowCullingInfos shadowCullingInfos = ref shadowData.visibleLightsShadowCullingInfos.UnsafeElementAt(visibleLightIndex);
ref readonly ShadowSliceData sliceData = ref shadowCullingInfos.slices.UnsafeElementAt(0);
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix = sliceData.viewMatrix;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix = sliceData.projectionMatrix;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData = sliceData.splitData;
if (shadowCullingInfos.IsSliceValid(0))
{
m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
var light = shadowLight.light;
float shadowStrength = light.shadowStrength;
float softShadows = ShadowUtils.SoftShadowQualityToShaderProperty(light, (supportsSoftShadows && light.shadows == LightShadows.Soft));
Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Spot, perLightFirstShadowSliceIndex);
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = sliceData.shadowTransform;
m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
isValidShadowCastingLight = true;
}
}
else if (lightType == LightType.Point)
{
ref readonly URPLightShadowCullingInfos shadowCullingInfos = ref shadowData.visibleLightsShadowCullingInfos.UnsafeElementAt(visibleLightIndex);
ref readonly ShadowSliceData sliceData = ref shadowCullingInfos.slices.UnsafeElementAt(perLightShadowSlice);
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix = sliceData.viewMatrix;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix = sliceData.projectionMatrix;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData = sliceData.splitData;
if (shadowCullingInfos.IsSliceValid(perLightShadowSlice))
{
m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
var light = shadowLight.light;
float shadowStrength = light.shadowStrength;
float softShadows = ShadowUtils.SoftShadowQualityToShaderProperty(light, (supportsSoftShadows && light.shadows == LightShadows.Soft));
Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Point, perLightFirstShadowSliceIndex);
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = sliceData.shadowTransform;
m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
isValidShadowCastingLight = true;
}
}
}
}
}
if (isValidShadowCastingLight)
validShadowCastingLightsCount++;
}
// Lights that need to be rendered in the shadow map atlas
if (validShadowCastingLightsCount == 0)
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants,shadowData);
int shadowCastingLightsBufferCount = m_ShadowSliceToAdditionalLightIndex.Count;
// Trim shadow atlas dimensions if possible (to avoid allocating texture space that will not be used)
int atlasMaxX = 0;
int atlasMaxY = 0;
for (int sortedShadowResolutionRequestIndex = 0; sortedShadowResolutionRequestIndex < totalShadowSlicesCount; ++sortedShadowResolutionRequestIndex)
{
var shadowResolutionRequest = atlasLayout.GetSortedShadowResolutionRequest(sortedShadowResolutionRequestIndex);
atlasMaxX = Mathf.Max(atlasMaxX, shadowResolutionRequest.offsetX + shadowResolutionRequest.allocatedResolution);
atlasMaxY = Mathf.Max(atlasMaxY, shadowResolutionRequest.offsetY + shadowResolutionRequest.allocatedResolution);
}
// ...but make sure we still use power-of-two dimensions (might perform better on some hardware)
renderTargetWidth = Mathf.NextPowerOfTwo(atlasMaxX);
renderTargetHeight = Mathf.NextPowerOfTwo(atlasMaxY);
float oneOverAtlasWidth = 1.0f / renderTargetWidth;
float oneOverAtlasHeight = 1.0f / renderTargetHeight;
Matrix4x4 sliceTransform;
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowCastingLightsBufferCount; ++globalShadowSliceIndex)
{
int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
// We can skip the slice if strength is zero.
if (Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f) || Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
continue;
int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
int perLightSliceIndex = m_GlobalShadowSliceIndexToPerLightShadowSliceIndex[globalShadowSliceIndex];
var shadowResolutionRequest = atlasLayout.GetSliceShadowResolutionRequest(visibleLightIndex, perLightSliceIndex);
int sliceResolution = shadowResolutionRequest.allocatedResolution;
sliceTransform = Matrix4x4.identity;
sliceTransform.m00 = sliceResolution * oneOverAtlasWidth;
sliceTransform.m11 = sliceResolution * oneOverAtlasHeight;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX = shadowResolutionRequest.offsetX;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY = shadowResolutionRequest.offsetY;
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].resolution = sliceResolution;
sliceTransform.m03 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX * oneOverAtlasWidth;
sliceTransform.m13 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY * oneOverAtlasHeight;
// We bake scale and bias to each shadow map in the atlas in the matrix.
// saves some instructions in shader.
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = sliceTransform * m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex];
}
ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_AdditionalLightsShadowmapHandle, renderTargetWidth, renderTargetHeight, k_ShadowmapBufferBits, name: "_AdditionalLightsShadowmapTexture");
m_MaxShadowDistanceSq = cameraData.maxShadowDistance * cameraData.maxShadowDistance;
m_CascadeBorder = shadowData.mainLightShadowCascadeBorder;
m_CreateEmptyShadowmap = false;
useNativeRenderPass = true;
return true;
}
bool SetupForEmptyRendering(bool stripShadowsOffVariants, UniversalShadowData shadowData)
{
if (!stripShadowsOffVariants)
return false;
shadowData.isKeywordAdditionalLightShadowsEnabled = true;
m_CreateEmptyShadowmap = true;
useNativeRenderPass = false;
// Required for scene view camera(URP renderer not initialized)
if (ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_EmptyAdditionalLightShadowmapTexture, k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions, k_ShadowmapBufferBits, name: k_EmptyShadowMapName))
m_EmptyShadowmapNeedsClear = true;
// initialize _AdditionalShadowParams
for (int i = 0; i < m_AdditionalLightIndexToShadowParams.Length; ++i)
m_AdditionalLightIndexToShadowParams[i] = c_DefaultShadowParams;
return true;
}
/// <inheritdoc/>
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
if (m_CreateEmptyShadowmap && !m_EmptyShadowmapNeedsClear)
return;
// Disable obsolete warning for internal usage
#pragma warning disable CS0618
if (m_CreateEmptyShadowmap)
{
ConfigureTarget(m_EmptyAdditionalLightShadowmapTexture);
m_EmptyShadowmapNeedsClear = false;
}
else
ConfigureTarget(m_AdditionalLightsShadowmapHandle);
ConfigureClear(ClearFlag.All, Color.black);
#pragma warning restore CS0618
}
/// <inheritdoc/>
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
if (m_CreateEmptyShadowmap)
{
SetEmptyAdditionalShadowmapAtlas(CommandBufferHelpers.GetRasterCommandBuffer(renderingData.commandBuffer));
universalRenderingData.commandBuffer.SetGlobalTexture(m_AdditionalLightsShadowmapID, m_EmptyAdditionalLightShadowmapTexture);
return;
}
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
if (!shadowData.supportsAdditionalLightShadows)
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
InitPassData(ref m_PassData, cameraData, lightData, shadowData);
InitRendererLists(ref universalRenderingData.cullResults, ref m_PassData, context, default(RenderGraph), false);
RenderAdditionalShadowmapAtlas(CommandBufferHelpers.GetRasterCommandBuffer(universalRenderingData.commandBuffer), ref m_PassData, false);
universalRenderingData.commandBuffer.SetGlobalTexture(m_AdditionalLightsShadowmapID, m_AdditionalLightsShadowmapHandle.nameID);
}
/// <summary>
/// Gets the additional light index from the global visible light index, which is used to index arrays _AdditionalLightsPosition, _AdditionalShadowParams, etc.
/// </summary>
/// <param name="visibleLightIndex">The index of the visible light.</param>
/// <returns>The additional light index.</returns>
public int GetShadowLightIndexFromLightIndex(int visibleLightIndex)
{
if (visibleLightIndex < 0 || visibleLightIndex >= m_VisibleLightIndexToAdditionalLightIndex.Length)
return -1;
return m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex];
}
void Clear()
{
m_ShadowSliceToAdditionalLightIndex.Clear();
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Clear();
}
void SetEmptyAdditionalShadowmapAtlas(RasterCommandBuffer cmd)
{
cmd.EnableKeyword(ShaderGlobalKeywords.AdditionalLightShadows);
SetEmptyAdditionalLightShadowParams(cmd, m_AdditionalLightIndexToShadowParams);
}
internal static void SetEmptyAdditionalLightShadowParams(RasterCommandBuffer cmd, Vector4[] lightIndexToShadowParams)
{
if (RenderingUtils.useStructuredBuffer)
{
ComputeBuffer shadowParamsBuffer = ShaderData.instance.GetAdditionalLightShadowParamsStructuredBuffer(lightIndexToShadowParams.Length);
shadowParamsBuffer.SetData(lightIndexToShadowParams);
cmd.SetGlobalBuffer(m_AdditionalShadowParams_SSBO, shadowParamsBuffer);
}
else
{
cmd.SetGlobalVectorArray(AdditionalShadowsConstantBuffer._AdditionalShadowParams, lightIndexToShadowParams);
}
}
void RenderAdditionalShadowmapAtlas(RasterCommandBuffer cmd, ref PassData data, bool useRenderGraph)
{
NativeArray<VisibleLight> visibleLights = data.lightData.visibleLights;
bool additionalLightHasSoftShadows = false;
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.AdditionalLightsShadow)))
{
// For non-RG, need set the worldToCamera Matrix as that is not set for passes executed before normal rendering,
// otherwise shadows will behave incorrectly when Scene and Game windows are open at the same time (UUM-63267).
if (!useRenderGraph)
ShadowUtils.SetWorldToCameraMatrix(cmd, data.viewMatrix);
bool anyShadowSliceRenderer = false;
int shadowSlicesCount = m_ShadowSliceToAdditionalLightIndex.Count;
if (shadowSlicesCount > 0)
cmd.SetKeyword(ShaderGlobalKeywords.CastingPunctualLightShadow, true);
float lastDepthBias = -10f;
float lastNormalBias = -10f;
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowSlicesCount; ++globalShadowSliceIndex)
{
int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
// we do the shadow strength check here again here because we might have zero strength for non-shadow-casting lights.
// In that case we need the shadow data buffer but we can skip rendering them to shadowmap.
if ( ShadowUtils.FastApproximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f)
|| ShadowUtils.FastApproximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
continue;
int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
ref VisibleLight shadowLight = ref visibleLights.UnsafeElementAt(visibleLightIndex);
ShadowSliceData shadowSliceData = m_AdditionalLightsShadowSlices[globalShadowSliceIndex];
Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, visibleLightIndex, data.shadowData, shadowSliceData.projectionMatrix, shadowSliceData.resolution);
// Update the bias when rendering the first slice or when the bias has changed
if ( globalShadowSliceIndex == 0
|| !ShadowUtils.FastApproximately(shadowBias.x, lastDepthBias)
|| !ShadowUtils.FastApproximately(shadowBias.y, lastNormalBias))
{
ShadowUtils.SetShadowBias(cmd, shadowBias);
lastDepthBias = shadowBias.x;
lastNormalBias = shadowBias.y;
}
// Update light position
Vector3 lightPosition = shadowLight.localToWorldMatrix.GetColumn(3);
ShadowUtils.SetLightPosition(cmd, lightPosition);
// Note: _LightDirection is not updated for additional lights.
// For Directional lights, _LightDirection is used when applying shadow Normal Bias.
// For Spot lights and Point lights _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex.
RendererList shadowRendererList = useRenderGraph? data.shadowRendererListsHdl[globalShadowSliceIndex] : data.shadowRendererLists[globalShadowSliceIndex];
ShadowUtils.RenderShadowSlice(cmd, ref shadowSliceData, ref shadowRendererList, shadowSliceData.projectionMatrix, shadowSliceData.viewMatrix);
additionalLightHasSoftShadows |= shadowLight.light.shadows == LightShadows.Soft;
anyShadowSliceRenderer = true;
}
// We share soft shadow settings for main light and additional lights to save keywords.
// So we check here if pipeline supports soft shadows and either main light or any additional light has soft shadows
// to enable the keyword.
// TODO: In PC and Consoles we can upload shadow data per light and branch on shader. That will be more likely way faster.
bool mainLightHasSoftShadows = data.shadowData.supportsMainLightShadows &&
data.lightData.mainLightIndex != -1 &&
visibleLights[data.lightData.mainLightIndex].light.shadows ==
LightShadows.Soft;
// If the OFF variant has been stripped, the additional light shadows keyword must always be enabled
bool hasOffVariant = !data.stripShadowsOffVariants;
data.shadowData.isKeywordAdditionalLightShadowsEnabled = !hasOffVariant || anyShadowSliceRenderer;
cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightShadows, data.shadowData.isKeywordAdditionalLightShadowsEnabled);
bool softShadows = data.shadowData.supportsSoftShadows && (mainLightHasSoftShadows || additionalLightHasSoftShadows);
data.shadowData.isKeywordSoftShadowsEnabled = softShadows;
ShadowUtils.SetSoftShadowQualityShaderKeywords(cmd, data.shadowData);
if (anyShadowSliceRenderer)
SetupAdditionalLightsShadowReceiverConstants(cmd, data.useStructuredBuffer, softShadows);
}
}
// Set constant buffer data that will be used during the lighting/shadowing pass
void SetupAdditionalLightsShadowReceiverConstants(RasterCommandBuffer cmd, bool useStructuredBuffer, bool softShadows)
{
if (useStructuredBuffer)
{
// per-light data
var shadowParamsBuffer = ShaderData.instance.GetAdditionalLightShadowParamsStructuredBuffer(m_AdditionalLightIndexToShadowParams.Length);
shadowParamsBuffer.SetData(m_AdditionalLightIndexToShadowParams);
cmd.SetGlobalBuffer(m_AdditionalShadowParams_SSBO, shadowParamsBuffer);
// per-shadow-slice data
var shadowSliceMatricesBuffer = ShaderData.instance.GetAdditionalLightShadowSliceMatricesStructuredBuffer(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length);
shadowSliceMatricesBuffer.SetData(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix);
cmd.SetGlobalBuffer(m_AdditionalLightsWorldToShadow_SSBO, shadowSliceMatricesBuffer);
}
else
{
cmd.SetGlobalVectorArray(AdditionalShadowsConstantBuffer._AdditionalShadowParams, m_AdditionalLightIndexToShadowParams); // per-light data
cmd.SetGlobalMatrixArray(AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow, m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix); // per-shadow-slice data
}
ShadowUtils.GetScaleAndBiasForLinearDistanceFade(m_MaxShadowDistanceSq, m_CascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowFadeParams, new Vector4(shadowFadeScale, shadowFadeBias, 0, 0));
if (softShadows)
{
Vector2Int allocatedShadowAtlasSize = m_AdditionalLightsShadowmapHandle.referenceSize;
Vector2 invShadowAtlasSize = Vector2.one / allocatedShadowAtlasSize;
Vector2 invHalfShadowAtlasSize = invShadowAtlasSize * 0.5f;
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset0,
new Vector4(-invHalfShadowAtlasSize.x, -invHalfShadowAtlasSize.y,
invHalfShadowAtlasSize.x, -invHalfShadowAtlasSize.y));
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset1,
new Vector4(-invHalfShadowAtlasSize.x, invHalfShadowAtlasSize.y,
invHalfShadowAtlasSize.x, invHalfShadowAtlasSize.y));
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowmapSize, new Vector4(invShadowAtlasSize.x, invShadowAtlasSize.y,
allocatedShadowAtlasSize.x, allocatedShadowAtlasSize.y));
}
}
private class PassData
{
internal UniversalLightData lightData;
internal UniversalShadowData shadowData;
internal Matrix4x4 viewMatrix;
internal bool stripShadowsOffVariants;
internal AdditionalLightsShadowCasterPass pass;
internal TextureHandle shadowmapTexture;
internal int shadowmapID;
internal bool useStructuredBuffer;
internal bool emptyShadowmap;
internal RendererListHandle[] shadowRendererListsHdl = new RendererListHandle[ShaderOptions.k_MaxVisibleLightCountDesktop];
internal RendererList[] shadowRendererLists = new RendererList[ShaderOptions.k_MaxVisibleLightCountDesktop];
}
private void InitPassData(ref PassData passData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
{
passData.pass = this;
passData.lightData = lightData;
passData.shadowData = shadowData;
passData.viewMatrix = cameraData.GetViewMatrix();
passData.stripShadowsOffVariants = cameraData.renderer.stripShadowsOffVariants;
passData.emptyShadowmap = m_CreateEmptyShadowmap;
passData.shadowmapID = m_AdditionalLightsShadowmapID;
passData.useStructuredBuffer = m_UseStructuredBuffer;
}
void InitEmptyPassData(ref PassData passData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
{
passData.pass = this;
passData.lightData = lightData;
passData.shadowData = shadowData;
passData.stripShadowsOffVariants = cameraData.renderer.stripShadowsOffVariants;
passData.emptyShadowmap = m_CreateEmptyShadowmap;
passData.shadowmapID = m_AdditionalLightsShadowmapID;
}
private void InitRendererLists(ref CullingResults cullResults, ref PassData passData, ScriptableRenderContext context, RenderGraph renderGraph, bool useRenderGraph)
{
if (!m_CreateEmptyShadowmap)
{
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < m_ShadowSliceToAdditionalLightIndex.Count; ++globalShadowSliceIndex)
{
int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
ShadowSliceData shadowSliceData = m_AdditionalLightsShadowSlices[globalShadowSliceIndex];
int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
var settings = new ShadowDrawingSettings(cullResults, visibleLightIndex);
settings.useRenderingLayerMaskTest = UniversalRenderPipeline.asset.useRenderingLayers;
if(useRenderGraph)
passData.shadowRendererListsHdl[globalShadowSliceIndex] = renderGraph.CreateShadowRendererList(ref settings);
else
passData.shadowRendererLists[globalShadowSliceIndex] = context.CreateShadowRendererList(ref settings);
}
}
}
internal TextureHandle Render(RenderGraph graph, ContextContainer frameData)
{
TextureHandle shadowTexture;
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
using (var builder = graph.AddRasterRenderPass<PassData>("Additional Lights Shadowmap", out var passData, base.profilingSampler))
{
InitPassData(ref passData, cameraData, lightData, shadowData);
InitRendererLists(ref renderingData.cullResults, ref passData, default(ScriptableRenderContext), graph, true);
if (!m_CreateEmptyShadowmap)
{
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < m_ShadowSliceToAdditionalLightIndex.Count; ++globalShadowSliceIndex)
{
builder.UseRendererList(passData.shadowRendererListsHdl[globalShadowSliceIndex]);
}
shadowTexture = UniversalRenderer.CreateRenderGraphTexture(graph, m_AdditionalLightsShadowmapHandle.rt.descriptor, "_AdditionalLightsShadowmapTexture", true, ShadowUtils.m_ForceShadowPointSampling ? FilterMode.Point : FilterMode.Bilinear);
builder.SetRenderAttachmentDepth(shadowTexture, AccessFlags.Write);
}
else
{
shadowTexture = graph.defaultResources.defaultShadowTexture;
}
// RENDERGRAPH TODO: Need this as shadowmap is only used as Global Texture and not a buffer, so would get culled by RG
builder.AllowPassCulling(false);
builder.AllowGlobalStateModification(true);
if (shadowTexture.IsValid())
builder.SetGlobalTextureAfterPass(shadowTexture, passData.shadowmapID);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
if (!data.emptyShadowmap)
data.pass.RenderAdditionalShadowmapAtlas(context.cmd, ref data, true);
else
data.pass.SetEmptyAdditionalShadowmapAtlas(context.cmd);
});
return shadowTexture;
}
}
}
}