diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 4aaefaaf1eb..5d3740022b0 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -128,6 +128,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed error Maximum allowed thread group count is 65535 when resolution is very high. - LOD meshes are now properly stripped based on the maximum lod value parameters contained in the HDRP asset. - Fixed an inconsistency in the LOD group UI where LOD bias was not the right one. +- Fixed outlines in transitions between post-processed and plain regions in the graphics compositor (case 1278775). ### Changed - Preparation pass for RTSSShadows to be supported by render graph. diff --git a/com.unity.render-pipelines.high-definition/Editor/Compositor/CompositionLayerUI.Drawers.cs b/com.unity.render-pipelines.high-definition/Editor/Compositor/CompositionLayerUI.Drawers.cs index 6fe41ad78aa..e5796f44981 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Compositor/CompositionLayerUI.Drawers.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Compositor/CompositionLayerUI.Drawers.cs @@ -30,6 +30,7 @@ static partial class Styles static public readonly GUIContent k_AAMode = EditorGUIUtility.TrTextContent("Post Anti-aliasing", "To override the postprocess Anti-aliasing mode, activate the option by clicking on the check-box and then select the desired value."); static public readonly GUIContent k_CullingMask = EditorGUIUtility.TrTextContent("Culling Mask", "To override the culling mask, activate the option by clicking on the check-box and then select the desired value."); static public readonly GUIContent k_VolumeMask = EditorGUIUtility.TrTextContent("Volume Mask", "To override the volume mask, activate the option by clicking on the check-box and then select the desired value."); + static public readonly GUIContent k_AlphaRange = EditorGUIUtility.TrTextContent("Alpha Range", "The range of alpha values used when transitioning from post-processed to plain image regions. A smaller range will result in a steeper transition."); static public readonly string k_AlphaInfoPost = "The use of AOVs properties in a player require to to enable the Runtime AOV API support in the HDRP quality settings."; @@ -178,6 +179,58 @@ public static void DrawStackedLayerProperties(Rect rect, SerializedCompositionLa rect.y += CompositorStyle.k_Spacing; EditorGUI.PropertyField(rect, serializedProperties.clearAlpha, Styles.k_ClearAlpha); + rect.y += 1.0f * CompositorStyle.k_Spacing; + + // Draw a min/max slider for tha alpha range + { + const float spacing = 5; + var labelRect = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height); + EditorGUI.PrefixLabel(labelRect, Styles.k_AlphaRange); + + var minLabelRect = rect; + minLabelRect.x += EditorGUIUtility.labelWidth; + minLabelRect.width = EditorGUIUtility.fieldWidth / 2; + serializedProperties.alphaMin.floatValue = EditorGUI.FloatField(minLabelRect, serializedProperties.alphaMin.floatValue); + + GUI.SetNextControlName("AlphaMinMaxSlider"); + var sliderRect = rect; + sliderRect.x += EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth / 2 + spacing; + sliderRect.width -= (EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth + 2 * spacing); + float minVal = serializedProperties.alphaMin.floatValue; + float maxVal = serializedProperties.alphaMax.floatValue; + + EditorGUI.MinMaxSlider(sliderRect, ref minVal, ref maxVal, 0, 1); + if (serializedProperties.alphaMin.floatValue != minVal || serializedProperties.alphaMax.floatValue != maxVal) + { + // Note: We need to move the focus when the slider changes, otherwise the textField will not update + GUI.FocusControl("AlphaMinMaxSlider"); + serializedProperties.alphaMin.floatValue = minVal; + serializedProperties.alphaMax.floatValue = maxVal; + } + + var maxLabelRect = rect; + maxLabelRect.x = sliderRect.x + sliderRect.width + spacing; + maxLabelRect.width = EditorGUIUtility.fieldWidth / 2; + serializedProperties.alphaMax.floatValue = EditorGUI.FloatField(maxLabelRect, serializedProperties.alphaMax.floatValue); + + // sanity checks + if (serializedProperties.alphaMax.floatValue < serializedProperties.alphaMin.floatValue) + { + serializedProperties.alphaMax.floatValue = serializedProperties.alphaMin.floatValue; + } + if (serializedProperties.alphaMax.floatValue > 1) + { + serializedProperties.alphaMax.floatValue = 1; + } + if (serializedProperties.alphaMin.floatValue > serializedProperties.alphaMax.floatValue) + { + serializedProperties.alphaMin.floatValue = serializedProperties.alphaMax.floatValue; + } + if (serializedProperties.alphaMin.floatValue < 0) + { + serializedProperties.alphaMin.floatValue = 0; + } + } rect.y += 1.5f * CompositorStyle.k_Spacing; // The clear mode should be visible / configurable only for the first layer in the stack. For the other layers we set a camera-stacking specific clear-mode . diff --git a/com.unity.render-pipelines.high-definition/Editor/Compositor/SerializedCompositionLayer.cs b/com.unity.render-pipelines.high-definition/Editor/Compositor/SerializedCompositionLayer.cs index 9a88b20f6dd..1f08919219f 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Compositor/SerializedCompositionLayer.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Compositor/SerializedCompositionLayer.cs @@ -30,6 +30,8 @@ internal class SerializedCompositionLayer public SerializedProperty aovBitmask; public SerializedProperty inputFilters; public SerializedProperty positionInStack; + public SerializedProperty alphaMin; + public SerializedProperty alphaMax; public List filterList = new List(); @@ -60,6 +62,8 @@ public SerializedCompositionLayer(SerializedProperty root) aovBitmask = root.FindPropertyRelative("m_AOVBitmask"); inputFilters = root.FindPropertyRelative("m_InputFilters"); positionInStack = root.FindPropertyRelative("m_LayerPositionInStack"); + alphaMin = root.FindPropertyRelative("m_AlphaMin"); + alphaMax = root.FindPropertyRelative("m_AlphaMax"); for (int index = 0; index < inputFilters.arraySize; index++) { @@ -91,7 +95,8 @@ public float GetPropertiesHeight() EditorGUI.GetPropertyHeight(cullingMaskProperty, null) + EditorGUI.GetPropertyHeight(volumeMask, null) + EditorGUI.GetPropertyHeight(inputFilters, null) + - EditorGUIUtility.singleLineHeight * 7; //for the heading and pading + EditorGUI.GetPropertyHeight(alphaMin, null) + // we use a min/max slider in the UI so it takes a sinle line, so we don't need to count the alphaMax + EditorGUIUtility.singleLineHeight * 7; // for the heading and pading if (inputFilters.arraySize > 0) { diff --git a/com.unity.render-pipelines.high-definition/Runtime/Compositor/AdditionalCompositorData.cs b/com.unity.render-pipelines.high-definition/Runtime/Compositor/AdditionalCompositorData.cs index 972c946cb10..4f72320ced8 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Compositor/AdditionalCompositorData.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Compositor/AdditionalCompositorData.cs @@ -17,6 +17,8 @@ internal class AdditionalCompositorData : MonoBehaviour public bool clearAlpha = true; // Clearing the alpha allows the post process to run only on the pixels covered by a stacked camera (and not the previous ones). public BackgroundFitMode imageFitMode = BackgroundFitMode.Stretch; public List layerFilters; + public float alphaMax = 1.0f; + public float alphaMin = 0.0f; public void Init(List layerFilters, bool clearAlpha) { @@ -36,6 +38,8 @@ public void ResetData() layerFilters = null; } + alphaMax = 1.0f; + alphaMin = 0.0f; } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionLayer.cs b/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionLayer.cs index 582cf0c4b88..ad3e8fc6cc2 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionLayer.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionLayer.cs @@ -136,6 +136,10 @@ public float aspectRatio // Returns true if this layer is using a camera that was cloned internally for drawing bool isUsingACameraClone => !m_LayerCamera.Equals(m_Camera); + // The input alpha will be mapped between the min and max range when blending between the post-processed and plain image regions. This way the user can controls how steep is the transition. + [SerializeField] float m_AlphaMin = 0.0f; + [SerializeField] float m_AlphaMax = 1.0f; + private CompositorLayer() { } @@ -469,6 +473,9 @@ public void SetAdditionalLayerData() if (layerData != null) { layerData.Init(m_InputFilters, m_ClearAlpha); + + layerData.alphaMin = m_AlphaMin; + layerData.alphaMax = m_AlphaMax; } } } @@ -507,7 +514,7 @@ public void UpdateOutputCamera() if (m_Type == LayerType.Image) { var compositorData = m_LayerCamera.GetComponent(); - if(compositorData) + if (compositorData) compositorData.clearColorTexture = (m_Show && m_InputTexture != null) ? m_InputTexture : Texture2D.blackTexture; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionManager.cs index d1803fcf875..ed8cbfe6098 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionManager.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Compositor/CompositionManager.cs @@ -838,5 +838,28 @@ static public Camera GetSceceCamera() static public CompositionManager GetInstance() => s_CompositorInstance ?? (s_CompositorInstance = GameObject.FindObjectOfType(typeof(CompositionManager), true) as CompositionManager); + static public Vector4 GetAlphaScaleAndBiasForCamera(HDCamera hdCamera) + { + AdditionalCompositorData compositorData = null; + hdCamera.camera.TryGetComponent(out compositorData); + + if (compositorData) + { + float alphaMin = compositorData.alphaMin; + float alphaMax = compositorData.alphaMax; + + if (alphaMax == alphaMin) + alphaMax += 0.0001f; // Mathf.Epsilon is too small and in this case it creates precission issues + + float alphaScale = 1.0f / (alphaMax - alphaMin); + float alphaBias = -alphaMin * alphaScale; + + return new Vector4(alphaScale, alphaBias, 0.0f, 0.0f); + } + + // No compositor-specific data for this camera/layer, just return the default/neutral scale and bias + return new Vector4(1.0f, 0.0f, 0.0f, 0.0f); + } + } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs index 92ac08735f4..946b1dec4ec 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs @@ -885,6 +885,8 @@ struct UberPostParameters public Vector4 bloomBicubicParams; public Vector4 bloomDirtTileOffset; public Vector4 bloomThreshold; + + public Vector4 alphaScaleBias; } UberPostParameters PrepareUberPostParameters(HDCamera hdCamera, bool isSceneView) @@ -918,10 +920,23 @@ UberPostParameters PrepareUberPostParameters(HDCamera hdCamera, bool isSceneView PrepareChromaticAberrationParameters(ref parameters, featureFlags); PrepareVignetteParameters(ref parameters, featureFlags); PrepareUberBloomParameters(ref parameters, hdCamera); + PrepareAlphaScaleParameters(ref parameters, hdCamera); return parameters; } + void PrepareAlphaScaleParameters(ref UberPostParameters parameters, HDCamera camera) + { + if (m_EnableAlpha) + { + parameters.alphaScaleBias = Compositor.CompositionManager.GetAlphaScaleAndBiasForCamera(camera); + } + else + { + parameters.alphaScaleBias = new Vector4(1.0f, 0.0f, 0.0f, 0.0f); + } + } + static void DoUberPostProcess(in UberPostParameters parameters, RTHandle source, RTHandle destination, @@ -956,6 +971,8 @@ static void DoUberPostProcess(in UberPostParameters parameters, cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomDirtScaleOffset, parameters.bloomDirtTileOffset); cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomThreshold, parameters.bloomThreshold); + // Alpha scale and bias (only used when alpha is enabled) + cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._AlphaScaleBias, parameters.alphaScaleBias); // Dispatch uber post cmd.SetComputeVectorParam(parameters.uberPostCS, "_DebugFlags", new Vector4(parameters.outputColorLog ? 1 : 0, 0, 0, 0)); diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/UberPost.compute b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/UberPost.compute index 8a0b252c5a1..f49084bdbd2 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/UberPost.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/UberPost.compute @@ -44,6 +44,7 @@ CBUFFER_START(cb0) float4 _BloomDirtScaleOffset; float4 _BloomBicubicParams; float4 _DebugFlags; + float4 _AlphaScaleBias; CBUFFER_END #define DistCenter _DistortionParams1.xy @@ -77,6 +78,9 @@ CBUFFER_END #define OutputLogEnabled _DebugFlags.x +#define AlphaScale _AlphaScaleBias.x +#define AlphaBias _AlphaScaleBias.y + float2 DistortUV(float2 uv) { // Lens distortion @@ -218,8 +222,10 @@ void Uber(uint3 dispatchThreadId : SV_DispatchThreadID) // Alpha mask #ifdef ENABLE_ALPHA // Post processing is not applied on pixels with zero alpha + // The alpha scale and bias control how steep is the transition between the post-processed and plain regions + float alpha = inputColor.a * AlphaScale + AlphaBias; // Saturate is necessary to avoid issues when additive blending pushes the alpha over 1. - color.xyz = lerp(inputColor.xyz, color.xyz, saturate(inputColor.a)); + color.xyz = lerp(inputColor.xyz, color.xyz, saturate(alpha)); #endif // Done diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs index b69648400b5..ece081b7e85 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs @@ -714,6 +714,8 @@ static class HDShaderIDs public static readonly int _ChromaSpectralLut = Shader.PropertyToID("_ChromaSpectralLut"); public static readonly int _ChromaParams = Shader.PropertyToID("_ChromaParams"); + public static readonly int _AlphaScaleBias = Shader.PropertyToID("_AlphaScaleBias"); + public static readonly int _VignetteParams1 = Shader.PropertyToID("_VignetteParams1"); public static readonly int _VignetteParams2 = Shader.PropertyToID("_VignetteParams2"); public static readonly int _VignetteColor = Shader.PropertyToID("_VignetteColor");