Skip to content

Commit 49c1915

Browse files
KEngelstoftEvergreen
authored andcommitted
Add volume based controls for Surface Cache GI
1 parent ca3ed61 commit 49c1915

File tree

7 files changed

+841
-196
lines changed

7 files changed

+841
-196
lines changed

Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCache.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ internal class SurfaceCacheVolume : IDisposable
101101

102102
public readonly uint SpatialResolution;
103103
public readonly uint CascadeCount;
104-
public readonly float VoxelMinSize;
104+
public float VoxelMinSize;
105105
public readonly int3[] CascadeOffsets;
106106
public readonly GraphicsBuffer CascadeOffsetBuffer;
107107
public readonly GraphicsBuffer CellAllocationMarks;
@@ -434,14 +434,10 @@ internal static class ShaderIDs
434434
public SurfaceCache(
435435
SurfaceCacheResourceSet resources,
436436
uint defragCount,
437-
SurfaceCacheVolumeParameterSet volParams,
438-
SurfaceCacheEstimationParameterSet estimationParams,
439-
SurfaceCachePatchFilteringParameterSet patchFilteringParams)
437+
SurfaceCacheVolumeParameterSet volParams)
440438
{
441439
Debug.Assert(volParams.CascadeCount != 0);
442440
Debug.Assert(volParams.CascadeCount <= CascadeMax);
443-
Debug.Assert(0.0f <= patchFilteringParams.TemporalSmoothing);
444-
Debug.Assert(patchFilteringParams.TemporalSmoothing <= 1.0f);
445441

446442
const uint punctualLightSampleCount = 128;
447443
const uint patchCapacity = 65536; // Must match HLSL side constant.
@@ -453,13 +449,24 @@ public SurfaceCache(
453449
_patches = new SurfaceCachePatchList(patchCapacity);
454450
_punctualLightSamples = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)punctualLightSampleCount, sizeof(float) * 17);
455451

452+
_defragCount = defragCount;
453+
}
454+
455+
public void SetEstimationParams(SurfaceCacheEstimationParameterSet estimationParams)
456+
{
456457
_estimationParams = estimationParams;
457-
_patchFilteringParams = patchFilteringParams;
458+
}
458459

460+
public void SetPatchFilteringParams(SurfaceCachePatchFilteringParameterSet patchFilteringParams)
461+
{
459462
Debug.Assert(0.0f <= patchFilteringParams.TemporalSmoothing && patchFilteringParams.TemporalSmoothing <= 1.0f);
463+
_patchFilteringParams = patchFilteringParams;
460464
_shortHysteresis = Mathf.Lerp(0.75f, 0.95f, patchFilteringParams.TemporalSmoothing);
465+
}
461466

462-
_defragCount = defragCount;
467+
public void UpdateVolumeSize(float size)
468+
{
469+
_volume.VoxelMinSize = size / (_volume.SpatialResolution * (float)(1u << (int)(_volume.CascadeCount - 1u)));
463470
}
464471

465472
public void RecordPreparation(RenderGraph renderGraph, uint frameIdx)
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#if SURFACE_CACHE
2+
3+
using UnityEngine;
4+
using UnityEngine.Rendering.Universal;
5+
6+
namespace UnityEditor.Rendering.Universal
7+
{
8+
[CustomEditor(typeof(SurfaceCacheGIVolumeOverride))]
9+
internal class SurfaceCacheGIVolumeEditor : VolumeComponentEditor
10+
{
11+
protected SerializedDataParameter m_Quality;
12+
13+
protected SerializedDataParameter m_MultiBounce;
14+
protected SerializedDataParameter m_SampleCount;
15+
16+
protected SerializedDataParameter m_TemporalSmoothing;
17+
protected SerializedDataParameter m_SpatialFilterEnabled;
18+
protected SerializedDataParameter m_SpatialSampleCount;
19+
protected SerializedDataParameter m_SpatialRadius;
20+
protected SerializedDataParameter m_TemporalPostFilter;
21+
22+
protected SerializedDataParameter m_LookupSampleCount;
23+
protected SerializedDataParameter m_UpsamplingKernelSize;
24+
protected SerializedDataParameter m_UpsamplingSampleCount;
25+
26+
protected SerializedDataParameter m_VolumeSize;
27+
protected SerializedDataParameter m_VolumeResolution;
28+
protected SerializedDataParameter m_VolumeCascadeCount;
29+
30+
protected SerializedDataParameter m_CascadeMovement;
31+
32+
protected SerializedDataParameter m_DefragCount;
33+
34+
struct LightTransportSetting
35+
{
36+
public bool multiBounce;
37+
public int sampleCount;
38+
}
39+
40+
struct PatchFilteringSetting
41+
{
42+
public float temporalSmoothing;
43+
public bool spatialFilterEnabled;
44+
public int spatialSampleCount;
45+
public float spatialRadius;
46+
public bool temporalPostFilter;
47+
}
48+
49+
struct ScreenFilteringSetting
50+
{
51+
public int lookupSampleCount;
52+
public float upsamplingKernelSize;
53+
public int upsamplingSampleCount;
54+
}
55+
56+
struct QualitySetting
57+
{
58+
public LightTransportSetting lightTransport;
59+
public PatchFilteringSetting patchFiltering;
60+
public ScreenFilteringSetting screenFiltering;
61+
}
62+
63+
// Quality preset definitions; indexed by SurfaceCacheGIQuality (Low=0, Medium=1, High=2, Ultra=3).
64+
static readonly QualitySetting[] k_QualityPresets =
65+
{
66+
// Low
67+
new QualitySetting
68+
{
69+
lightTransport = new LightTransportSetting { multiBounce = false, sampleCount = 1 },
70+
patchFiltering = new PatchFilteringSetting { temporalSmoothing = 0.9f, spatialFilterEnabled = false, spatialSampleCount = 4, spatialRadius = 1.0f, temporalPostFilter = false },
71+
screenFiltering = new ScreenFilteringSetting { lookupSampleCount = 4, upsamplingKernelSize = 2.0f, upsamplingSampleCount = 1 },
72+
},
73+
// Medium
74+
new QualitySetting
75+
{
76+
lightTransport = new LightTransportSetting { multiBounce = true, sampleCount = 2 },
77+
patchFiltering = new PatchFilteringSetting { temporalSmoothing = 0.8f, spatialFilterEnabled = true, spatialSampleCount = 4, spatialRadius = 1.0f, temporalPostFilter = true },
78+
screenFiltering = new ScreenFilteringSetting { lookupSampleCount = 6, upsamplingKernelSize = 4.0f, upsamplingSampleCount = 2 },
79+
},
80+
// High
81+
new QualitySetting
82+
{
83+
lightTransport = new LightTransportSetting { multiBounce = true, sampleCount = 4 },
84+
patchFiltering = new PatchFilteringSetting { temporalSmoothing = 0.7f, spatialFilterEnabled = true, spatialSampleCount = 6, spatialRadius = 1.5f, temporalPostFilter = true },
85+
screenFiltering = new ScreenFilteringSetting { lookupSampleCount = 8, upsamplingKernelSize = 5.0f, upsamplingSampleCount = 4 },
86+
},
87+
// Ultra
88+
new QualitySetting
89+
{
90+
lightTransport = new LightTransportSetting { multiBounce = true, sampleCount = 8 },
91+
patchFiltering = new PatchFilteringSetting { temporalSmoothing = 0.6f, spatialFilterEnabled = true, spatialSampleCount = 8, spatialRadius = 2.0f, temporalPostFilter = true },
92+
screenFiltering = new ScreenFilteringSetting { lookupSampleCount = 8, upsamplingKernelSize = 7.0f, upsamplingSampleCount = 8 },
93+
},
94+
};
95+
96+
static GUIContent s_Quality = EditorGUIUtility.TrTextContent("Quality", "Quality preset for Surface Cache GI. Select Custom to manually adjust individual parameters.");
97+
static GUIContent s_MultiBounce = EditorGUIUtility.TrTextContent("Multi Bounce", "Enable multi-bounce global illumination for more accurate light propagation.");
98+
static GUIContent s_SampleCount = EditorGUIUtility.TrTextContent("Sample Count", "Number of samples used for GI estimation. Higher values improve quality at performance cost.");
99+
static GUIContent s_TemporalSmoothing = EditorGUIUtility.TrTextContent("Temporal Smoothing", "Temporal smoothing for patch data. Higher values produce more stable results but slower response to lighting changes.");
100+
static GUIContent s_SpatialFilterEnabled = EditorGUIUtility.TrTextContent("Spatial Filter", "Enables spatial filtering across patches. This reduces noise but may also increase leaking.");
101+
static GUIContent s_SpatialSampleCount = EditorGUIUtility.TrTextContent("Spatial Sample Count", "Number of samples for spatial filtering. Higher values improve quality at performance cost.");
102+
static GUIContent s_SpatialRadius = EditorGUIUtility.TrTextContent("Spatial Radius", "Radius used for the spatial filtering kernel. Larger values reduces noise but may cause over-blurring.");
103+
static GUIContent s_TemporalPostFilter = EditorGUIUtility.TrTextContent("Temporal Post Filter", "Enable temporal post-filtering for additional stability.");
104+
static GUIContent s_LookupSampleCount = EditorGUIUtility.TrTextContent("Lookup Sample Count", "Number of samples for screen-space lookups.");
105+
static GUIContent s_UpsamplingKernelSize = EditorGUIUtility.TrTextContent("Upsampling Kernel Size", "Kernel size for upsampling filtering.");
106+
static GUIContent s_UpsamplingSampleCount = EditorGUIUtility.TrTextContent("Upsampling Sample Count", "Number of samples for upsampling. Higher values improve quality at performance cost.");
107+
static GUIContent s_VolumeSize = EditorGUIUtility.TrTextContent("Size", "Size of the surface cache volume in world units. Can be changed at runtime without a performance hitch.");
108+
static GUIContent s_VolumeResolution = EditorGUIUtility.TrTextContent("Resolution", "Spatial resolution of the volume grid. Higher values improve spatial detail but use more memory. Changing at runtime can cause a performance hitch if internal buffers are reallocated.");
109+
static GUIContent s_VolumeCascadeCount = EditorGUIUtility.TrTextContent("Cascade Count", "Number of volume cascades. More cascades extend the volume's effective range. Changing at runtime can cause a performance hitch if internal buffers are reallocated.");
110+
static GUIContent s_CascadeMovement = EditorGUIUtility.TrTextContent("Cascade Movement", "Enable volume cascades to follow the camera.");
111+
112+
// Section header labels with tooltips
113+
static GUIContent s_LightTransportHeader = EditorGUIUtility.TrTextContent("Light Transport", "Controls how many rays are cast per patch to estimate indirect lighting. More samples reduce variance but increase GPU cost per frame.");
114+
static GUIContent s_PatchFilteringHeader = EditorGUIUtility.TrTextContent("Patch Filtering", "Controls how patch irradiance data is filtered over time and space. These settings trade temporal stability and spatial smoothness against responsiveness to lighting changes and light leaking.");
115+
static GUIContent s_ScreenFilteringHeader = EditorGUIUtility.TrTextContent("Screen Filtering", "Controls how the low-resolution patch irradiance is resolved and upsampled to full screen resolution. These settings affect the final image quality and sharpness of the GI contribution.");
116+
static GUIContent s_VolumeConfigurationHeader = EditorGUIUtility.TrTextContent("Volume Configuration", "Defines the spatial extent and grid density of the surface cache volume. Size can be changed freely at runtime. Resolution and cascade count changes reallocate internal buffers, which may cause a brief hitch.");
117+
static GUIContent s_VolumeBehaviorHeader = EditorGUIUtility.TrTextContent("Volume Behavior", "Controls how the volume cascades move relative to the camera during gameplay.");
118+
static GUIContent s_DefragCount = EditorGUIUtility.TrTextContent("Defrag Count", "Number of surface cache patches to defragment per frame. Higher values reduce memory fragmentation at a small per-frame cost.");
119+
120+
public override void OnEnable()
121+
{
122+
var o = new PropertyFetcher<SurfaceCacheGIVolumeOverride>(serializedObject);
123+
124+
m_Quality = Unpack(o.Find(x => x.quality));
125+
m_MultiBounce = Unpack(o.Find("m_MultiBounce"));
126+
m_SampleCount = Unpack(o.Find("m_SampleCount"));
127+
m_TemporalSmoothing = Unpack(o.Find("m_TemporalSmoothing"));
128+
m_SpatialFilterEnabled = Unpack(o.Find("m_SpatialFilterEnabled"));
129+
m_SpatialSampleCount = Unpack(o.Find("m_SpatialSampleCount"));
130+
m_SpatialRadius = Unpack(o.Find("m_SpatialRadius"));
131+
m_TemporalPostFilter = Unpack(o.Find("m_TemporalPostFilter"));
132+
m_LookupSampleCount = Unpack(o.Find("m_LookupSampleCount"));
133+
m_UpsamplingKernelSize = Unpack(o.Find("m_UpsamplingKernelSize"));
134+
m_UpsamplingSampleCount = Unpack(o.Find("m_UpsamplingSampleCount"));
135+
m_VolumeSize = Unpack(o.Find("m_VolumeSize"));
136+
m_VolumeResolution = Unpack(o.Find("m_VolumeResolution"));
137+
m_VolumeCascadeCount = Unpack(o.Find("m_VolumeCascadeCount"));
138+
m_CascadeMovement = Unpack(o.Find("m_CascadeMovement"));
139+
m_DefragCount = Unpack(o.Find("m_DefragCount"));
140+
}
141+
142+
public override void OnInspectorGUI()
143+
{
144+
// Quality preset dropdown. Switching to a named preset writes its values into the
145+
// backing fields so users can see the preset values and make fine adjustments from there.
146+
EditorGUI.BeginChangeCheck();
147+
PropertyField(m_Quality, s_Quality);
148+
if (EditorGUI.EndChangeCheck())
149+
{
150+
var quality = (SurfaceCacheGIQuality)m_Quality.value.enumValueIndex;
151+
if (quality != SurfaceCacheGIQuality.Custom)
152+
ApplyQualityPreset(quality);
153+
}
154+
155+
// Light Transport section
156+
EditorGUILayout.Space();
157+
EditorGUILayout.LabelField(s_LightTransportHeader, EditorStyles.boldLabel);
158+
EditorGUI.BeginChangeCheck();
159+
PropertyField(m_MultiBounce, s_MultiBounce);
160+
PropertyField(m_SampleCount, s_SampleCount);
161+
if (EditorGUI.EndChangeCheck())
162+
SwitchToCustomIfNeeded();
163+
164+
// Patch Filtering section
165+
EditorGUILayout.Space();
166+
EditorGUILayout.LabelField(s_PatchFilteringHeader, EditorStyles.boldLabel);
167+
EditorGUI.BeginChangeCheck();
168+
PropertyField(m_TemporalSmoothing, s_TemporalSmoothing);
169+
PropertyField(m_SpatialFilterEnabled, s_SpatialFilterEnabled);
170+
using (new IndentLevelScope())
171+
{
172+
PropertyField(m_SpatialSampleCount, s_SpatialSampleCount);
173+
PropertyField(m_SpatialRadius, s_SpatialRadius);
174+
}
175+
PropertyField(m_TemporalPostFilter, s_TemporalPostFilter);
176+
if (EditorGUI.EndChangeCheck())
177+
SwitchToCustomIfNeeded();
178+
179+
// Screen Filtering section
180+
EditorGUILayout.Space();
181+
EditorGUILayout.LabelField(s_ScreenFilteringHeader, EditorStyles.boldLabel);
182+
EditorGUI.BeginChangeCheck();
183+
PropertyField(m_LookupSampleCount, s_LookupSampleCount);
184+
PropertyField(m_UpsamplingKernelSize, s_UpsamplingKernelSize);
185+
PropertyField(m_UpsamplingSampleCount, s_UpsamplingSampleCount);
186+
if (EditorGUI.EndChangeCheck())
187+
SwitchToCustomIfNeeded();
188+
189+
// Volume Configuration section (always editable, not affected by quality preset)
190+
EditorGUILayout.Space();
191+
EditorGUILayout.LabelField(s_VolumeConfigurationHeader, EditorStyles.boldLabel);
192+
PropertyField(m_VolumeSize, s_VolumeSize);
193+
PropertyField(m_VolumeResolution, s_VolumeResolution);
194+
PropertyField(m_VolumeCascadeCount, s_VolumeCascadeCount);
195+
196+
// Volume Behavior (always editable, not affected by quality preset)
197+
EditorGUILayout.Space();
198+
EditorGUILayout.LabelField(s_VolumeBehaviorHeader, EditorStyles.boldLabel);
199+
PropertyField(m_CascadeMovement, s_CascadeMovement);
200+
201+
if (showAdditionalProperties)
202+
{
203+
EditorGUILayout.Space();
204+
EditorGUILayout.LabelField("Advanced", EditorStyles.boldLabel);
205+
PropertyField(m_DefragCount, s_DefragCount);
206+
}
207+
}
208+
209+
// Switches quality to Custom when the user edits a preset-controlled field while a preset is active.
210+
void SwitchToCustomIfNeeded()
211+
{
212+
if ((SurfaceCacheGIQuality)m_Quality.value.enumValueIndex != SurfaceCacheGIQuality.Custom)
213+
{
214+
m_Quality.value.enumValueIndex = (int)SurfaceCacheGIQuality.Custom;
215+
m_Quality.overrideState.boolValue = true;
216+
}
217+
}
218+
219+
// Writes preset values into the backing serialized fields so users can inspect them
220+
// and use them as a starting point when switching to Custom.
221+
void ApplyQualityPreset(SurfaceCacheGIQuality quality)
222+
{
223+
var settings = k_QualityPresets[(int)quality];
224+
225+
m_MultiBounce.value.boolValue = settings.lightTransport.multiBounce;
226+
m_MultiBounce.overrideState.boolValue = true;
227+
m_SampleCount.value.intValue = settings.lightTransport.sampleCount;
228+
m_SampleCount.overrideState.boolValue = true;
229+
m_TemporalSmoothing.value.floatValue = settings.patchFiltering.temporalSmoothing;
230+
m_TemporalSmoothing.overrideState.boolValue = true;
231+
m_SpatialFilterEnabled.value.boolValue = settings.patchFiltering.spatialFilterEnabled;
232+
m_SpatialFilterEnabled.overrideState.boolValue = true;
233+
m_SpatialSampleCount.value.intValue = settings.patchFiltering.spatialSampleCount;
234+
m_SpatialSampleCount.overrideState.boolValue = true;
235+
m_SpatialRadius.value.floatValue = settings.patchFiltering.spatialRadius;
236+
m_SpatialRadius.overrideState.boolValue = true;
237+
m_TemporalPostFilter.value.boolValue = settings.patchFiltering.temporalPostFilter;
238+
m_TemporalPostFilter.overrideState.boolValue = true;
239+
m_LookupSampleCount.value.intValue = settings.screenFiltering.lookupSampleCount;
240+
m_LookupSampleCount.overrideState.boolValue = true;
241+
m_UpsamplingKernelSize.value.floatValue = settings.screenFiltering.upsamplingKernelSize;
242+
m_UpsamplingKernelSize.overrideState.boolValue = true;
243+
m_UpsamplingSampleCount.value.intValue = settings.screenFiltering.upsamplingSampleCount;
244+
m_UpsamplingSampleCount.overrideState.boolValue = true;
245+
}
246+
}
247+
}
248+
249+
#endif

Packages/com.unity.render-pipelines.universal/Editor/Overrides/SurfaceCacheGIVolumeEditor.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)