|
| 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 |
0 commit comments