A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEngine.Rendering.RenderGraphModule;
5using UnityEditor;
6
7namespace UnityEngine.Rendering
8{
9 /// <summary>
10 /// Modes for Debugging Probes
11 /// </summary>
12 [GenerateHLSL]
13 public enum DebugProbeShadingMode
14 {
15 /// <summary>
16 /// Based on Spherical Harmonics
17 /// </summary>
18 SH,
19 /// <summary>
20 /// Based on Spherical Harmonics first band only (ambient)
21 /// </summary>
22 SHL0,
23 /// <summary>
24 /// Based on Spherical Harmonics band zero and one only
25 /// </summary>
26 SHL0L1,
27 /// <summary>
28 /// Based on validity
29 /// </summary>
30 Validity,
31 /// <summary>
32 /// Based on validity over a dilation threshold
33 /// </summary>
34 ValidityOverDilationThreshold,
35 /// <summary>
36 /// Based on rendering layer masks
37 /// </summary>
38 RenderingLayerMasks,
39 /// <summary>
40 /// Show in red probes that have been made invalid by adjustment volumes. Important to note that this debug view will only show result for volumes still present in the scene.
41 /// </summary>
42 InvalidatedByAdjustmentVolumes,
43 /// <summary>
44 /// Based on size
45 /// </summary>
46 Size,
47 /// <summary>
48 /// Based on spherical harmonics sky occlusion
49 /// </summary>
50 SkyOcclusionSH,
51 /// <summary>
52 /// Based on shading direction
53 /// </summary>
54 SkyDirection,
55 /// <summary>
56 /// Occlusion values per probe
57 /// </summary>
58 ProbeOcclusion,
59 }
60
61 enum ProbeSamplingDebugUpdate
62 {
63 Never,
64 Once,
65 Always
66 }
67
68 class ProbeSamplingDebugData
69 {
70 public ProbeSamplingDebugUpdate update = ProbeSamplingDebugUpdate.Never; // When compute buffer should be updated
71 public Vector2 coordinates = new Vector2(0.5f, 0.5f);
72 public bool forceScreenCenterCoordinates = false; // use screen center instead of mouse position
73 public Camera camera = null; // useful in editor when multiple scene tabs are opened
74 public bool shortcutPressed = false;
75 public GraphicsBuffer positionNormalBuffer; // buffer storing position and normal
76 }
77
78 class ProbeVolumeDebug : IDebugData
79 {
80 public bool drawProbes;
81 public bool drawBricks;
82 public bool drawCells;
83 public bool realtimeSubdivision;
84 public int subdivisionCellUpdatePerFrame = 4;
85 public float subdivisionDelayInSeconds = 1;
86 public DebugProbeShadingMode probeShading;
87 public float probeSize = 0.3f;
88 public float subdivisionViewCullingDistance = 500.0f;
89 public float probeCullingDistance = 200.0f;
90 public int maxSubdivToVisualize = ProbeBrickIndex.kMaxSubdivisionLevels;
91 public int minSubdivToVisualize = 0;
92 public float exposureCompensation;
93 public bool drawProbeSamplingDebug = false;
94 public float probeSamplingDebugSize = 0.3f;
95 public bool debugWithSamplingNoise = false;
96 public uint samplingRenderingLayer;
97 public bool drawVirtualOffsetPush;
98 public float offsetSize = 0.025f;
99 public bool freezeStreaming;
100 public bool displayCellStreamingScore;
101 public bool displayIndexFragmentation;
102 public int otherStateIndex = 0;
103 public bool verboseStreamingLog;
104 public bool debugStreaming = false;
105 public bool autoDrawProbes = true;
106 public bool isolationProbeDebug = true;
107 public byte visibleLayers;
108
109 // NOTE: We should get that from the camera directly instead of storing it as static
110 // But we can't access the volume parameters as they are specific to the RP
111 public static Vector3 currentOffset;
112
113 static internal int s_ActiveAdjustmentVolumes = 0;
114
115 public ProbeVolumeDebug()
116 {
117 Init();
118 }
119
120 void Init()
121 {
122 drawProbes = false;
123 drawBricks = false;
124 drawCells = false;
125 realtimeSubdivision = false;
126 subdivisionCellUpdatePerFrame = 4;
127 subdivisionDelayInSeconds = 1;
128 probeShading = DebugProbeShadingMode.SH;
129 probeSize = 0.3f;
130 subdivisionViewCullingDistance = 500.0f;
131 probeCullingDistance = 200.0f;
132 maxSubdivToVisualize = ProbeBrickIndex.kMaxSubdivisionLevels;
133 minSubdivToVisualize = 0;
134 exposureCompensation = 0.0f;
135 drawProbeSamplingDebug = false;
136 probeSamplingDebugSize = 0.3f;
137 drawVirtualOffsetPush = false;
138 offsetSize = 0.025f;
139 freezeStreaming = false;
140 displayCellStreamingScore = false;
141 displayIndexFragmentation = false;
142 otherStateIndex = 0;
143 autoDrawProbes = true;
144 isolationProbeDebug = true;
145 visibleLayers = 0xFF;
146 }
147
148 public Action GetReset() => () => Init();
149 }
150
151
152#if UNITY_EDITOR
153 [UnityEditor.InitializeOnLoad]
154#endif
155 internal class ProbeVolumeDebugColorPreferences
156 {
157 internal static Func<Color> GetDetailSubdivisionColor;
158 internal static Func<Color> GetMediumSubdivisionColor;
159 internal static Func<Color> GetLowSubdivisionColor;
160 internal static Func<Color> GetVeryLowSubdivisionColor;
161 internal static Func<Color> GetSparseSubdivisionColor;
162 internal static Func<Color> GetSparsestSubdivisionColor;
163
164 internal static Color s_DetailSubdivision = new Color32(135, 35, 255, 255);
165 internal static Color s_MediumSubdivision = new Color32(54, 208, 228, 255);
166 internal static Color s_LowSubdivision = new Color32(255, 100, 45, 255);
167 internal static Color s_VeryLowSubdivision = new Color32(52, 87, 255, 255);
168 internal static Color s_SparseSubdivision = new Color32(255, 71, 97, 255);
169 internal static Color s_SparsestSubdivision = new Color32(200, 227, 39, 255);
170
171 static ProbeVolumeDebugColorPreferences()
172 {
173#if UNITY_EDITOR
174 GetDetailSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 0 Subdivision", s_DetailSubdivision);
175 GetMediumSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 1 Subdivision", s_MediumSubdivision);
176 GetLowSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 2 Subdivision", s_LowSubdivision);
177 GetVeryLowSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 3 Subdivision", s_VeryLowSubdivision);
178 GetSparseSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 4 Subdivision", s_SparseSubdivision);
179 GetSparsestSubdivisionColor = CoreRenderPipelinePreferences.RegisterPreferenceColor("Adaptive Probe Volumes/Level 5 Subdivision", s_SparsestSubdivision);
180#endif
181 }
182
183 }
184 public partial class ProbeReferenceVolume
185 {
186 internal class CellInstancedDebugProbes
187 {
188 public List<Matrix4x4[]> probeBuffers;
189 public List<Matrix4x4[]> offsetBuffers;
190 public List<MaterialPropertyBlock> props;
191 }
192
193 const int kProbesPerBatch = 511;
194
195 /// <summary>Name of debug panel for Probe Volume</summary>
196 public static readonly string k_DebugPanelName = "Probe Volumes";
197
198 internal ProbeVolumeDebug probeVolumeDebug { get; } = new ProbeVolumeDebug();
199
200 /// <summary>Colors that can be used for debug visualization of the brick structure subdivision.</summary>
201 public Color[] subdivisionDebugColors { get; } = new Color[ProbeBrickIndex.kMaxSubdivisionLevels];
202
203 Mesh m_DebugMesh;
204 Mesh debugMesh {
205 get
206 {
207 if (m_DebugMesh == null)
208 {
209 m_DebugMesh = DebugShapes.instance.BuildCustomSphereMesh(0.5f, 9, 8); // (longSubdiv + 1) * latSubdiv + 2 = 82
210 m_DebugMesh.bounds = new Bounds(Vector3.zero, Vector3.one * 9999999.9f); // dirty way of disabling culling (objects spawned at (0.0, 0.0, 0.0) but vertices moved in vertex shader)
211 }
212 return m_DebugMesh;
213 }
214 }
215
216 DebugUI.Widget[] m_DebugItems;
217 Material m_DebugMaterial;
218
219 // Sample position debug
220 Mesh m_DebugProbeSamplingMesh; // mesh with 8 quads, 1 arrow and 2 locators
221 Material m_ProbeSamplingDebugMaterial; // Used to draw probe sampling information (quad with weight, arrow, locator)
222 Material m_ProbeSamplingDebugMaterial02; // Used to draw probe sampling information (shaded probes)
223
224 Texture m_DisplayNumbersTexture;
225
226 internal static ProbeSamplingDebugData probeSamplingDebugData = new ProbeSamplingDebugData();
227
228 Mesh m_DebugOffsetMesh;
229 Material m_DebugOffsetMaterial;
230 Material m_DebugFragmentationMaterial;
231 Plane[] m_DebugFrustumPlanes = new Plane[6];
232
233 // Scenario blending debug data
234 GUIContent[] m_DebugScenarioNames = new GUIContent[0];
235 int[] m_DebugScenarioValues = new int[0];
236 string m_DebugActiveSceneGUID, m_DebugActiveScenario;
237 DebugUI.EnumField m_DebugScenarioField;
238
239 // Field used for the realtime subdivision preview
240 internal Dictionary<Bounds, ProbeBrickIndex.Brick[]> realtimeSubdivisionInfo = new ();
241
242 bool m_MaxSubdivVisualizedIsMaxAvailable = false;
243
244 /// <summary>
245 /// Obsolete. Render Probe Volume related debug
246 /// </summary>
247 /// <param name="camera">The <see cref="Camera"/></param>
248 /// <param name="exposureTexture">Texture containing the exposure value for this frame.</param>
249 [Obsolete("Use the other override to support sampling offset in debug modes.")]
250 public void RenderDebug(Camera camera, Texture exposureTexture)
251 {
252 RenderDebug(camera, null, exposureTexture);
253 }
254
255 /// <summary>
256 /// Render Probe Volume related debug
257 /// </summary>
258 /// <param name="camera">The <see cref="Camera"/></param>
259 /// <param name="options">Options coming from the volume stack.</param>
260 /// <param name="exposureTexture">Texture containing the exposure value for this frame.</param>
261 public void RenderDebug(Camera camera, ProbeVolumesOptions options, Texture exposureTexture)
262 {
263 if (camera.cameraType != CameraType.Reflection && camera.cameraType != CameraType.Preview)
264 {
265 if (options != null)
266 ProbeVolumeDebug.currentOffset = options.worldOffset.value;
267
268 DrawProbeDebug(camera, exposureTexture);
269 }
270 }
271
272 /// <summary>
273 /// Checks if APV sampling debug is enabled
274 /// </summary>
275 /// <returns>True if APV sampling debug is enabled</returns>
276 public bool IsProbeSamplingDebugEnabled()
277 {
278 return probeSamplingDebugData.update != ProbeSamplingDebugUpdate.Never;
279 }
280
281 /// <summary>
282 /// Returns the resources used for APV probe sampling debug mode
283 /// </summary>
284 /// <param name="camera">The camera for which to evaluate the debug mode</param>
285 /// <param name="resultBuffer">The buffer that should be filled with position and normal</param>
286 /// <param name="coords">The screen space coords to sample the position and normal</param>
287 /// <returns>True if the pipeline should write position and normal at coords in resultBuffer</returns>
288 public bool GetProbeSamplingDebugResources(Camera camera, out GraphicsBuffer resultBuffer, out Vector2 coords)
289 {
290 resultBuffer = probeSamplingDebugData.positionNormalBuffer;
291 coords = probeSamplingDebugData.coordinates;
292
293 if (!probeVolumeDebug.drawProbeSamplingDebug)
294 return false;
295
296#if UNITY_EDITOR
297 if (probeSamplingDebugData.camera != camera)
298 return false;
299#endif
300
301 if (probeSamplingDebugData.update == ProbeSamplingDebugUpdate.Never)
302 return false;
303
304 if (probeSamplingDebugData.update == ProbeSamplingDebugUpdate.Once)
305 {
306 probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Never;
307 probeSamplingDebugData.forceScreenCenterCoordinates = false;
308 }
309
310 return true;
311 }
312
313#if UNITY_EDITOR
314 static void SceneGUI(SceneView sceneView)
315 {
316 // APV debug needs to detect user keyboard and mouse position to update ProbeSamplingPositionDebug
317 Event e = Event.current;
318
319 if (e.control && !ProbeReferenceVolume.probeSamplingDebugData.shortcutPressed)
320 ProbeReferenceVolume.probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Always;
321
322 if (!e.control && ProbeReferenceVolume.probeSamplingDebugData.shortcutPressed)
323 ProbeReferenceVolume.probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Never;
324
325 ProbeReferenceVolume.probeSamplingDebugData.shortcutPressed = e.control;
326
327 if (e.clickCount > 0 && e.button == 0)
328 {
329 if (ProbeReferenceVolume.probeSamplingDebugData.shortcutPressed)
330 ProbeReferenceVolume.probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Once;
331 else
332 ProbeReferenceVolume.probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Never;
333 }
334
335 if (ProbeReferenceVolume.probeSamplingDebugData.update == ProbeSamplingDebugUpdate.Never)
336 return;
337
338 Vector2 screenCoordinates;
339
340 if (ProbeReferenceVolume.probeSamplingDebugData.forceScreenCenterCoordinates)
341 screenCoordinates = new Vector2(sceneView.camera.scaledPixelWidth / 2.0f, sceneView.camera.scaledPixelHeight / 2.0f);
342 else
343 screenCoordinates = HandleUtility.GUIPointToScreenPixelCoordinate(e.mousePosition);
344
345 if (screenCoordinates.x < 0 || screenCoordinates.x > sceneView.camera.scaledPixelWidth || screenCoordinates.y < 0 || screenCoordinates.y > sceneView.camera.scaledPixelHeight)
346 return;
347
348 ProbeReferenceVolume.probeSamplingDebugData.camera = sceneView.camera;
349 ProbeReferenceVolume.probeSamplingDebugData.coordinates = screenCoordinates;
350
351 if (e.type != EventType.Repaint && e.type != EventType.Layout)
352 {
353 var go = HandleUtility.PickGameObject(e.mousePosition, false);
354 if (go != null && go.TryGetComponent<MeshRenderer>(out var renderer))
355 instance.probeVolumeDebug.samplingRenderingLayer = renderer.renderingLayerMask;
356 }
357
358 SceneView.currentDrawingSceneView.Repaint(); // useful when 'Always Refresh' is not toggled
359 }
360#endif
361
362 bool TryCreateDebugRenderData()
363 {
364 if (!GraphicsSettings.TryGetRenderPipelineSettings<ProbeVolumeDebugResources>(out var debugResources))
365 return false;
366
367#if !UNITY_EDITOR // On non editor builds, we need to check if the standalone build contains debug shaders
368 if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderStrippingSetting) && shaderStrippingSetting.stripRuntimeDebugShaders)
369 return false;
370#endif
371 m_DebugMaterial = CoreUtils.CreateEngineMaterial(debugResources.probeVolumeDebugShader);
372 m_DebugMaterial.enableInstancing = true;
373
374 // Probe Sampling Debug Mesh : useful to show additional information concerning probe sampling for a specific fragment
375 // - Arrow : Debug fragment position and normal
376 // - Locator : Debug sampling position
377 // - 8 Quads : Debug probes weights
378 m_DebugProbeSamplingMesh = debugResources.probeSamplingDebugMesh;
379 m_DebugProbeSamplingMesh.bounds = new Bounds(Vector3.zero, Vector3.one * 9999999.9f); // dirty way of disabling culling (objects spawned at (0.0, 0.0, 0.0) but vertices moved in vertex shader)
380 m_ProbeSamplingDebugMaterial = CoreUtils.CreateEngineMaterial(debugResources.probeVolumeSamplingDebugShader);
381 m_ProbeSamplingDebugMaterial02 = CoreUtils.CreateEngineMaterial(debugResources.probeVolumeDebugShader);
382 m_ProbeSamplingDebugMaterial02.enableInstancing = true;
383
384 probeSamplingDebugData.positionNormalBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 2, System.Runtime.InteropServices.Marshal.SizeOf(typeof(Vector4)));
385
386 m_DisplayNumbersTexture = debugResources.numbersDisplayTex;
387
388 m_DebugOffsetMesh = Resources.GetBuiltinResource<Mesh>("pyramid.fbx");
389 m_DebugOffsetMesh.bounds = new Bounds(Vector3.zero, Vector3.one * 9999999.9f); // dirty way of disabling culling (objects spawned at (0.0, 0.0, 0.0) but vertices moved in vertex shader)
390 m_DebugOffsetMaterial = CoreUtils.CreateEngineMaterial(debugResources.probeVolumeOffsetDebugShader);
391 m_DebugOffsetMaterial.enableInstancing = true;
392 m_DebugFragmentationMaterial = CoreUtils.CreateEngineMaterial(debugResources.probeVolumeFragmentationDebugShader);
393
394 // Hard-coded colors for now.
395 Debug.Assert(ProbeBrickIndex.kMaxSubdivisionLevels == 7); // Update list if this changes.
396
397 subdivisionDebugColors[0] = ProbeVolumeDebugColorPreferences.s_DetailSubdivision;
398 subdivisionDebugColors[1] = ProbeVolumeDebugColorPreferences.s_MediumSubdivision;
399 subdivisionDebugColors[2] = ProbeVolumeDebugColorPreferences.s_LowSubdivision;
400 subdivisionDebugColors[3] = ProbeVolumeDebugColorPreferences.s_VeryLowSubdivision;
401 subdivisionDebugColors[4] = ProbeVolumeDebugColorPreferences.s_SparseSubdivision;
402 subdivisionDebugColors[5] = ProbeVolumeDebugColorPreferences.s_SparsestSubdivision;
403 subdivisionDebugColors[6] = ProbeVolumeDebugColorPreferences.s_DetailSubdivision;
404
405 return true;
406 }
407
408 void InitializeDebug()
409 {
410#if UNITY_EDITOR
411 SceneView.duringSceneGui += SceneGUI; // Used to get click and keyboard event on scene view for Probe Sampling Debug
412#endif
413 if (TryCreateDebugRenderData())
414 RegisterDebug();
415
416#if UNITY_EDITOR
417 UnityEditor.Lightmapping.lightingDataCleared += OnClearLightingdata;
418#endif
419 }
420
421 void CleanupDebug()
422 {
423 UnregisterDebug(true);
424 CoreUtils.Destroy(m_DebugMaterial);
425 CoreUtils.Destroy(m_ProbeSamplingDebugMaterial);
426 CoreUtils.Destroy(m_ProbeSamplingDebugMaterial02);
427 CoreUtils.Destroy(m_DebugOffsetMaterial);
428 CoreUtils.Destroy(m_DebugFragmentationMaterial);
429 CoreUtils.SafeRelease(probeSamplingDebugData?.positionNormalBuffer);
430
431#if UNITY_EDITOR
432 UnityEditor.Lightmapping.lightingDataCleared -= OnClearLightingdata;
433 SceneView.duringSceneGui -= SceneGUI;
434#endif
435 }
436
437 void DebugCellIndexChanged<T>(DebugUI.Field<T> field, T value)
438 {
439 ClearDebugData();
440 }
441
442 void RegisterDebug()
443 {
444 void RefreshDebug<T>(DebugUI.Field<T> field, T value)
445 {
446 UnregisterDebug(false);
447 RegisterDebug();
448 }
449
450 const float kProbeSizeMin = 0.05f, kProbeSizeMax = 10.0f;
451 const float kOffsetSizeMin = 0.001f, kOffsetSizeMax = 0.1f;
452
453 var widgetList = new List<DebugUI.Widget>();
454
455 widgetList.Add(new DebugUI.RuntimeDebugShadersMessageBox());
456
457 var subdivContainer = new DebugUI.Container()
458 {
459 displayName = "Subdivision Visualization",
460 isHiddenCallback = () =>
461 {
462#if UNITY_EDITOR
463 return false;
464#else
465 return false; // Cells / Bricks visualization is not implemented in a runtime compatible way atm.
466#endif
467 }
468 };
469 subdivContainer.children.Add(new DebugUI.BoolField
470 {
471 displayName = "Display Cells",
472 tooltip = "Draw Cells used for loading and streaming.",
473 getter = () => probeVolumeDebug.drawCells,
474 setter = value => probeVolumeDebug.drawCells = value,
475 onValueChanged = RefreshDebug
476 });
477 subdivContainer.children.Add(new DebugUI.BoolField
478 {
479 displayName = "Display Bricks",
480 tooltip = "Display Subdivision bricks.",
481 getter = () => probeVolumeDebug.drawBricks,
482 setter = value => probeVolumeDebug.drawBricks = value,
483 onValueChanged = RefreshDebug
484 });
485
486 subdivContainer.children.Add(new DebugUI.FloatField { displayName = "Debug Draw Distance", tooltip = "How far from the Scene Camera to draw debug visualization for Cells and Bricks. Large distances can impact Editor performance.", getter = () => probeVolumeDebug.subdivisionViewCullingDistance, setter = value => probeVolumeDebug.subdivisionViewCullingDistance = value, min = () => 0.0f });
487 widgetList.Add(subdivContainer);
488
489#if UNITY_EDITOR
490 var subdivPreviewContainer = new DebugUI.Container()
491 {
492 displayName = "Subdivision Preview",
493 isHiddenCallback = () =>
494 {
495 return (!probeVolumeDebug.drawCells && !probeVolumeDebug.drawBricks);
496 }
497 };
498 subdivPreviewContainer.children.Add(new DebugUI.BoolField
499 {
500 displayName = "Live Updates",
501 tooltip = "Enable a preview of Adaptive Probe Volumes data in the Scene without baking. Can impact Editor performance.",
502 getter = () => probeVolumeDebug.realtimeSubdivision,
503 setter = value => probeVolumeDebug.realtimeSubdivision = value,
504 });
505
506 var realtimeSubdivisonChildContainer = new DebugUI.Container()
507 {
508 isHiddenCallback = () => !probeVolumeDebug.realtimeSubdivision
509 };
510 realtimeSubdivisonChildContainer.children.Add(new DebugUI.IntField { displayName = "Cell Updates Per Frame", tooltip = "The number of Cells, bricks, and probe positions updated per frame. Higher numbers can impact Editor performance.", getter = () => probeVolumeDebug.subdivisionCellUpdatePerFrame, setter = value => probeVolumeDebug.subdivisionCellUpdatePerFrame = value, min = () => 1, max = () => 100 });
511 realtimeSubdivisonChildContainer.children.Add(new DebugUI.FloatField { displayName = "Update Frequency", tooltip = "Delay in seconds between updates to Cell, Brick, and Probe positions if Live Subdivision Preview is enabled.", getter = () => probeVolumeDebug.subdivisionDelayInSeconds, setter = value => probeVolumeDebug.subdivisionDelayInSeconds = value, min = () => 0.1f, max = () => 10 });
512 subdivPreviewContainer.children.Add(realtimeSubdivisonChildContainer);
513 widgetList.Add(subdivPreviewContainer);
514#endif
515
516 widgetList.Add(new DebugUI.RuntimeDebugShadersMessageBox());
517
518 var probeContainer = new DebugUI.Container()
519 {
520 displayName = "Probe Visualization"
521 };
522
523 probeContainer.children.Add(new DebugUI.BoolField { displayName = "Display Probes", tooltip = "Render the debug view showing probe positions. Use the shading mode to determine which type of lighting data to visualize.", getter = () => probeVolumeDebug.drawProbes, setter = value => probeVolumeDebug.drawProbes = value, onValueChanged = RefreshDebug });
524 {
525 var probeContainerChildren = new DebugUI.Container()
526 {
527 isHiddenCallback = () => !probeVolumeDebug.drawProbes
528 };
529
530 probeContainerChildren.children.Add(new DebugUI.EnumField
531 {
532 displayName = "Probe Shading Mode",
533 tooltip = "Choose which lighting data to show in the probe debug visualization.",
534 getter = () => (int)probeVolumeDebug.probeShading,
535 setter = value => probeVolumeDebug.probeShading = (DebugProbeShadingMode)value,
536 autoEnum = typeof(DebugProbeShadingMode),
537 getIndex = () => (int)probeVolumeDebug.probeShading,
538 setIndex = value => probeVolumeDebug.probeShading = (DebugProbeShadingMode)value,
539 });
540 probeContainerChildren.children.Add(new DebugUI.FloatField { displayName = "Debug Size", tooltip = "The size of probes shown in the debug view.", getter = () => probeVolumeDebug.probeSize, setter = value => probeVolumeDebug.probeSize = value, min = () => kProbeSizeMin, max = () => kProbeSizeMax });
541
542 var exposureCompensation = new DebugUI.FloatField
543 {
544 displayName = "Exposure Compensation",
545 tooltip = "Modify the brightness of probe visualizations. Decrease this number to make very bright probes more visible.",
546 getter = () => probeVolumeDebug.exposureCompensation,
547 setter = value => probeVolumeDebug.exposureCompensation = value,
548 isHiddenCallback = () =>
549 {
550 return probeVolumeDebug.probeShading switch
551 {
552 DebugProbeShadingMode.SH => false,
553 DebugProbeShadingMode.SHL0 => false,
554 DebugProbeShadingMode.SHL0L1 => false,
555 DebugProbeShadingMode.SkyOcclusionSH => false,
556 DebugProbeShadingMode.SkyDirection => false,
557 DebugProbeShadingMode.ProbeOcclusion => false,
558 _ => true
559 };
560 }
561 };
562 probeContainerChildren.children.Add(exposureCompensation);
563
564 probeContainerChildren.children.Add(new DebugUI.IntField
565 {
566 displayName = "Max Subdivisions Displayed",
567 tooltip = "The highest (most dense) probe subdivision level displayed in the debug view.",
568 getter = () => probeVolumeDebug.maxSubdivToVisualize,
569 setter = (v) => probeVolumeDebug.maxSubdivToVisualize = Mathf.Max(0, Mathf.Min(v, GetMaxSubdivision() - 1)),
570 min = () => 0,
571 max = () => Mathf.Max(0, GetMaxSubdivision() - 1),
572 });
573
574 probeContainerChildren.children.Add(new DebugUI.IntField
575 {
576 displayName = "Min Subdivisions Displayed",
577 tooltip = "The lowest (least dense) probe subdivision level displayed in the debug view.",
578 getter = () => probeVolumeDebug.minSubdivToVisualize,
579 setter = (v) => probeVolumeDebug.minSubdivToVisualize = Mathf.Max(v, 0),
580 min = () => 0,
581 max = () => Mathf.Max(0, GetMaxSubdivision() - 1),
582 });
583
584 probeContainer.children.Add(probeContainerChildren);
585 }
586
587 probeContainer.children.Add(new DebugUI.BoolField
588 {
589 displayName = "Debug Probe Sampling",
590 tooltip = "Render the debug view displaying how probes are sampled for a selected pixel. Use the viewport overlay 'SelectPixel' button or Ctrl+Click on the viewport to select the debugged pixel",
591 getter = () => probeVolumeDebug.drawProbeSamplingDebug,
592 setter = value =>
593 {
594 probeVolumeDebug.drawProbeSamplingDebug = value;
595 probeSamplingDebugData.update = ProbeSamplingDebugUpdate.Once;
596 probeSamplingDebugData.forceScreenCenterCoordinates = true;
597 },
598 });
599
600 var drawProbeSamplingDebugChildren = new DebugUI.Container()
601 {
602 isHiddenCallback = () => !probeVolumeDebug.drawProbeSamplingDebug
603 };
604
605 drawProbeSamplingDebugChildren.children.Add(new DebugUI.FloatField { displayName = "Debug Size", tooltip = "The size of gizmos shown in the debug view.", getter = () => probeVolumeDebug.probeSamplingDebugSize, setter = value => probeVolumeDebug.probeSamplingDebugSize = value, min = () => kProbeSizeMin, max = () => kProbeSizeMax });
606 drawProbeSamplingDebugChildren.children.Add(new DebugUI.BoolField { displayName = "Debug With Sampling Noise", tooltip = "Enable Sampling Noise for this debug view. It should be enabled for accuracy but it can make results more difficult to read", getter = () => probeVolumeDebug.debugWithSamplingNoise, setter = value => { probeVolumeDebug.debugWithSamplingNoise = value; }, onValueChanged = RefreshDebug });
607 probeContainer.children.Add(drawProbeSamplingDebugChildren);
608
609 probeContainer.children.Add(new DebugUI.BoolField
610 {
611 displayName = "Virtual Offset Debug",
612 tooltip = "Enable Virtual Offset debug visualization. Indicates the offsets applied to probe positions. These are used to capture lighting when probes are considered invalid.",
613 getter = () => probeVolumeDebug.drawVirtualOffsetPush,
614 setter = value =>
615 {
616 probeVolumeDebug.drawVirtualOffsetPush = value;
617
618 if (probeVolumeDebug.drawVirtualOffsetPush && probeVolumeDebug.drawProbes && m_CurrentBakingSet != null)
619 {
620 // If probes are being drawn when enabling offset, automatically scale them down to a reasonable size so the arrows aren't obscured by the probes.
621 var searchDistance = CellSize(0) * MinBrickSize() / ProbeBrickPool.kBrickCellCount * m_CurrentBakingSet.settings.virtualOffsetSettings.searchMultiplier + m_CurrentBakingSet.settings.virtualOffsetSettings.outOfGeoOffset;
622 probeVolumeDebug.probeSize = Mathf.Min(probeVolumeDebug.probeSize, Mathf.Clamp(searchDistance, kProbeSizeMin, kProbeSizeMax));
623 }
624 }
625 });
626
627 var drawVirtualOffsetDebugChildren = new DebugUI.Container()
628 {
629 isHiddenCallback = () => !probeVolumeDebug.drawVirtualOffsetPush
630 };
631
632 var voOffset = new DebugUI.FloatField
633 {
634 displayName = "Debug Size",
635 tooltip = "Modify the size of the arrows used in the virtual offset debug visualization.",
636 getter = () => probeVolumeDebug.offsetSize,
637 setter = value => probeVolumeDebug.offsetSize = value,
638 min = () => kOffsetSizeMin,
639 max = () => kOffsetSizeMax,
640 isHiddenCallback = () => !probeVolumeDebug.drawVirtualOffsetPush
641 };
642 drawVirtualOffsetDebugChildren.children.Add(voOffset);
643 probeContainer.children.Add(drawVirtualOffsetDebugChildren);
644
645 probeContainer.children.Add(new DebugUI.FloatField { displayName = "Debug Draw Distance", tooltip = "How far from the Scene Camera to draw probe debug visualizations. Large distances can impact Editor performance.", getter = () => probeVolumeDebug.probeCullingDistance, setter = value => probeVolumeDebug.probeCullingDistance = value, min = () => 0.0f });
646 widgetList.Add(probeContainer);
647
648 var adjustmentContainer = new DebugUI.Container()
649 {
650 displayName = "Probe Adjustment Volumes"
651 };
652 adjustmentContainer.children.Add(new DebugUI.BoolField
653 {
654 displayName = "Auto Display Probes",
655 tooltip = "When enabled and a Probe Adjustment Volumes is selected, automatically display the probes.",
656 getter = () => probeVolumeDebug.autoDrawProbes,
657 setter = value => probeVolumeDebug.autoDrawProbes = value,
658 onValueChanged = RefreshDebug
659 });
660 adjustmentContainer.children.Add(new DebugUI.BoolField
661 {
662 displayName = "Isolate Affected",
663 tooltip = "When enabled, only displayed probes in the influence of the currently selected Probe Adjustment Volumes.",
664 getter = () => probeVolumeDebug.isolationProbeDebug,
665 setter = value => probeVolumeDebug.isolationProbeDebug = value,
666 onValueChanged = RefreshDebug
667 });
668 widgetList.Add(adjustmentContainer);
669
670 var streamingContainer = new DebugUI.Container()
671 {
672 displayName = "Streaming",
673 isHiddenCallback = () => !(gpuStreamingEnabled || diskStreamingEnabled)
674 };
675 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Freeze Streaming", tooltip = "Stop Unity from streaming probe data in or out of GPU memory.", getter = () => probeVolumeDebug.freezeStreaming, setter = value => probeVolumeDebug.freezeStreaming = value });
676 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Display Streaming Score", getter = () => probeVolumeDebug.displayCellStreamingScore, setter = value => probeVolumeDebug.displayCellStreamingScore = value });
677 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Maximum cell streaming", tooltip = "Enable streaming as many cells as possible every frame.", getter = () => instance.loadMaxCellsPerFrame, setter = value => instance.loadMaxCellsPerFrame = value});
678
679 var maxCellStreamingContainerChildren = new DebugUI.Container()
680 {
681 isHiddenCallback = () => instance.loadMaxCellsPerFrame
682 };
683 maxCellStreamingContainerChildren.children.Add(new DebugUI.IntField { displayName = "Loaded Cells Per Frame", tooltip = "Determines the maximum number of Cells Unity streams per frame. Loading more Cells per frame can impact performance.", getter = () => instance.numberOfCellsLoadedPerFrame, setter = value => instance.SetNumberOfCellsLoadedPerFrame(value), min = () => 1, max = () => kMaxCellLoadedPerFrame });
684 streamingContainer.children.Add(maxCellStreamingContainerChildren);
685
686 // Those are mostly for internal dev purpose.
687 if (Debug.isDebugBuild)
688 {
689 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Display Index Fragmentation", getter = () => probeVolumeDebug.displayIndexFragmentation, setter = value => probeVolumeDebug.displayIndexFragmentation = value });
690 var indexDefragContainerChildren = new DebugUI.Container()
691 {
692 isHiddenCallback = () => !probeVolumeDebug.displayIndexFragmentation
693 };
694 indexDefragContainerChildren.children.Add(new DebugUI.Value { displayName = "Index Fragmentation Rate", getter = () => instance.indexFragmentationRate });
695 streamingContainer.children.Add(indexDefragContainerChildren);
696 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Verbose Log", getter = () => probeVolumeDebug.verboseStreamingLog, setter = value => probeVolumeDebug.verboseStreamingLog = value });
697 streamingContainer.children.Add(new DebugUI.BoolField { displayName = "Debug Streaming", getter = () => probeVolumeDebug.debugStreaming, setter = value => probeVolumeDebug.debugStreaming = value });
698 }
699
700 widgetList.Add(streamingContainer);
701
702 if (supportScenarioBlending && m_CurrentBakingSet != null)
703 {
704 var blendingContainer = new DebugUI.Container() { displayName = "Scenario Blending" };
705 blendingContainer.children.Add(new DebugUI.IntField { displayName = "Number Of Cells Blended Per Frame", getter = () => instance.numberOfCellsBlendedPerFrame, setter = value => instance.numberOfCellsBlendedPerFrame = value, min = () => 0 });
706 blendingContainer.children.Add(new DebugUI.FloatField { displayName = "Turnover Rate", getter = () => instance.turnoverRate, setter = value => instance.turnoverRate = value, min = () => 0, max = () => 1 });
707
708 void RefreshScenarioNames(string guid)
709 {
710 HashSet<string> allScenarios = new();
711 foreach (var set in Resources.FindObjectsOfTypeAll<ProbeVolumeBakingSet>())
712 {
713 if (!set.sceneGUIDs.Contains(guid))
714 continue;
715 foreach (var scenario in set.lightingScenarios)
716 allScenarios.Add(scenario);
717 }
718
719 allScenarios.Remove(m_CurrentBakingSet.lightingScenario);
720 if (m_DebugActiveSceneGUID == guid && allScenarios.Count + 1 == m_DebugScenarioNames.Length && m_DebugActiveScenario == m_CurrentBakingSet.lightingScenario)
721 return;
722
723 int i = 0;
724 ArrayExtensions.ResizeArray(ref m_DebugScenarioNames, allScenarios.Count + 1);
725 ArrayExtensions.ResizeArray(ref m_DebugScenarioValues, allScenarios.Count + 1);
726 m_DebugScenarioNames[0] = new GUIContent("None");
727 m_DebugScenarioValues[0] = 0;
728 foreach (var scenario in allScenarios)
729 {
730 i++;
731 m_DebugScenarioNames[i] = new GUIContent(scenario);
732 m_DebugScenarioValues[i] = i;
733 }
734
735 m_DebugActiveSceneGUID = guid;
736 m_DebugActiveScenario = m_CurrentBakingSet.lightingScenario;
737 m_DebugScenarioField.enumNames = m_DebugScenarioNames;
738 m_DebugScenarioField.enumValues = m_DebugScenarioValues;
739 if (probeVolumeDebug.otherStateIndex >= m_DebugScenarioNames.Length)
740 probeVolumeDebug.otherStateIndex = 0;
741 }
742
743 m_DebugScenarioField = new DebugUI.EnumField
744 {
745 displayName = "Scenario Blend Target",
746 tooltip = "Select another lighting scenario to blend with the active lighting scenario.",
747 enumNames = m_DebugScenarioNames,
748 enumValues = m_DebugScenarioValues,
749 getIndex = () =>
750 {
751 if (m_CurrentBakingSet == null)
752 return 0;
753 RefreshScenarioNames(GetSceneGUID(SceneManagement.SceneManager.GetActiveScene()));
754
755 probeVolumeDebug.otherStateIndex = 0;
756 if (!string.IsNullOrEmpty(m_CurrentBakingSet.otherScenario))
757 {
758 for (int i = 1; i < m_DebugScenarioNames.Length; i++)
759 {
760 if (m_DebugScenarioNames[i].text == m_CurrentBakingSet.otherScenario)
761 {
762 probeVolumeDebug.otherStateIndex = i;
763 break;
764 }
765 }
766 }
767 return probeVolumeDebug.otherStateIndex;
768 },
769 setIndex = value =>
770 {
771 string other = value == 0 ? null : m_DebugScenarioNames[value].text;
772 m_CurrentBakingSet.BlendLightingScenario(other, m_CurrentBakingSet.scenarioBlendingFactor);
773 probeVolumeDebug.otherStateIndex = value;
774 },
775 getter = () => probeVolumeDebug.otherStateIndex,
776 setter = (value) => probeVolumeDebug.otherStateIndex = value,
777 };
778
779 blendingContainer.children.Add(m_DebugScenarioField);
780 blendingContainer.children.Add(new DebugUI.FloatField { displayName = "Scenario Blending Factor", tooltip = "Blend between lighting scenarios by adjusting this slider.", getter = () => instance.scenarioBlendingFactor, setter = value => instance.scenarioBlendingFactor = value, min = () => 0.0f, max = () => 1.0f });
781
782 widgetList.Add(blendingContainer);
783 }
784
785 if (widgetList.Count > 0)
786 {
787 m_DebugItems = widgetList.ToArray();
788 var panel = DebugManager.instance.GetPanel(k_DebugPanelName, true);
789 panel.children.Add(m_DebugItems);
790 }
791
792 DebugManager debugManager = DebugManager.instance;
793 debugManager.RegisterData(probeVolumeDebug);
794 }
795
796 void UnregisterDebug(bool destroyPanel)
797 {
798 if (destroyPanel)
799 DebugManager.instance.RemovePanel(k_DebugPanelName);
800 else
801 DebugManager.instance.GetPanel(k_DebugPanelName, false).children.Remove(m_DebugItems);
802 }
803
804 class RenderFragmentationOverlayPassData
805 {
806 public Material debugFragmentationMaterial;
807 public Rendering.DebugOverlay debugOverlay;
808 public int chunkCount;
809 public ComputeBuffer debugFragmentationData;
810 public TextureHandle colorBuffer;
811 public TextureHandle depthBuffer;
812 }
813
814 /// <summary>
815 /// Render a debug view showing fragmentation of the GPU memory.
816 /// </summary>
817 /// <param name="renderGraph">The RenderGraph responsible for executing this pass.</param>
818 /// <param name="colorBuffer">The color buffer where the overlay will be rendered.</param>
819 /// <param name="depthBuffer">The depth buffer used for depth-testing the overlay.</param>
820 /// <param name="debugOverlay">The debug overlay manager to orchestrate multiple overlays.</param>
821 public void RenderFragmentationOverlay(RenderGraph renderGraph, TextureHandle colorBuffer, TextureHandle depthBuffer, DebugOverlay debugOverlay)
822 {
823 if (!m_ProbeReferenceVolumeInit || !probeVolumeDebug.displayIndexFragmentation)
824 return;
825
826 using (var builder = renderGraph.AddRenderPass<RenderFragmentationOverlayPassData>("APVFragmentationOverlay", out var passData))
827 {
828 passData.debugOverlay = debugOverlay;
829 passData.debugFragmentationMaterial = m_DebugFragmentationMaterial;
830 passData.colorBuffer = builder.UseColorBuffer(colorBuffer, 0);
831 passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite);
832 passData.debugFragmentationData = m_Index.GetDebugFragmentationBuffer();
833 passData.chunkCount = passData.debugFragmentationData.count;
834
835 builder.SetRenderFunc(
836 (RenderFragmentationOverlayPassData data, RenderGraphContext ctx) =>
837 {
838 var mpb = ctx.renderGraphPool.GetTempMaterialPropertyBlock();
839
840 data.debugOverlay.SetViewport(ctx.cmd);
841 mpb.SetInt("_ChunkCount", data.chunkCount);
842 mpb.SetBuffer("_DebugFragmentation", data.debugFragmentationData);
843 ctx.cmd.DrawProcedural(Matrix4x4.identity, data.debugFragmentationMaterial, 0, MeshTopology.Triangles, 3, 1, mpb);
844 data.debugOverlay.Next();
845 });
846 }
847 }
848
849 bool ShouldCullCell(Vector3 cellPosition, Transform cameraTransform, Plane[] frustumPlanes)
850 {
851 var volumeAABB = GetCellBounds(cellPosition);
852 var cellSize = MaxBrickSize();
853
854 // We do coarse culling with cell, finer culling later.
855 float distanceRoundedUpWithCellSize = Mathf.CeilToInt(probeVolumeDebug.probeCullingDistance / cellSize) * cellSize;
856
857 if (Vector3.Distance(cameraTransform.position, volumeAABB.center) > distanceRoundedUpWithCellSize)
858 return true;
859
860 return !GeometryUtility.TestPlanesAABB(frustumPlanes, volumeAABB);
861 }
862
863 static Vector4[] s_BoundsArray = new Vector4[16 * 3];
864
865 static void UpdateDebugFromSelection(ref Vector4[] _AdjustmentVolumeBounds, ref int _AdjustmentVolumeCount)
866 {
867 if (ProbeVolumeDebug.s_ActiveAdjustmentVolumes == 0)
868 return;
869
870 #if UNITY_EDITOR
871 foreach (var touchup in Selection.GetFiltered<ProbeAdjustmentVolume>(SelectionMode.Unfiltered))
872 {
873 if (!touchup.isActiveAndEnabled) continue;
874
875 Volume volume = new Volume(Matrix4x4.TRS(touchup.transform.position, touchup.transform.rotation, touchup.GetExtents()), 0, 0);
876 volume.CalculateCenterAndSize(out Vector3 center, out var _);
877
878 if (touchup.shape == ProbeAdjustmentVolume.Shape.Sphere)
879 {
880 volume.Z.x = float.MaxValue;
881 volume.X.x = touchup.radius;
882 }
883 else
884 {
885 volume.X *= 0.5f;
886 volume.Y *= 0.5f;
887 volume.Z *= 0.5f;
888 }
889
890 _AdjustmentVolumeBounds[_AdjustmentVolumeCount * 3 + 0] = new Vector4(center.x, center.y, center.z, volume.Z.x);
891 _AdjustmentVolumeBounds[_AdjustmentVolumeCount * 3 + 1] = new Vector4(volume.X.x, volume.X.y, volume.X.z, volume.Z.y);
892 _AdjustmentVolumeBounds[_AdjustmentVolumeCount * 3 + 2] = new Vector4(volume.Y.x, volume.Y.y, volume.Y.z, volume.Z.z);
893
894 if (++_AdjustmentVolumeCount == 16)
895 break;
896 }
897 #endif
898 }
899
900 Bounds GetCellBounds(Vector3 cellPosition)
901 {
902 var cellSize = MaxBrickSize();
903 var cellOffset = ProbeOffset() + ProbeVolumeDebug.currentOffset;
904 Vector3 cellCenterWS = cellOffset + cellPosition * cellSize + Vector3.one * (cellSize / 2.0f);
905
906 return new Bounds(cellCenterWS, cellSize * Vector3.one);
907 }
908
909 bool ShouldCullCell(Vector3 cellPosition, Vector4[] adjustmentVolumeBounds, int adjustmentVolumeCount)
910 {
911 var cellAABB = GetCellBounds(cellPosition);
912
913 for (int touchup = 0; touchup < adjustmentVolumeCount; touchup++)
914 {
915 Vector3 center = adjustmentVolumeBounds[touchup * 3 + 0];
916
917 if (adjustmentVolumeBounds[touchup * 3].w == float.MaxValue) // sphere
918 {
919 var diameter = adjustmentVolumeBounds[touchup * 3 + 1].x * 2.0f;
920 Bounds bounds = new Bounds(center, new Vector3(diameter, diameter, diameter));
921
922 if (bounds.Intersects(cellAABB))
923 return false;
924 }
925 else
926 {
927 Volume volume = new Volume();
928 volume.X = adjustmentVolumeBounds[touchup * 3 + 1];
929 volume.Y = adjustmentVolumeBounds[touchup * 3 + 2];
930 volume.Z = new Vector3(adjustmentVolumeBounds[touchup * 3 + 0].w, adjustmentVolumeBounds[touchup * 3 + 1].w, adjustmentVolumeBounds[touchup * 3 + 2].w);
931
932 volume.corner = center - volume.X - volume.Y - volume.Z;
933 volume.X *= 2.0f;
934 volume.Y *= 2.05f;
935 volume.Z *= 2.0f;
936
937 if (ProbeVolumePositioning.OBBAABBIntersect(volume, cellAABB, volume.CalculateAABB()))
938 return false;
939 }
940 }
941 return true;
942 }
943
944 void DrawProbeDebug(Camera camera, Texture exposureTexture)
945 {
946 if (!enabledBySRP || !isInitialized)
947 return;
948
949 bool drawProbes = probeVolumeDebug.drawProbes;
950 bool debugDraw = drawProbes ||
951 probeVolumeDebug.drawVirtualOffsetPush ||
952 probeVolumeDebug.drawProbeSamplingDebug;
953
954 int adjustmentVolumeCount = 0;
955 Vector4[] adjustmentVolumeBounds = s_BoundsArray;
956 if (!debugDraw && probeVolumeDebug.autoDrawProbes)
957 {
958 UpdateDebugFromSelection(ref adjustmentVolumeBounds, ref adjustmentVolumeCount);
959 drawProbes |= adjustmentVolumeCount != 0;
960 }
961
962 if (!debugDraw && !drawProbes)
963 return;
964
965 GeometryUtility.CalculateFrustumPlanes(camera, m_DebugFrustumPlanes);
966
967 m_DebugMaterial.shaderKeywords = null;
968 if (m_SHBands == ProbeVolumeSHBands.SphericalHarmonicsL1)
969 m_DebugMaterial.EnableKeyword("PROBE_VOLUMES_L1");
970 else if (m_SHBands == ProbeVolumeSHBands.SphericalHarmonicsL2)
971 m_DebugMaterial.EnableKeyword("PROBE_VOLUMES_L2");
972
973 // This is to force the rendering not to draw to the depth pre pass and still behave.
974 // They are going to be rendered opaque anyhow, just using the transparent render queue to make sure
975 // they properly behave w.r.t fog.
976 m_DebugMaterial.renderQueue = (int)RenderQueue.Transparent;
977 m_DebugOffsetMaterial.renderQueue = (int)RenderQueue.Transparent;
978 m_ProbeSamplingDebugMaterial.renderQueue = (int)RenderQueue.Transparent;
979 m_ProbeSamplingDebugMaterial02.renderQueue = (int)RenderQueue.Transparent;
980
981 m_DebugMaterial.SetVector("_DebugEmptyProbeData", APVDefinitions.debugEmptyColor);
982
983 if (probeVolumeDebug.drawProbeSamplingDebug)
984 {
985 m_ProbeSamplingDebugMaterial.SetInt("_ShadingMode", (int)probeVolumeDebug.probeShading);
986 m_ProbeSamplingDebugMaterial.SetInt("_RenderingLayerMask", (int)probeVolumeDebug.samplingRenderingLayer);
987 m_ProbeSamplingDebugMaterial.SetVector("_DebugArrowColor", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
988 m_ProbeSamplingDebugMaterial.SetVector("_DebugLocator01Color", new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
989 m_ProbeSamplingDebugMaterial.SetVector("_DebugLocator02Color", new Vector4(0.3f, 0.3f, 0.3f, 1.0f));
990 m_ProbeSamplingDebugMaterial.SetFloat("_ProbeSize", probeVolumeDebug.probeSamplingDebugSize);
991 m_ProbeSamplingDebugMaterial.SetTexture("_NumbersTex", m_DisplayNumbersTexture);
992 m_ProbeSamplingDebugMaterial.SetInt("_DebugSamplingNoise", Convert.ToInt32(probeVolumeDebug.debugWithSamplingNoise));
993 m_ProbeSamplingDebugMaterial.SetInt("_ForceDebugNormalViewBias", 0); // Add a secondary locator to show intermediate position (with no Anti-Leak) when Anti-Leak is active
994
995 m_ProbeSamplingDebugMaterial.SetBuffer("_positionNormalBuffer", probeSamplingDebugData.positionNormalBuffer);
996
997 Graphics.DrawMesh(m_DebugProbeSamplingMesh, new Vector4(0.0f, 0.0f, 0.0f, 1.0f), Quaternion.identity, m_ProbeSamplingDebugMaterial, 0, camera);
998 Graphics.ClearRandomWriteTargets();
999 }
1000
1001 // Sanitize the min max subdiv levels with what is available
1002 int minAvailableSubdiv = cells.Count > 0 ? GetMaxSubdivision()-1 : 0;
1003 foreach (var cell in cells.Values)
1004 {
1005 minAvailableSubdiv = Mathf.Min(minAvailableSubdiv, cell.desc.minSubdiv);
1006 }
1007
1008 int maxSubdivToVisualize = Mathf.Max(0, Mathf.Min(probeVolumeDebug.maxSubdivToVisualize, GetMaxSubdivision() - 1));
1009 int minSubdivToVisualize = Mathf.Clamp(probeVolumeDebug.minSubdivToVisualize, minAvailableSubdiv, maxSubdivToVisualize);
1010 m_MaxSubdivVisualizedIsMaxAvailable = maxSubdivToVisualize == GetMaxSubdivision() - 1;
1011
1012 bool adjustmentCulling = drawProbes && !probeVolumeDebug.drawProbes && probeVolumeDebug.isolationProbeDebug;
1013 foreach (var cell in cells.Values)
1014 {
1015 if (ShouldCullCell(cell.desc.position, camera.transform, m_DebugFrustumPlanes))
1016 continue;
1017
1018 if (adjustmentCulling && ShouldCullCell(cell.desc.position, adjustmentVolumeBounds, adjustmentVolumeCount))
1019 continue;
1020
1021 var debug = CreateInstancedProbes(cell);
1022
1023 if (debug == null)
1024 continue;
1025
1026 for (int i = 0; i < debug.probeBuffers.Count; ++i)
1027 {
1028 var props = debug.props[i];
1029 props.SetInt("_ShadingMode", (int)probeVolumeDebug.probeShading);
1030 props.SetFloat("_ExposureCompensation", probeVolumeDebug.exposureCompensation);
1031 props.SetFloat("_ProbeSize", probeVolumeDebug.probeSize);
1032 props.SetFloat("_CullDistance", probeVolumeDebug.probeCullingDistance);
1033 props.SetInt("_MaxAllowedSubdiv", maxSubdivToVisualize);
1034 props.SetInt("_MinAllowedSubdiv", minSubdivToVisualize);
1035 props.SetFloat("_ValidityThreshold", m_CurrentBakingSet.settings.dilationSettings.dilationValidityThreshold);
1036 props.SetInt("_RenderingLayerMask", probeVolumeDebug.visibleLayers);
1037 props.SetFloat("_OffsetSize", probeVolumeDebug.offsetSize);
1038 props.SetTexture("_ExposureTexture", exposureTexture);
1039
1040 if (drawProbes)
1041 {
1042 m_DebugMaterial.SetVectorArray("_TouchupVolumeBounds", adjustmentVolumeBounds);
1043 m_DebugMaterial.SetInt("_AdjustmentVolumeCount", probeVolumeDebug.isolationProbeDebug ? adjustmentVolumeCount : 0);
1044 m_DebugMaterial.SetVector("_ScreenSize", new Vector4(camera.pixelWidth, camera.pixelHeight, 1.0f/camera.pixelWidth, 1.0f/camera.pixelHeight));
1045
1046 var probeBuffer = debug.probeBuffers[i];
1047 m_DebugMaterial.SetInt("_DebugProbeVolumeSampling", 0);
1048 m_DebugMaterial.SetBuffer("_positionNormalBuffer", probeSamplingDebugData.positionNormalBuffer);
1049 Graphics.DrawMeshInstanced(debugMesh, 0, m_DebugMaterial, probeBuffer, probeBuffer.Length, props, ShadowCastingMode.Off, false, 0, camera, LightProbeUsage.Off, null);
1050 }
1051
1052 if (probeVolumeDebug.drawProbeSamplingDebug)
1053 {
1054 var probeBuffer = debug.probeBuffers[i];
1055 m_ProbeSamplingDebugMaterial02.SetInt("_DebugProbeVolumeSampling", 1);
1056 props.SetInt("_ShadingMode", (int)DebugProbeShadingMode.SH);
1057 props.SetFloat("_ProbeSize", probeVolumeDebug.probeSamplingDebugSize);
1058 props.SetInt("_DebugSamplingNoise", Convert.ToInt32(probeVolumeDebug.debugWithSamplingNoise));
1059 props.SetInt("_RenderingLayerMask", (int)probeVolumeDebug.samplingRenderingLayer);
1060 m_ProbeSamplingDebugMaterial02.SetBuffer("_positionNormalBuffer", probeSamplingDebugData.positionNormalBuffer);
1061 Graphics.DrawMeshInstanced(debugMesh, 0, m_ProbeSamplingDebugMaterial02, probeBuffer, probeBuffer.Length, props, ShadowCastingMode.Off, false, 0, camera, LightProbeUsage.Off, null);
1062 }
1063
1064 if (probeVolumeDebug.drawVirtualOffsetPush)
1065 {
1066 m_DebugOffsetMaterial.SetVectorArray("_TouchupVolumeBounds", adjustmentVolumeBounds);
1067 m_DebugOffsetMaterial.SetInt("_AdjustmentVolumeCount", probeVolumeDebug.isolationProbeDebug ? adjustmentVolumeCount : 0);
1068
1069 var offsetBuffer = debug.offsetBuffers[i];
1070 Graphics.DrawMeshInstanced(m_DebugOffsetMesh, 0, m_DebugOffsetMaterial, offsetBuffer, offsetBuffer.Length, props, ShadowCastingMode.Off, false, 0, camera, LightProbeUsage.Off, null);
1071 }
1072 }
1073 }
1074 }
1075
1076 internal void ResetDebugViewToMaxSubdiv()
1077 {
1078 if (m_MaxSubdivVisualizedIsMaxAvailable)
1079 probeVolumeDebug.maxSubdivToVisualize = GetMaxSubdivision() - 1;
1080 }
1081
1082 void ClearDebugData()
1083 {
1084 realtimeSubdivisionInfo.Clear();
1085 }
1086
1087 CellInstancedDebugProbes CreateInstancedProbes(Cell cell)
1088 {
1089 if (cell.debugProbes != null)
1090 return cell.debugProbes;
1091
1092 int maxSubdiv = GetMaxSubdivision() - 1;
1093
1094 if (!cell.data.bricks.IsCreated || cell.data.bricks.Length == 0 || !cell.data.probePositions.IsCreated || !cell.loaded)
1095 return null;
1096
1097 List<Matrix4x4[]> probeBuffers = new List<Matrix4x4[]>();
1098 List<Matrix4x4[]> offsetBuffers = new List<Matrix4x4[]>();
1099 List<MaterialPropertyBlock> props = new List<MaterialPropertyBlock>();
1100 var chunks = cell.poolInfo.chunkList;
1101
1102 Vector4[] texels = new Vector4[kProbesPerBatch];
1103 float[] layer = new float[kProbesPerBatch];
1104 float[] validity = new float[kProbesPerBatch];
1105 float[] dilationThreshold = new float[kProbesPerBatch];
1106 float[] relativeSize = new float[kProbesPerBatch];
1107 float[] touchupUpVolumeAction = cell.data.touchupVolumeInteraction.Length > 0 ? new float[kProbesPerBatch] : null;
1108 Vector4[] offsets = cell.data.offsetVectors.Length > 0 ? new Vector4[kProbesPerBatch] : null;
1109
1110 List<Matrix4x4> probeBuffer = new List<Matrix4x4>();
1111 List<Matrix4x4> offsetBuffer = new List<Matrix4x4>();
1112
1113 var debugData = new CellInstancedDebugProbes();
1114 debugData.probeBuffers = probeBuffers;
1115 debugData.offsetBuffers = offsetBuffers;
1116 debugData.props = props;
1117
1118 var chunkSizeInProbes = ProbeBrickPool.GetChunkSizeInProbeCount();
1119
1120 var loc = ProbeBrickPool.ProbeCountToDataLocSize(chunkSizeInProbes);
1121
1122 float baseThreshold = m_CurrentBakingSet.settings.dilationSettings.dilationValidityThreshold;
1123 int idxInBatch = 0;
1124 int globalIndex = 0;
1125 int brickCount = cell.desc.probeCount / ProbeBrickPool.kBrickProbeCountTotal;
1126 int bx = 0, by = 0, bz = 0;
1127 for (int brickIndex = 0; brickIndex < brickCount; ++brickIndex)
1128 {
1129 Debug.Assert(bz < loc.z);
1130
1131 int brickSize = cell.data.bricks[brickIndex].subdivisionLevel;
1132 int chunkIndex = brickIndex / ProbeBrickPool.GetChunkSizeInBrickCount();
1133 var chunk = chunks[chunkIndex];
1134 Vector3Int brickStart = new Vector3Int(chunk.x + bx, chunk.y + by, chunk.z + bz);
1135
1136 for (int z = 0; z < ProbeBrickPool.kBrickProbeCountPerDim; ++z)
1137 {
1138 for (int y = 0; y < ProbeBrickPool.kBrickProbeCountPerDim; ++y)
1139 {
1140 for (int x = 0; x < ProbeBrickPool.kBrickProbeCountPerDim; ++x)
1141 {
1142 Vector3Int texelLoc = new Vector3Int(brickStart.x + x, brickStart.y + y, brickStart.z + z);
1143
1144 int probeFlatIndex = chunkIndex * chunkSizeInProbes + (bx + x) + loc.x * ((by + y) + loc.y * (bz + z));
1145 var position = cell.data.probePositions[probeFlatIndex] - ProbeOffset(); // Offset is applied in shader
1146
1147 probeBuffer.Add(Matrix4x4.TRS(position, Quaternion.identity, Vector3.one * (0.3f * (brickSize + 1))));
1148 validity[idxInBatch] = cell.data.validity[probeFlatIndex];
1149 dilationThreshold[idxInBatch] = baseThreshold;
1150 texels[idxInBatch] = new Vector4(texelLoc.x, texelLoc.y, texelLoc.z, brickSize);
1151 relativeSize[idxInBatch] = (float)brickSize / (float)maxSubdiv;
1152
1153 layer[idxInBatch] = Unity.Mathematics.math.asfloat(cell.data.layer.Length > 0 ? cell.data.layer[probeFlatIndex] : 0xFFFFFFFF);
1154
1155 if (touchupUpVolumeAction != null)
1156 {
1157 touchupUpVolumeAction[idxInBatch] = cell.data.touchupVolumeInteraction[probeFlatIndex];
1158 dilationThreshold[idxInBatch] = touchupUpVolumeAction[idxInBatch] > 1.0f ? touchupUpVolumeAction[idxInBatch] - 1.0f : baseThreshold;
1159 }
1160
1161 if (offsets != null)
1162 {
1163 const float kOffsetThresholdSqr = 1e-6f;
1164
1165 var offset = cell.data.offsetVectors[probeFlatIndex];
1166 offsets[idxInBatch] = offset;
1167
1168 if (offset.sqrMagnitude < kOffsetThresholdSqr)
1169 {
1170 offsetBuffer.Add(Matrix4x4.identity);
1171 }
1172 else
1173 {
1174 var orientation = Quaternion.LookRotation(-offset);
1175 var scale = new Vector3(0.5f, 0.5f, offset.magnitude);
1176 offsetBuffer.Add(Matrix4x4.TRS(position + offset, orientation, scale));
1177 }
1178 }
1179 idxInBatch++;
1180
1181 if (probeBuffer.Count >= kProbesPerBatch || globalIndex == cell.desc.probeCount - 1)
1182 {
1183 idxInBatch = 0;
1184 MaterialPropertyBlock prop = new MaterialPropertyBlock();
1185
1186 prop.SetFloatArray("_Validity", validity);
1187 prop.SetFloatArray("_RenderingLayer", layer);
1188 prop.SetFloatArray("_DilationThreshold", dilationThreshold);
1189 prop.SetFloatArray("_TouchupedByVolume", touchupUpVolumeAction);
1190 prop.SetFloatArray("_RelativeSize", relativeSize);
1191 prop.SetVectorArray("_IndexInAtlas", texels);
1192
1193 if (offsets != null)
1194 prop.SetVectorArray("_Offset", offsets);
1195
1196 props.Add(prop);
1197
1198 probeBuffers.Add(probeBuffer.ToArray());
1199 probeBuffer.Clear();
1200
1201 offsetBuffers.Add(offsetBuffer.ToArray());
1202 offsetBuffer.Clear();
1203 }
1204
1205 globalIndex++;
1206 }
1207 }
1208 }
1209
1210 bx += ProbeBrickPool.kBrickProbeCountPerDim;
1211 if (bx >= loc.x)
1212 {
1213 bx = 0;
1214 by += ProbeBrickPool.kBrickProbeCountPerDim;
1215 if (by >= loc.y)
1216 {
1217 by = 0;
1218 bz += ProbeBrickPool.kBrickProbeCountPerDim;
1219 if (bz >= loc.z)
1220 {
1221 bx = 0;
1222 by = 0;
1223 bz = 0;
1224 }
1225 }
1226 }
1227 }
1228
1229 cell.debugProbes = debugData;
1230
1231 return debugData;
1232 }
1233
1234 void OnClearLightingdata()
1235 {
1236 ClearDebugData();
1237 }
1238 }
1239}