diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index cd7a777a286..b1a2fc0499e 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -152,6 +152,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added compute shader stripping. - Added Cull Mode option for opaque materials and ShaderGraphs. - Added scene view exposure override. +- Added support for exposure curve remapping for min/max limits. ### Fixed - Fix when rescale probe all direction below zero (1219246) diff --git a/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs b/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs index 14fc781f072..63f9e976fc5 100644 --- a/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs +++ b/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs @@ -16,7 +16,9 @@ sealed class ExposureEditor : VolumeComponentEditor SerializedDataParameter m_LimitMin; SerializedDataParameter m_LimitMax; SerializedDataParameter m_CurveMap; - + SerializedDataParameter m_CurveMin; + SerializedDataParameter m_CurveMax; + SerializedDataParameter m_AdaptationMode; SerializedDataParameter m_AdaptationSpeedDarkToLight; SerializedDataParameter m_AdaptationSpeedLightToDark; @@ -52,7 +54,9 @@ public override void OnEnable() m_LimitMin = Unpack(o.Find(x => x.limitMin)); m_LimitMax = Unpack(o.Find(x => x.limitMax)); m_CurveMap = Unpack(o.Find(x => x.curveMap)); - + m_CurveMin = Unpack(o.Find(x => x.limitMinCurveMap)); + m_CurveMax = Unpack(o.Find(x => x.limitMaxCurveMap)); + m_AdaptationMode = Unpack(o.Find(x => x.adaptationMode)); m_AdaptationSpeedDarkToLight = Unpack(o.Find(x => x.adaptationSpeedDarkToLight)); m_AdaptationSpeedLightToDark = Unpack(o.Find(x => x.adaptationSpeedLightToDark)); @@ -137,11 +141,18 @@ public override void OnInspectorGUI() // EditorGUILayout.HelpBox("Luminance source buffer isn't supported yet.", MessageType.Warning); if (mode == (int)ExposureMode.CurveMapping) + { PropertyField(m_CurveMap); - + PropertyField(m_CurveMin, EditorGUIUtility.TrTextContent("Limit Min")); + PropertyField(m_CurveMax, EditorGUIUtility.TrTextContent("Limit Max")); + } + else if (!(mode == (int)ExposureMode.AutomaticHistogram && m_HistogramCurveRemapping.value.boolValue)) + { + PropertyField(m_LimitMin); + PropertyField(m_LimitMax); + } + PropertyField(m_Compensation); - PropertyField(m_LimitMin); - PropertyField(m_LimitMax); if(mode == (int)ExposureMode.AutomaticHistogram) { @@ -152,6 +163,8 @@ public override void OnInspectorGUI() if (m_HistogramCurveRemapping.value.boolValue) { PropertyField(m_CurveMap); + PropertyField(m_CurveMin, EditorGUIUtility.TrTextContent("Limit Min")); + PropertyField(m_CurveMax, EditorGUIUtility.TrTextContent("Limit Max")); } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs index fdef62f313f..45b560bad65 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs @@ -64,6 +64,20 @@ public sealed class Exposure : VolumeComponent, IPostProcessComponent [Tooltip("Specifies a curve that remaps the Scene exposure on the x-axis to the exposure you want on the y-axis.")] public AnimationCurveParameter curveMap = new AnimationCurveParameter(AnimationCurve.Linear(-10f, -10f, 20f, 20f)); // TODO: Use TextureCurve instead? + /// + /// Specifies a curve that determines for each current exposure value (x-value) what minimum value is allowed to auto-adaptation (y-axis). + /// This parameter is only used when is set. + /// + [Tooltip("Specifies a curve that determines for each current exposure value (x-value) what minimum value is allowed to auto-adaptation (y-axis).")] + public AnimationCurveParameter limitMinCurveMap = new AnimationCurveParameter(AnimationCurve.Linear(-10f, -12f, 20f, 18f)); + + /// + /// Specifies a curve that determines for each current exposure value (x-value) what maximum value is allowed to auto-adaptation (y-axis). + /// This parameter is only used when is set. + /// + [Tooltip("Specifies a curve that determines for each current exposure value (x-value) what maximum value is allowed to auto-adaptation (y-axis).")] + public AnimationCurveParameter limitMaxCurveMap = new AnimationCurveParameter(AnimationCurve.Linear(-10f, -8f, 20f, 22f)); + /// /// Specifies the method that HDRP uses to change the exposure when the Camera moves from dark to light and vice versa. /// This parameter is only used when or is set. 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 9f0b17d71bf..487069c6a43 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs @@ -1017,11 +1017,15 @@ RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSystem) previous = camera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.Exposure); } - void PrepareExposureCurveData(AnimationCurve curve, out float min, out float max) + void PrepareExposureCurveData(out float min, out float max) { + var curve = m_Exposure.curveMap.value; + var minCurve = m_Exposure.limitMinCurveMap.value; + var maxCurve = m_Exposure.limitMaxCurveMap.value; + if (m_ExposureCurveTexture == null) { - m_ExposureCurveTexture = new Texture2D(k_ExposureCurvePrecision, 1, TextureFormat.RHalf, false, true) + m_ExposureCurveTexture = new Texture2D(k_ExposureCurvePrecision, 1, TextureFormat.RGBAHalf, false, true) { name = "Exposure Curve", filterMode = FilterMode.Bilinear, @@ -1029,6 +1033,11 @@ void PrepareExposureCurveData(AnimationCurve curve, out float min, out float max }; } + bool minCurveHasPoints = minCurve.length > 0; + bool maxCurveHasPoints = maxCurve.length > 0; + float defaultMin = -100.0f; + float defaultMax = 100.0f; + var pixels = m_ExposureCurveColorArray; // Fail safe in case the curve is deleted / has 0 point @@ -1047,7 +1056,13 @@ void PrepareExposureCurveData(AnimationCurve curve, out float min, out float max float step = (max - min) / (k_ExposureCurvePrecision - 1f); for (int i = 0; i < k_ExposureCurvePrecision; i++) - pixels[i] = new Color(curve.Evaluate(min + step * i), 0f, 0f, 0f); + { + float currTime = min + step * i; + pixels[i] = new Color(curve.Evaluate(currTime), + minCurveHasPoints ? minCurve.Evaluate(currTime) : defaultMin, + maxCurveHasPoints ? maxCurve.Evaluate(currTime) : defaultMax, + 0f); + } } m_ExposureCurveTexture.SetPixels(pixels); @@ -1124,7 +1139,7 @@ void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer) } else if (m_Exposure.mode.value == ExposureMode.CurveMapping) { - PrepareExposureCurveData(m_Exposure.curveMap.value, out float min, out float max); + PrepareExposureCurveData(out float min, out float max); cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, m_ExposureCurveTexture); cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, min, max, 0f)); cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, new Vector4(min, max, ColorUtils.lensImperfectionExposureScale, ColorUtils.s_LightMeterCalibrationConstant)); @@ -1199,7 +1214,7 @@ void DoHistogramBasedExposure(CommandBuffer cmd, HDCamera camera, RTHandle sourc m_ExposureVariants[3] = 0; if (m_Exposure.histogramUseCurveRemapping.value) { - PrepareExposureCurveData(m_Exposure.curveMap.value, out float min, out float max); + PrepareExposureCurveData(out float min, out float max); cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, new Vector4(min, max, ColorUtils.lensImperfectionExposureScale, ColorUtils.s_LightMeterCalibrationConstant)); m_ExposureVariants[3] = 2; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute index 88e7a7490ce..3d313dab02c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute @@ -127,8 +127,11 @@ void KReduction(uint2 groupId : SV_GroupID, uint2 groupThreadId : SV_GroupThread case 2u: { // Curve remapping - float exposure = CurveRemap(avgLuminance); + float minExposure = ParamExposureLimitMin; + float maxExposure = ParamExposureLimitMax; + float exposure = CurveRemap(avgLuminance, minExposure, maxExposure); exposure = AdaptExposure(exposure - ParamExposureCompensation); + exposure = clamp(exposure, minExposure, maxExposure); _OutputTexture[groupId.xy] = float2(ConvertEV100ToExposure(exposure, LensImperfectionExposureScale), exposure); break; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl index 0b16e8d8641..7f74f3af74a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl @@ -122,10 +122,13 @@ float AdaptExposure(float exposure) } } -float CurveRemap(float inEV) +float CurveRemap(float inEV, out float limitMin, out float limitMax) { float remap = saturate((inEV - ParamCurveMin) / (ParamCurveMax - ParamCurveMin)); - return SAMPLE_TEXTURE2D_LOD(_ExposureCurveTexture, s_linear_clamp_sampler, float2(remap, 0.0), 0.0).x; + float3 curveSample = SAMPLE_TEXTURE2D_LOD(_ExposureCurveTexture, s_linear_clamp_sampler, float2(remap, 0.0), 0.0).xyz; + limitMin = curveSample.y; + limitMax = curveSample.z; + return curveSample.x; } #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute index 8f12e2925ef..a72bc28bac3 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute @@ -165,13 +165,15 @@ void KHistogramReduce(uint3 dispatchThreadId : SV_DispatchThreadID) w = max(w, 1e-4f); float avgEV = evProcessedSum * rcp(w); + float minExposure = ParamExposureLimitMin; + float maxExposure = ParamExposureLimitMax; if (ParamEvaluateMode == 2) { - avgEV = CurveRemap(avgEV); + avgEV = CurveRemap(avgEV, minExposure, maxExposure); } float exposure = AdaptExposure(avgEV - ParamExposureCompensation); - exposure = clamp(exposure, ParamExposureLimitMin, ParamExposureLimitMax); + exposure = clamp(exposure, minExposure, maxExposure); _OutputTexture[uint2(0, 0)] = float2(ConvertEV100ToExposure(exposure, LensImperfectionExposureScale), exposure); #ifdef OUTPUT_DEBUG_DATA _ExposureDebugTexture[uint2(0, 0)] = float2(avgEV - ParamExposureCompensation, 0.0f);