diff --git a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs index a2e0dc1f8b6..45e82fea778 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs @@ -377,7 +377,7 @@ static void SubdivideSubCell(ProbeReferenceVolume.Volume cellVolume, ProbeSubdiv VoxelizeProbeVolumeData(cmd, cellAABB, probeVolumes, ctx); // Find the maximum subdivision level we can have in this cell (avoid extra work if not needed) - int startSubdivisionLevel = ctx.maxSubdivisionLevelInSubCell - GetMaxSubdivision(ctx, probeVolumes.Max(p => p.component.maxSubdivisionMultiplier)); + int startSubdivisionLevel = ctx.maxSubdivisionLevelInSubCell - GetMaxSubdivision(ctx, probeVolumes.Max(p => p.component.GetMaxSubdivMultiplier())); for (int subdivisionLevel = startSubdivisionLevel; subdivisionLevel <= ctx.maxSubdivisionLevelInSubCell; subdivisionLevel++) { // Add the bricks from the probe volume min subdivision level: @@ -596,8 +596,8 @@ static void VoxelizeProbeVolumeData(CommandBuffer cmd, Bounds cellAABB, // Prepare list of GPU probe volumes foreach (var kp in probeVolumes) { - int minSubdiv = GetMaxSubdivision(ctx, kp.component.minSubdivisionMultiplier); - int maxSubdiv = GetMaxSubdivision(ctx, kp.component.maxSubdivisionMultiplier); + int minSubdiv = GetMaxSubdivision(ctx, kp.component.GetMinSubdivMultiplier()); + int maxSubdiv = GetMaxSubdivision(ctx, kp.component.GetMaxSubdivMultiplier()); // Constrain the probe volume AABB inside the cell var pvAABB = kp.volume.CalculateAABB(); diff --git a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs index a281f7dd9c8..cba47d6c04d 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs @@ -145,7 +145,7 @@ public void Initialize(ProbeReferenceVolumeProfile profile, Vector3 refVolOrigin if (!pv.isActiveAndEnabled) continue; - ProbeReferenceVolume.Volume volume = new ProbeReferenceVolume.Volume(Matrix4x4.TRS(pv.transform.position, pv.transform.rotation, pv.GetExtents()), pv.maxSubdivisionMultiplier, pv.minSubdivisionMultiplier); + ProbeReferenceVolume.Volume volume = new ProbeReferenceVolume.Volume(Matrix4x4.TRS(pv.transform.position, pv.transform.rotation, pv.GetExtents()), pv.GetMaxSubdivMultiplier(), pv.GetMinSubdivMultiplier()); probeVolumes.Add((pv, volume)); } diff --git a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs index 3e9a4d915b2..70c72739a62 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Drawer.cs @@ -138,23 +138,52 @@ static void Drawer_VolumeContent(SerializedProbeVolume serialized, Editor owner) EditorGUILayout.PropertyField(serialized.size, Styles.s_Size); var rect = EditorGUILayout.GetControlRect(true); - EditorGUI.BeginProperty(rect, Styles.s_MinMaxSubdivSlider, serialized.minSubdivisionMultiplier); - EditorGUI.BeginProperty(rect, Styles.s_MinMaxSubdivSlider, serialized.maxSubdivisionMultiplier); + EditorGUI.BeginProperty(rect, Styles.s_HighestSubdivLevel, serialized.highestSubdivisionLevelOverride); + EditorGUI.BeginProperty(rect, Styles.s_LowestSubdivLevel, serialized.lowestSubdivisionLevelOverride); // Round min and max subdiv - float maxSubdiv = ProbeReferenceVolume.instance.GetMaxSubdivision(1) - 1; - float min = Mathf.Round(serialized.minSubdivisionMultiplier.floatValue * maxSubdiv) / maxSubdiv; - float max = Mathf.Round(serialized.maxSubdivisionMultiplier.floatValue * maxSubdiv) / maxSubdiv; + int maxSubdiv = ProbeReferenceVolume.instance.GetMaxSubdivision() - 1; + // it's likely we don't have a profile loaded yet. + if (maxSubdiv < 0) + { + if (ProbeReferenceVolume.instance.sceneData != null) + { + ProbeVolume pv = (serialized.serializedObject.targetObject as ProbeVolume); + + var profile = ProbeReferenceVolume.instance.sceneData.GetProfileForScene(pv.gameObject.scene); + + if (profile != null) + { + ProbeReferenceVolume.instance.SetMinBrickAndMaxSubdiv(profile.minBrickSize, profile.maxSubdivision); + maxSubdiv = ProbeReferenceVolume.instance.GetMaxSubdivision() - 1; + } + else + { + maxSubdiv = 0; + } + } + } + + EditorGUILayout.LabelField("Subdivision Overrides", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + int value = serialized.highestSubdivisionLevelOverride.intValue; - EditorGUILayout.MinMaxSlider(Styles.s_MinMaxSubdivSlider, ref min, ref max, 0, 1); - serialized.minSubdivisionMultiplier.floatValue = Mathf.Max(0.00f, min); - serialized.maxSubdivisionMultiplier.floatValue = Mathf.Max(0.01f, max); + // We were initialized, but we cannot know the highest subdiv statically, so we need to resort to this. + if (serialized.highestSubdivisionLevelOverride.intValue < 0) + serialized.highestSubdivisionLevelOverride.intValue = maxSubdiv; + + serialized.highestSubdivisionLevelOverride.intValue = EditorGUILayout.IntSlider(Styles.s_HighestSubdivLevel, serialized.highestSubdivisionLevelOverride.intValue, 0, maxSubdiv); + serialized.lowestSubdivisionLevelOverride.intValue = EditorGUILayout.IntSlider(Styles.s_LowestSubdivLevel, serialized.lowestSubdivisionLevelOverride.intValue, 0, maxSubdiv); + serialized.lowestSubdivisionLevelOverride.intValue = Mathf.Min(serialized.lowestSubdivisionLevelOverride.intValue, serialized.highestSubdivisionLevelOverride.intValue); EditorGUI.EndProperty(); EditorGUI.EndProperty(); - int minSubdivInVolume = ProbeReferenceVolume.instance.GetMaxSubdivision(1 - serialized.minSubdivisionMultiplier.floatValue); - int maxSubdivInVolume = ProbeReferenceVolume.instance.GetMaxSubdivision(1 - serialized.maxSubdivisionMultiplier.floatValue); - EditorGUILayout.HelpBox($"The distance between probes will fluctuate between : {ProbeReferenceVolume.instance.GetDistanceBetweenProbes(maxSubdivInVolume)}m and {ProbeReferenceVolume.instance.GetDistanceBetweenProbes(minSubdivInVolume)}m", MessageType.Info); + int minSubdivInVolume = serialized.lowestSubdivisionLevelOverride.intValue; + int maxSubdivInVolume = serialized.highestSubdivisionLevelOverride.intValue; + EditorGUI.indentLevel--; + + EditorGUILayout.HelpBox($"The distance between probes will fluctuate between : {ProbeReferenceVolume.instance.GetDistanceBetweenProbes(maxSubdiv - maxSubdivInVolume)}m and {ProbeReferenceVolume.instance.GetDistanceBetweenProbes(maxSubdiv - minSubdivInVolume)}m", MessageType.Info); + if (EditorGUI.EndChangeCheck()) { Vector3 tmpClamp = serialized.size.vector3Value; diff --git a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs index d9cb2a1a36c..959f003e597 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeUI.Skin.cs @@ -8,7 +8,8 @@ internal static class Styles { internal static readonly GUIContent s_Size = new GUIContent("Size", "Modify the size of this Probe Volume. This is independent of the Transform's Scale."); internal static readonly GUIContent s_GlobalVolume = new GUIContent("Global", "If the volume is marked as global, it will be fit to the scene content every time the scene is saved or the baking starts."); - internal static readonly GUIContent s_MinMaxSubdivSlider = new GUIContent("Subdivision Controller", "Control how much the probe baking system will subdivide in this volume.\nBoth min and max values are used to compute the allowed subdivision levels inside this volume. e.g. a Min subdivision of 2 will ensure that there is at least 2 levels of subdivision everywhere in the volume."); + internal static readonly GUIContent s_HighestSubdivLevel = new GUIContent("Highest Subdivision Level", "Overrides the highest subdivision level used by the system. This determines how finely a probe volume is subdivided, lower values means larger minimum distance between probes."); + internal static readonly GUIContent s_LowestSubdivLevel = new GUIContent("Lowest Subdivision Level", "Overrides the lowest subdivision level used by the system. This determines how coarsely a probe volume is allowed to be subdivided, higher values means smaller maximum distance between probes."); internal static readonly GUIContent s_ObjectLayerMask = new GUIContent("Object Layer Mask", "Control which layers will be used to select the meshes for the probe placement algorithm."); internal static readonly GUIContent s_GeometryDistanceOffset = new GUIContent("Geometry Distance Offset", "Affects the minimum distance at which the subdivision system will place probes near the geometry."); internal static readonly string s_ProbeVolumeChangedMessage = "The probe volume has changed since last baking or the data was never baked.\nPlease bake lighting in the lighting panel to update the lighting data."; diff --git a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs index 10542a25482..e10e428751f 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/SerializedProbeVolume.cs @@ -6,8 +6,8 @@ internal class SerializedProbeVolume internal SerializedProperty globalVolume; internal SerializedProperty size; - internal SerializedProperty maxSubdivisionMultiplier; - internal SerializedProperty minSubdivisionMultiplier; + internal SerializedProperty highestSubdivisionLevelOverride; + internal SerializedProperty lowestSubdivisionLevelOverride; internal SerializedProperty objectLayerMask; internal SerializedProperty geometryDistanceOffset; @@ -19,10 +19,11 @@ internal SerializedProbeVolume(SerializedObject obj) globalVolume = serializedObject.FindProperty("globalVolume"); size = serializedObject.FindProperty("size"); - maxSubdivisionMultiplier = serializedObject.FindProperty("maxSubdivisionMultiplier"); - minSubdivisionMultiplier = serializedObject.FindProperty("minSubdivisionMultiplier"); objectLayerMask = serializedObject.FindProperty("objectLayerMask"); geometryDistanceOffset = serializedObject.FindProperty("geometryDistanceOffset"); + highestSubdivisionLevelOverride = serializedObject.FindProperty("highestSubdivLevelOverride"); + lowestSubdivisionLevelOverride = serializedObject.FindProperty("lowestSubdivLevelOverride"); + } internal void Apply() diff --git a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs index 8c23325ea14..8173b8746d8 100644 --- a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs +++ b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs @@ -819,6 +819,12 @@ void PerformPendingIndexChangeAndInit() } } + internal void SetMinBrickAndMaxSubdiv(float minBrickSize, int maxSubdiv) + { + SetTRS(Vector3.zero, Quaternion.identity, minBrickSize); + SetMaxSubdivision(maxSubdiv); + } + void LoadAsset(ProbeVolumeAsset asset) { if (asset.Version != (int)ProbeVolumeAsset.AssetVersion.Current) @@ -830,9 +836,7 @@ void LoadAsset(ProbeVolumeAsset asset) var path = asset.GetSerializedFullPath(); // Load info coming originally from profile - SetTRS(Vector3.zero, Quaternion.identity, asset.minBrickSize); - SetMaxSubdivision(asset.maxSubdivision); - + SetMinBrickAndMaxSubdiv(asset.minBrickSize, asset.maxSubdivision); for (int i = 0; i < asset.cells.Count; ++i) { diff --git a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolumeProfile.cs b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolumeProfile.cs index 6ec8e6a0ef7..a27bf0fd87f 100644 --- a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolumeProfile.cs +++ b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolumeProfile.cs @@ -108,7 +108,7 @@ public override void OnInspectorGUI() EditorGUILayout.HelpBox(Styles.simplificationLevelsHighWarning, MessageType.Warning); } EditorGUILayout.PropertyField(m_MinDistanceBetweenProbes, Styles.minDistanceBetweenProbes); - EditorGUILayout.HelpBox($"The distance between probes will fluctuate between : {profile.minDistanceBetweenProbes}m and {profile.cellSizeInMeters}m", MessageType.Info); + EditorGUILayout.HelpBox($"The distance between probes will fluctuate between : {profile.minDistanceBetweenProbes}m and {profile.cellSizeInMeters / 3.0f}m", MessageType.Info); if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); diff --git a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.cs b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.cs index 74378765a81..1ca7aa8dfa9 100644 --- a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.cs +++ b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.cs @@ -21,15 +21,17 @@ public class ProbeVolume : MonoBehaviour { public bool globalVolume = false; public Vector3 size = new Vector3(10, 10, 10); - [HideInInspector] - public float maxSubdivisionMultiplier = 1; - [HideInInspector] - public float minSubdivisionMultiplier = 0; [HideInInspector, Range(0f, 2f)] public float geometryDistanceOffset = 0.2f; public LayerMask objectLayerMask = -1; + + [HideInInspector] + public int lowestSubdivLevelOverride = 0; + [HideInInspector] + public int highestSubdivLevelOverride = -1; + [SerializeField] internal bool mightNeedRebaking = false; [SerializeField] internal Matrix4x4 cachedTransform; @@ -112,8 +114,8 @@ public override int GetHashCode() unchecked { hash = hash * 23 + size.GetHashCode(); - hash = hash * 23 + maxSubdivisionMultiplier.GetHashCode(); - hash = hash * 23 + minSubdivisionMultiplier.GetHashCode(); + hash = hash * 23 + highestSubdivLevelOverride.GetHashCode(); + hash = hash * 23 + lowestSubdivLevelOverride.GetHashCode(); hash = hash * 23 + geometryDistanceOffset.GetHashCode(); hash = hash * 23 + objectLayerMask.GetHashCode(); } @@ -123,6 +125,18 @@ public override int GetHashCode() #endif + internal float GetMinSubdivMultiplier() + { + float maxSubdiv = ProbeReferenceVolume.instance.GetMaxSubdivision() - 1; + return Mathf.Max(0.0f, lowestSubdivLevelOverride / maxSubdiv); + } + + internal float GetMaxSubdivMultiplier() + { + float maxSubdiv = ProbeReferenceVolume.instance.GetMaxSubdivision() - 1; + return Mathf.Max(0.0f, highestSubdivLevelOverride / maxSubdiv); + } + // Momentarily moving the gizmo rendering for bricks and cells to Probe Volume itself, // only the first probe volume in the scene will render them. The reason is that we dont have any // other non-hidden component related to APV. diff --git a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeSceneData.cs b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeSceneData.cs index 44997802090..199125ba2e8 100644 --- a/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeSceneData.cs +++ b/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeSceneData.cs @@ -343,8 +343,7 @@ internal void UpdateSceneBounds(Scene scene) } return; } - ProbeReferenceVolume.instance.SetTRS(Vector3.zero, Quaternion.identity, profile.minBrickSize); - ProbeReferenceVolume.instance.SetMaxSubdivision(profile.maxSubdivision); + ProbeReferenceVolume.instance.SetMinBrickAndMaxSubdiv(profile.minBrickSize, profile.maxSubdivision); } var sceneGUID = GetSceneGUID(scene);