diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs index e02c9ee6fa5..9283a59cbf2 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs @@ -801,7 +801,10 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) rowRect.xMin += 2; rowRect.xMax -= 2; - EditorGUI.LabelField(rowRect, GUIContent.none, EditorGUIUtility.TrTextContent(row.displayName), DebugWindow.Styles.centeredLeft); + + bool isAlternate = r % 2 == 0; + + EditorGUI.LabelField(rowRect, GUIContent.none, EditorGUIUtility.TrTextContent(row.displayName),isAlternate ? DebugWindow.Styles.centeredLeft : DebugWindow.Styles.centeredLeftAlternate); rowRect.xMin -= 2; rowRect.xMax += 2; @@ -812,7 +815,7 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) rowRect.x += rowRect.width; rowRect.width = columns[visible[c]].width; if (!row.isHidden) - DisplayChild(rowRect, row.children[visible[c] - 1]); + DisplayChild(rowRect, row.children[visible[c] - 1], isAlternate); } rowRect.y += rowRect.height; } @@ -855,7 +858,7 @@ internal Rect DrawOutline(Rect rect) return new Rect(rect.x + size, rect.y + size, rect.width - 2 * size, rect.height - 2 * size); } - internal void DisplayChild(Rect rect, DebugUI.Widget child) + internal void DisplayChild(Rect rect, DebugUI.Widget child, bool isAlternate) { rect.xMin += 2; rect.xMax -= 2; @@ -869,7 +872,7 @@ internal void DisplayChild(Rect rect, DebugUI.Widget child) if (child.GetType() == typeof(DebugUI.Value)) { var widget = Cast(child); - EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(widget.GetValue().ToString())); + EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(widget.GetValue().ToString()), isAlternate ? DebugWindow.Styles.centeredLeft : DebugWindow.Styles.centeredLeftAlternate); } else if (child.GetType() == typeof(DebugUI.ColorField)) { diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs index 177840c5c7f..1912d97f857 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs @@ -10,6 +10,7 @@ using UnityEngine; using UnityEngine.Assertions; using UnityEngine.Rendering; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; namespace UnityEditor.Rendering { @@ -582,6 +583,7 @@ public class Styles public readonly Color skinBackgroundColor; public static GUIStyle centeredLeft = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft }; + public static GUIStyle centeredLeftAlternate = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft }; public static float singleRowHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; public static int foldoutColumnWidth = 70; @@ -593,6 +595,12 @@ public Styles() Color backgroundColorDarkSkin = new Color32(38, 38, 38, 128); Color backgroundColorLightSkin = new Color32(128, 128, 128, 96); + centeredLeftAlternate.normal.background = CoreEditorUtils.CreateColoredTexture2D( + EditorGUIUtility.isProSkin + ? new Color(63 / 255.0f, 63 / 255.0f, 63 / 255.0f, 255 / 255.0f) + : new Color(202 / 255.0f, 202 / 255.0f, 202 / 255.0f, 255 / 255.0f), + "centeredLeftAlternate Background"); + sectionScrollView = new GUIStyle(sectionScrollView); sectionScrollView.overflow.bottom += 1; @@ -604,6 +612,15 @@ public Styles() sectionHeader.margin.left += 1; sectionHeader.normal.textColor = EditorGUIUtility.isProSkin ? textColorDarkSkin : textColorLightSkin; skinBackgroundColor = EditorGUIUtility.isProSkin ? backgroundColorDarkSkin : backgroundColorLightSkin; + + // Make sure that textures are unloaded on domain reloads. + void OnBeforeAssemblyReload() + { + UnityEngine.Object.DestroyImmediate(centeredLeftAlternate.normal.background); + AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload; + } + + AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload; } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugDisplaySettingsVolumes.cs b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugDisplaySettingsVolumes.cs index 783ad228311..0ff6303ba12 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugDisplaySettingsVolumes.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugDisplaySettingsVolumes.cs @@ -37,12 +37,19 @@ static class Strings public static readonly string camera = "Camera"; public static readonly string parameter = "Parameter"; public static readonly string component = "Component"; - public static readonly string debugViewNotSupported = "Debug view not supported"; + public static readonly string debugViewNotSupported = "N/A"; + public static readonly string parameterNotOverrided = "-"; public static readonly string volumeInfo = "Volume Info"; - public static readonly string interpolatedValue = "Interpolated Value"; - public static readonly string defaultValue = "Default Value"; + public static readonly string gameObject = "GameObject"; + public static readonly string resultValue = "Result"; + public static readonly string resultValueTooltip = "The interpolated result value of the parameter. This value is used to render the camera."; + public static readonly string globalDefaultValue = "Graphics Settings"; + public static readonly string globalDefaultValueTooltip = "Default value for this parameter, defined by the Default Volume Profile in Global Settings."; + public static readonly string qualityLevelValue = "Quality Settings"; + public static readonly string qualityLevelValueTooltip = "Override value for this parameter, defined by the Volume Profile in the current SRP Asset."; public static readonly string global = "Global"; public static readonly string local = "Local"; + public static readonly string volumeProfile = "Volume Profile"; } internal static class WidgetFactory @@ -88,40 +95,26 @@ public static DebugUI.ObjectPopupField CreateCameraSelector(SettingsPanel panel, }; } - static DebugUI.Widget CreateVolumeParameterWidget(string name, VolumeParameter param, Func isHiddenCallback = null) + static DebugUI.Widget CreateVolumeParameterWidget(string name, bool isResultParameter, VolumeParameter param, Func isHiddenCallback = null) { - if (param == null) - return new DebugUI.Value() { displayName = name, getter = () => "-" }; - - var parameterType = param.GetType(); - - // Special overrides - if (parameterType == typeof(ColorParameter)) +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if (param != null) { - var p = (ColorParameter)param; - return new DebugUI.ColorField() + var parameterType = param.GetType(); + if (parameterType == typeof(ColorParameter)) { - displayName = name, - hdr = p.hdr, - showAlpha = p.showAlpha, - getter = () => p.value, - setter = value => p.value = value, - isHiddenCallback = isHiddenCallback - }; - } - else if (parameterType == typeof(BoolParameter)) - { - var p = (BoolParameter)param; - return new DebugUI.BoolField() - { - displayName = name, - getter = () => p.value, - setter = value => p.value = value, - isHiddenCallback = isHiddenCallback - }; - } - else - { + var p = (ColorParameter)param; + return new DebugUI.ColorField() + { + displayName = name, + hdr = p.hdr, + showAlpha = p.showAlpha, + getter = () => p.value, + setter = value => p.value = value, + isHiddenCallback = isHiddenCallback + }; + } + var typeInfo = parameterType.GetTypeInfo(); var genericArguments = typeInfo.BaseType.GenericTypeArguments; if (genericArguments.Length > 0 && genericArguments[0].IsArray) @@ -133,181 +126,137 @@ static DebugUI.Widget CreateVolumeParameterWidget(string name, VolumeParameter p type = parameterType }; } - } - // For parameters that do not override `ToString` - var property = param.GetType().GetProperty("value"); - var toString = property.PropertyType.GetMethod("ToString", Type.EmptyTypes); - if ((toString == null) || (toString.DeclaringType == typeof(object)) || (toString.DeclaringType == typeof(UnityEngine.Object))) - { - // Check if the parameter has a name - var nameProp = property.PropertyType.GetProperty("name"); - if (nameProp == null) - return new DebugUI.Value() { displayName = name, getter = () => Strings.debugViewNotSupported }; - - // Return the parameter name return new DebugUI.Value() { displayName = name, getter = () => { - var value = property.GetValue(param); - if (value == null || value.Equals(null)) - return Strings.none; - var valueString = nameProp.GetValue(value); - return valueString ?? Strings.none; + var property = param.GetType().GetProperty("value"); + if (property == null) + return "-"; + + if (isResultParameter || param.overrideState) + { + var value = property.GetValue(param); + var propertyType = property.PropertyType; + if (value == null || value.Equals(null)) + return Strings.none + $" ({propertyType.Name})"; + + var toString = propertyType.GetMethod("ToString", Type.EmptyTypes); + if ((toString == null) || (toString.DeclaringType == typeof(object)) || (toString.DeclaringType == typeof(UnityEngine.Object))) + { + // Check if the parameter has a name + var nameProp = property.PropertyType.GetProperty("name"); + if (nameProp == null) + return Strings.debugViewNotSupported; + + var valueString = nameProp.GetValue(value); + return valueString ?? Strings.none; + } + + return value.ToString(); + } + + return Strings.parameterNotOverrided; }, isHiddenCallback = isHiddenCallback }; } + #endif + return new DebugUI.Value(); + } - // Call the ToString method - return new DebugUI.Value() - { - displayName = name, - getter = () => - { - var value = property.GetValue(param); - return value == null ? Strings.none : value.ToString(); - }, - isHiddenCallback = isHiddenCallback - }; + static DebugUI.Value s_EmptyDebugUIValue = new DebugUI.Value { getter = () => string.Empty }; + + struct VolumeParameterChain + { + public DebugUI.Widget.NameAndTooltip nameAndTooltip; + public VolumeProfile volumeProfile; + public VolumeComponent volumeComponent; + public Volume volume; } - public static DebugUI.Table CreateVolumeTable(DebugDisplaySettingsVolume data) + static VolumeComponent GetSelectedVolumeComponent(VolumeProfile profile, Type selectedType) { - var table = new DebugUI.Table() + if (profile != null) { - displayName = Strings.parameter, - isReadOnly = true - }; + foreach (var component in profile.components) + if (component.GetType() == selectedType) + return component; + } + return null; + } + + static List GetResolutionChain(DebugDisplaySettingsVolume data) + { + List chain = new List(); Type selectedType = data.volumeDebugSettings.selectedComponentType; if (selectedType == null) - return table; + return chain; - var stack = data.volumeDebugSettings.selectedCameraVolumeStack ?? VolumeManager.instance.stack; + var volumeManager = VolumeManager.instance; + var stack = data.volumeDebugSettings.selectedCameraVolumeStack ?? volumeManager.stack; var stackComponent = stack.GetComponent(selectedType); if (stackComponent == null) - return table; + return chain; - var volumes = data.volumeDebugSettings.GetVolumes(); - - var inst = (VolumeComponent)ScriptableObject.CreateInstance(selectedType); - - // First row for volume info - var row = new DebugUI.Table.Row() + var result = new VolumeParameterChain() { - displayName = Strings.volumeInfo, - opened = true, // Open by default for the in-game view - children = + nameAndTooltip = new DebugUI.Widget.NameAndTooltip() { - new DebugUI.Value() - { - displayName = Strings.interpolatedValue, - getter = () => string.Empty - } - } + name = Strings.resultValue, + tooltip = Strings.resultValueTooltip, + }, + volumeComponent = stackComponent, }; - // Second row, links to volume gameobjects - var row2 = new DebugUI.Table.Row() - { - displayName = "GameObject", - children = { new DebugUI.Value() { getter = () => string.Empty } } - }; + chain.Add(result); + // Add volume components that override default values + var volumes = data.volumeDebugSettings.GetVolumes(); foreach (var volume in volumes) { var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; - row.children.Add(new DebugUI.Value() + var overrideComponent = GetSelectedVolumeComponent(profile, selectedType); + if (overrideComponent != null) { - displayName = profile.name, - getter = () => + var overrideVolume = new VolumeParameterChain() { - var scope = volume.isGlobal ? Strings.global : Strings.local; - var weight = data.volumeDebugSettings.GetVolumeWeight(volume); - return scope + " (" + (weight * 100f) + "%)"; - } - }); - - row2.children.Add(new DebugUI.ObjectField() - { - displayName = profile.name, - getter = () => volume, - }); + nameAndTooltip = new DebugUI.Widget.NameAndTooltip() + { + name = profile.name, + tooltip = profile.name, + }, + volumeProfile = profile, + volumeComponent = overrideComponent, + volume = volume + }; + chain.Add(overrideVolume); + } } - row.children.Add(new DebugUI.Value() { displayName = Strings.defaultValue, getter = () => string.Empty }); - table.children.Add(row); - - row2.children.Add(new DebugUI.Value() { getter = () => string.Empty }); - table.children.Add(row2); + return chain; + } - // Build rows - recursively handles nested parameters - var rows = new List(); - int AddParameterRows(Type type, string baseName = null, int skip = 0) + public static DebugUI.Table CreateVolumeTable(DebugDisplaySettingsVolume data) + { + var table = new DebugUI.Table() { - void AddRow(FieldInfo f, string prefix, int skip) - { - var fieldName = prefix + f.Name; - var attr = (DisplayInfoAttribute[])f.GetCustomAttributes(typeof(DisplayInfoAttribute), true); - if (attr.Length != 0) - fieldName = prefix + attr[0].name; -#if UNITY_EDITOR - // Would be nice to have the equivalent for the runtime debug. - else - fieldName = UnityEditor.ObjectNames.NicifyVariableName(fieldName); -#endif - - int currentParam = rows.Count + skip; - row = new DebugUI.Table.Row() - { - displayName = fieldName, - children = { CreateVolumeParameterWidget(Strings.interpolatedValue, stackComponent.parameterList[currentParam]) }, - }; - - foreach (var volume in volumes) - { - VolumeParameter param = null; - var profile = volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile; - if (profile.TryGet(selectedType, out VolumeComponent component)) - param = component.parameterList[currentParam]; - row.children.Add(CreateVolumeParameterWidget(volume.name + " (" + profile.name + ")", param, () => !component.parameterList[currentParam].overrideState)); - } - - row.children.Add(CreateVolumeParameterWidget(Strings.defaultValue, inst.parameterList[currentParam])); - rows.Add(row); - } - - var fields = type - .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .OrderBy(t => t.MetadataToken); - foreach (var field in fields) - { - if (field.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length != 0) - { - skip++; - continue; - } - var fieldType = field.FieldType; - if (fieldType.IsSubclassOf(typeof(VolumeParameter))) - AddRow(field, baseName ?? string.Empty, skip); - else if (!fieldType.IsArray && fieldType.IsClass) - skip += AddParameterRows(fieldType, baseName ?? (field.Name + " "), skip); - } - return skip; - } + displayName = Strings.parameter, + isReadOnly = true + }; - AddParameterRows(selectedType); - foreach (var r in rows.OrderBy(t => t.displayName)) - table.children.Add(r); + var resolutionChain = GetResolutionChain(data); + if (resolutionChain.Count == 0) + return table; - data.volumeDebugSettings.RefreshVolumes(volumes); - for (int i = 0; i < volumes.Length; i++) - table.SetColumnVisibility(i + 1, data.volumeDebugSettings.VolumeHasInfluence(volumes[i])); + GenerateTableRows(table, resolutionChain); + GenerateTableColumns(table, data, resolutionChain); float timer = 0.0f, refreshRate = 0.2f; + var volumes = data.volumeDebugSettings.GetVolumes(); table.isHiddenCallback = () => { timer += Time.deltaTime; @@ -315,16 +264,9 @@ void AddRow(FieldInfo f, string prefix, int skip) { if (data.volumeDebugSettings.selectedCamera != null) { - var newVolumes = data.volumeDebugSettings.GetVolumes(); - if (!data.volumeDebugSettings.RefreshVolumes(newVolumes)) - { - for (int i = 0; i < newVolumes.Length; i++) - { - var visible = data.volumeDebugSettings.VolumeHasInfluence(newVolumes[i]); - table.SetColumnVisibility(i + 1, visible); - } - } + SetTableColumnVisibility(data, table); + var newVolumes = data.volumeDebugSettings.GetVolumes(); if (!volumes.SequenceEqual(newVolumes)) { volumes = newVolumes; @@ -339,6 +281,139 @@ void AddRow(FieldInfo f, string prefix, int skip) return table; } + + private static void SetTableColumnVisibility(DebugDisplaySettingsVolume data, DebugUI.Table table) + { + var newResolutionChain = GetResolutionChain(data); + for (int i = 1; i < newResolutionChain.Count; i++) // We always skip the interpolated stack that is in index 0 + { + bool visible = true; + if (newResolutionChain[i].volume != null) + { + visible = data.volumeDebugSettings.VolumeHasInfluence(newResolutionChain[i].volume); + } + else + { + visible = newResolutionChain[i].volumeComponent.active; + + if (visible) + { + bool atLeastOneParameterIsOverriden = false; + foreach (var parameter in newResolutionChain[i].volumeComponent.parameterList) + { + if (parameter.overrideState == true) + { + atLeastOneParameterIsOverriden = true; + break; + } + } + + visible &= atLeastOneParameterIsOverriden; + } + } + + table.SetColumnVisibility(i, visible); + } + } + + private static void GenerateTableColumns(DebugUI.Table table, DebugDisplaySettingsVolume data, List resolutionChain) + { + for (int i = 0; i < resolutionChain.Count; ++i) + { + var chain = resolutionChain[i]; + int iRowIndex = -1; + + if (chain.volume != null) + { + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(new DebugUI.Value() + { + nameAndTooltip = chain.nameAndTooltip, + getter = () => + { + var scope = chain.volume.isGlobal ? Strings.global : Strings.local; + var weight = data.volumeDebugSettings.GetVolumeWeight(chain.volume); + return scope + " (" + (weight * 100f) + "%)"; + }, + refreshRate = 0.2f + }); + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(new DebugUI.ObjectField() { displayName = string.Empty, getter = () => chain.volume }); + } + else + { + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(new DebugUI.Value() + { + nameAndTooltip = chain.nameAndTooltip, + getter = () => string.Empty + }); + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(s_EmptyDebugUIValue); + } + + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(chain.volumeProfile != null ? new DebugUI.ObjectField() { displayName = string.Empty, getter = () => chain.volumeProfile } : + s_EmptyDebugUIValue); + + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(s_EmptyDebugUIValue); + + bool isResultParameter = i == 0; + for (int j = 0; j < chain.volumeComponent.parameterList.Count; ++j) + { + var parameter = chain.volumeComponent.parameterList[j]; + ((DebugUI.Table.Row)table.children[++iRowIndex]).children.Add(CreateVolumeParameterWidget(chain.nameAndTooltip.name, isResultParameter, parameter)); + } + } + } + + private static void GenerateTableRows(DebugUI.Table table, List resolutionChain) + { + // First row for volume info + var volumeInfoRow = new DebugUI.Table.Row() + { + displayName = Strings.volumeInfo, + opened = true, // Open by default for the in-game view + }; + + table.children.Add(volumeInfoRow); + + // Second row, links to volume gameobjects + var gameObjectRow = new DebugUI.Table.Row() + { + displayName = Strings.gameObject, + }; + + table.children.Add(gameObjectRow); + + // Third row, links to volume profile assets + var volumeProfileRow = new DebugUI.Table.Row() + { + displayName = Strings.volumeProfile, + }; + table.children.Add(volumeProfileRow); + + var separatorRow = new DebugUI.Table.Row() + { + displayName = string.Empty , + }; + + table.children.Add(separatorRow); + + var results = resolutionChain[0].volumeComponent; + for (int i = 0; i < results.parameterList.Count; ++i) + { + var parameter = results.parameterList[i]; + +#if UNITY_EDITOR + string displayName = UnityEditor.ObjectNames.NicifyVariableName(parameter.debugId); // In the editor, make the name more readable +#elif DEVELOPMENT_BUILD + string displayName = parameter.debugId; // In the development player, just the debug id +#else + string displayName = i.ToString(); // Everywhere else, just a dummy id ( TODO: The Volume panel code should be stripped completely in nom-development builds ) +#endif + + table.children.Add(new DebugUI.Table.Row() + { + displayName = displayName + }); + } + } } [DisplayInfo(name = "Volume", order = int.MaxValue)] diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/VolumeDebugSettings.cs b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/VolumeDebugSettings.cs index 3987abd897b..7e938642230 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/VolumeDebugSettings.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/VolumeDebugSettings.cs @@ -80,7 +80,10 @@ public IEnumerable cameras if (camera.cameraType != CameraType.Preview && camera.cameraType != CameraType.Reflection) { - if (camera.TryGetComponent(out T additionalData)) + if (!camera.TryGetComponent(out T additionalData)) + additionalData = camera.gameObject.AddComponent(); + + if (additionalData != null) m_Cameras.Add(camera); } } @@ -270,21 +273,9 @@ public bool RefreshVolumes(Volume[] newVolumes) /// The weight of the volume public float GetVolumeWeight(Volume volume) { - if (weights == null) - return 0; - - float total = 0f, weight = 0f; - for (int i = 0; i < volumes.Length; i++) - { - weight = weights[i]; - weight *= 1f - total; - total += weight; - - if (volumes[i] == volume) - return weight; - } - - return 0f; + // TODO: Store the calculated weight in the stack for the volumes that have influence and return it here + var triggerPos = selectedCameraPosition; + return ComputeWeight(volume, triggerPos); } /// @@ -294,14 +285,9 @@ public float GetVolumeWeight(Volume volume) /// If the volume has influence public bool VolumeHasInfluence(Volume volume) { - if (weights == null) - return false; - - int index = Array.IndexOf(volumes, volume); - if (index == -1) - return false; - - return weights[index] != 0f; + // TODO: Store the calculated weight in the stack for the volumes that have influence and return it here + var triggerPos = selectedCameraPosition; + return ComputeWeight(volume, triggerPos) > 0.0f; } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs index 3a1a4e3faef..65965a61981 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs @@ -162,6 +162,17 @@ internal static void FindParameters(object o, List parameters, if (filter?.Invoke(field) ?? true) { VolumeParameter volumeParameter = (VolumeParameter)field.GetValue(o); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + var attr = (DisplayInfoAttribute[])field.GetCustomAttributes(typeof(DisplayInfoAttribute), true); + if (attr.Length != 0) + { + volumeParameter.debugId = attr[0].name; + } + else + { + volumeParameter.debugId = field.Name; + } +#endif parameters.Add(volumeParameter); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeParameter.cs b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeParameter.cs index a92c1291281..0a08f1ba9a8 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeParameter.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeParameter.cs @@ -18,6 +18,10 @@ namespace UnityEngine.Rendering /// public abstract class VolumeParameter : ICloneable { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + internal string debugId { get; set; } +#endif + /// /// A beautified string for debugger output. This is set on a DebuggerDisplay on every /// parameter types. diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightEvaluation.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightEvaluation.hlsl index 975107cd0c9..2f0d63f0af1 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightEvaluation.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightEvaluation.hlsl @@ -276,12 +276,7 @@ SHADOW_TYPE EvaluateShadow_Directional( LightLoopContext lightLoopContext, Posit } else if (shadowSplitIndex == int(_CascadeShadowCount) - 1) { - // float fade = lightLoopContext.shadowContext.fade; - float3 camToPixel = posInput.positionWS - GetPrimaryCameraPosition(); - float distanceCamToPixel2 = dot(camToPixel, camToPixel); - - HDDirectionalShadowData dsd = lightLoopContext.shadowContext.directionalShadowData; - float fade = saturate(distanceCamToPixel2 * dsd.fadeScale + dsd.fadeBias); + float fade = lightLoopContext.shadowContext.fade; // In the transition code (both dithering and blend) we use shadow = lerp( shadow, 1.0, fade ) for last transition // mean if we expend the code we have (shadow * (1 - fade) + fade). Here to make transition with shadow mask // we will remove fade and add fade * shadowMask which mean we do a lerp with shadow mask @@ -289,7 +284,20 @@ SHADOW_TYPE EvaluateShadow_Directional( LightLoopContext lightLoopContext, Posit } // See comment in EvaluateBSDF_Punctual - shadow = light.nonLightMappedOnly ? min(shadowMask, shadow) : shadow; + if (light.nonLightMappedOnly) + { + shadow = min(shadowMask, shadow); + } + else + { + // Use shadowmask when shadow value ​​cannot be retrieved due to shadow caster culling. + float3 camToPixel = posInput.positionWS - GetPrimaryCameraPosition(); + float distanceCamToPixel2 = dot(camToPixel, camToPixel); + + HDDirectionalShadowData dsd = lightLoopContext.shadowContext.directionalShadowData; + float alpha = saturate(distanceCamToPixel2 * dsd.fadeScale + dsd.fadeBias); + shadow = min(shadow, lerp(1.0, shadowMask, alpha * alpha)); + } #endif shadow = lerp(shadowMask.SHADOW_TYPE_REPLICATE, shadow, light.shadowDimmer); diff --git a/Packages/com.unity.render-pipelines.universal/Documentation~/TableOfContents.md b/Packages/com.unity.render-pipelines.universal/Documentation~/TableOfContents.md index 62c2c716619..37554721642 100644 --- a/Packages/com.unity.render-pipelines.universal/Documentation~/TableOfContents.md +++ b/Packages/com.unity.render-pipelines.universal/Documentation~/TableOfContents.md @@ -143,7 +143,8 @@ * [Scriptable Render Passes](renderer-features/scriptable-render-passes.md) * [Scriptable Render Passes](renderer-features/intro-to-scriptable-render-passes.md) * [Write a Scriptable Render Pass](renderer-features/write-a-scriptable-render-pass.md) - * [Inject a pass via scripting](customize/inject-render-pass-via-script.md) + * [Inject a pass via scripting](customize/inject-render-pass-via-script.md) + * [Restrict a render pass to a scene area](customize/restrict-render-pass-scene-area.md) * [Scriptable Renderer Features](renderer-features/scriptable-renderer-features/scriptable-renderer-features-landing.md) * [Introduction to Scriptable Renderer Features](renderer-features/scriptable-renderer-features/intro-to-scriptable-renderer-features.md) * [Inject a custom render pass using a Scriptable Renderer Feature](renderer-features/scriptable-renderer-features/inject-a-pass-using-a-scriptable-renderer-feature.md) diff --git a/Packages/com.unity.render-pipelines.universal/Documentation~/customize/restrict-render-pass-scene-area.md b/Packages/com.unity.render-pipelines.universal/Documentation~/customize/restrict-render-pass-scene-area.md new file mode 100644 index 00000000000..d04e30cd0e0 --- /dev/null +++ b/Packages/com.unity.render-pipelines.universal/Documentation~/customize/restrict-render-pass-scene-area.md @@ -0,0 +1,112 @@ +# Restrict a render pass to a scene area in URP + +To restrict a render pass to a specific area of a scene, add a [volume](../Volumes.md) to the scene, then add code to your render pass and your shader to check if the camera is inside the volume. + +Follow these steps: + +1. Update your shader code to enable or disable your custom rendering effect based on a Boolean value. + + For example, add the following code to your shader: + + ```hlsl + Pass + { + ... + + // Add a variable to enable or disable your custom rendering effect + float _Enabled; + + ... + + float4 Frag(Varyings input) : SV_Target0 + { + ... + + // Return the color with the effect if the variable is 1, or the original color if the variable is 0 + if (_Enabled == 1){ + return colorWithEffect; + } else { + return originalColor; + } + } + } + ``` + +2. Create a script that implements the `VolumeComponent` class. This creates a volume override component that you can add to a volume. + + ```c# + using UnityEngine; + using UnityEngine.Rendering; + + public class MyVolumeOverride : VolumeComponent + { + } + ``` + +3. In the **Hierarchy** window, select the **Add** (**+**) button, then select **GameObject** > **Volume** > **Box Volume**. + +4. In the **Inspector** window for the new box volume, under **Volume**, select **New** to create a new volume profile. + +5. Select **Add override**, then select your volume override component, for example **My Volume Override**. + +6. Add a property to the volume override script. Unity adds the property in the **Inspector** window of the volume override. + + For example: + + ```c# + public class MyVolumeOverride : VolumeComponent + { + // Add an 'Effect Enabled' checkbox to the Volume Override, with a default value of true. + public BoolParameter effectEnabled = new BoolParameter(true); + } + ``` + +5. In your custom pass, use the `GetComponent` API to get the volume override component and check the value of the property. + + For example: + + ```c# + class myCustomPass : ScriptableRenderPass + { + + ... + + public void Setup(Material material) + { + // Get the volume override component + MyVolumeOverride myOverride = VolumeManager.instance.stack.GetComponent(); + + // Get the value of the 'Effect Enabled' property + bool effectStatus = myOverride.effectEnabled.overrideState ? myOverride.effectEnabled.value : false; + } + } + ``` + +6. Pass the value of the property to the variable you added to the shader code. + + For example: + + ```c# + class myCustomPass : ScriptableRenderPass + { + + ... + + public void Setup(Material material) + { + MyVolumeOverride myOverride = VolumeManager.instance.stack.GetComponent(); + bool effectStatus = myOverride.effectEnabled.overrideState ? myOverride.effectEnabled.value : false; + + // Pass the value to the shader + material.SetFloat("_Enabled", effectStatus ? 1 : 0); + } + } + ``` + +Your custom rendering effect is now enabled when the camera is inside the volume, and disabled when the camera is outside the volume. + +## Additional resources + +- [Write a Scriptable Render Pass](../renderer-features/write-a-scriptable-render-pass.md) +- [Volumes in URP](../volumes.md) +- [Writing custom shaders in URP](../writing-custom-shaders-urp.md) diff --git a/Packages/com.unity.render-pipelines.universal/Documentation~/renderer-features/scriptable-render-passes.md b/Packages/com.unity.render-pipelines.universal/Documentation~/renderer-features/scriptable-render-passes.md index cc38e8984da..d3ff9078b97 100644 --- a/Packages/com.unity.render-pipelines.universal/Documentation~/renderer-features/scriptable-render-passes.md +++ b/Packages/com.unity.render-pipelines.universal/Documentation~/renderer-features/scriptable-render-passes.md @@ -7,6 +7,7 @@ Use the `ScriptableRenderPass` API to write a custom render pass. You can then i |[Introduction to Scriptable Render Passes](intro-to-scriptable-render-passes.md)|What a Scriptable Render Pass is, and how you can inject it into a scene.| |[Write a Scriptable Render Pass](write-a-scriptable-render-pass.md)|An example of a `ScriptableRenderPass` instance that uses `Blit` to create a red tint effect.| |[Inject a pass via scripting](../customize/inject-render-pass-via-script.md)|Use the `RenderPipelineManager` API to inject a render pass, without using a Scriptable Renderer Feature.| +|[Restrict a render pass to a scene area](../customize/restrict-render-pass-scene-area.md) | Enable a custom rendering effect only if the camera is inside a volume in a scene. | ## Additional resources diff --git a/Tests/SRPTests/Projects/HDRP_Tests/Assets/GraphicTests/Scenes/2x_Lighting/2324_Shadow_Mask_Directional_Blending/Scene Settings Profile.asset b/Tests/SRPTests/Projects/HDRP_Tests/Assets/GraphicTests/Scenes/2x_Lighting/2324_Shadow_Mask_Directional_Blending/Scene Settings Profile.asset index b3455c51fc2..62b02387ca5 100644 --- a/Tests/SRPTests/Projects/HDRP_Tests/Assets/GraphicTests/Scenes/2x_Lighting/2324_Shadow_Mask_Directional_Blending/Scene Settings Profile.asset +++ b/Tests/SRPTests/Projects/HDRP_Tests/Assets/GraphicTests/Scenes/2x_Lighting/2324_Shadow_Mask_Directional_Blending/Scene Settings Profile.asset @@ -16,7 +16,7 @@ MonoBehaviour: interCascadeBorders: 1 maxShadowDistance: m_OverrideState: 1 - m_Value: 5 + m_Value: 7 directionalTransmissionMultiplier: m_OverrideState: 1 m_Value: 1