using System.IO; using UnityEngine; using UnityEngine.Rendering.HighDefinition; using UnityEngine.Rendering; using UnityEngine.Experimental.Rendering; namespace UnityEditor.Rendering.HighDefinition { // TODO: handle retina / EditorGUIUtility.pixelsPerPoint [CustomEditor(typeof(Tonemapping))] sealed class TonemappingEditor : VolumeComponentEditor { SerializedDataParameter m_Mode; SerializedDataParameter m_UseFullACES; SerializedDataParameter m_ToeStrength; SerializedDataParameter m_ToeLength; SerializedDataParameter m_ShoulderStrength; SerializedDataParameter m_ShoulderLength; SerializedDataParameter m_ShoulderAngle; SerializedDataParameter m_Gamma; SerializedDataParameter m_LutTexture; SerializedDataParameter m_LutContribution; // HDR Mode. SerializedDataParameter m_NeutralHDRRangeReductionMode; SerializedDataParameter m_HueShiftAmount; SerializedDataParameter m_HDRDetectPaperWhite; SerializedDataParameter m_HDRPaperwhite; SerializedDataParameter m_HDRDetectNitLimits; SerializedDataParameter m_HDRMinNits; SerializedDataParameter m_HDRMaxNits; SerializedDataParameter m_HDRAcesPreset; SerializedDataParameter m_HDRFallbackMode; public override bool hasAdditionalProperties => true; // Curve drawing utilities readonly HableCurve m_HableCurve = new HableCurve(); Rect m_CurveRect; Material m_Material; RenderTexture m_CurveTex; public override void OnEnable() { var o = new PropertyFetcher(serializedObject); m_Mode = Unpack(o.Find(x => x.mode)); m_UseFullACES = Unpack(o.Find(x => x.useFullACES)); m_ToeStrength = Unpack(o.Find(x => x.toeStrength)); m_ToeLength = Unpack(o.Find(x => x.toeLength)); m_ShoulderStrength = Unpack(o.Find(x => x.shoulderStrength)); m_ShoulderLength = Unpack(o.Find(x => x.shoulderLength)); m_ShoulderAngle = Unpack(o.Find(x => x.shoulderAngle)); m_Gamma = Unpack(o.Find(x => x.gamma)); m_LutTexture = Unpack(o.Find(x => x.lutTexture)); m_LutContribution = Unpack(o.Find(x => x.lutContribution)); m_NeutralHDRRangeReductionMode = Unpack(o.Find(x => x.neutralHDRRangeReductionMode)); m_HueShiftAmount = Unpack(o.Find(x => x.hueShiftAmount)); m_HDRDetectPaperWhite = Unpack(o.Find(x => x.detectPaperWhite)); m_HDRPaperwhite = Unpack(o.Find(x => x.paperWhite)); m_HDRDetectNitLimits = Unpack(o.Find(x => x.detectBrightnessLimits)); m_HDRMinNits = Unpack(o.Find(x => x.minNits)); m_HDRMaxNits = Unpack(o.Find(x => x.maxNits)); m_HDRAcesPreset = Unpack(o.Find(x => x.acesPreset)); m_HDRFallbackMode = Unpack(o.Find(x => x.fallbackMode)); m_Material = new Material(Shader.Find("Hidden/HD PostProcessing/Editor/Custom Tonemapper Curve")); } public override void OnDisable() { CoreUtils.Destroy(m_Material); m_Material = null; CoreUtils.Destroy(m_CurveTex); m_CurveTex = null; } internal bool HDROutputIsActive() { return SystemInfo.hdrDisplaySupportFlags.HasFlag(HDRDisplaySupportFlags.Supported) && HDROutputSettings.main.active; } public override void OnInspectorGUI() { HDEditorUtils.EnsureFrameSetting(FrameSettingsField.Tonemapping, "Tonemapping"); bool hdrInPlayerSettings = UnityEditor.PlayerSettings.allowHDRDisplaySupport; PropertyField(m_Mode); // Draw a curve for the custom tonemapping mode to make it easier to tweak visually if (m_Mode.value.intValue == (int)TonemappingMode.Custom) { EditorGUILayout.Space(); // Reserve GUI space m_CurveRect = GUILayoutUtility.GetRect(128, 80); m_CurveRect.xMin += EditorGUI.indentLevel * 15f; if (Event.current.type == EventType.Repaint) { // Prepare curve data float toeStrength = m_ToeStrength.value.floatValue; float toeLength = m_ToeLength.value.floatValue; float shoulderStrength = m_ShoulderStrength.value.floatValue; float shoulderLength = m_ShoulderLength.value.floatValue; float shoulderAngle = m_ShoulderAngle.value.floatValue; float gamma = m_Gamma.value.floatValue; m_HableCurve.Init( toeStrength, toeLength, shoulderStrength, shoulderLength, shoulderAngle, gamma ); float alpha = GUI.enabled ? 1f : 0.5f; m_Material.SetVector(HDShaderIDs._CustomToneCurve, m_HableCurve.uniforms.curve); m_Material.SetVector(HDShaderIDs._ToeSegmentA, m_HableCurve.uniforms.toeSegmentA); m_Material.SetVector(HDShaderIDs._ToeSegmentB, m_HableCurve.uniforms.toeSegmentB); m_Material.SetVector(HDShaderIDs._MidSegmentA, m_HableCurve.uniforms.midSegmentA); m_Material.SetVector(HDShaderIDs._MidSegmentB, m_HableCurve.uniforms.midSegmentB); m_Material.SetVector(HDShaderIDs._ShoSegmentA, m_HableCurve.uniforms.shoSegmentA); m_Material.SetVector(HDShaderIDs._ShoSegmentB, m_HableCurve.uniforms.shoSegmentB); m_Material.SetVector(HDShaderIDs._Variants, new Vector4(alpha, m_HableCurve.whitePoint, 0f, 0f)); CheckCurveRT((int)m_CurveRect.width, (int)m_CurveRect.height); var oldRt = RenderTexture.active; Graphics.Blit(null, m_CurveTex, m_Material, EditorGUIUtility.isProSkin ? 0 : 1); RenderTexture.active = oldRt; GUI.DrawTexture(m_CurveRect, m_CurveTex); Handles.DrawSolidRectangleWithOutline(m_CurveRect, Color.clear, Color.white * 0.4f); } PropertyField(m_ToeStrength); PropertyField(m_ToeLength); PropertyField(m_ShoulderStrength); PropertyField(m_ShoulderLength); PropertyField(m_ShoulderAngle); PropertyField(m_Gamma); } else if (m_Mode.value.intValue == (int)TonemappingMode.External) { PropertyField(m_LutTexture, EditorGUIUtility.TrTextContent("Lookup Texture")); var lut = m_LutTexture.value.objectReferenceValue; if (lut != null && !((Tonemapping)target).ValidateLUT()) EditorGUILayout.HelpBox("Invalid lookup texture. It must be a 3D Texture or a Render Texture and have the same size as set in the HDRP settings.", MessageType.Warning); PropertyField(m_LutContribution, EditorGUIUtility.TrTextContent("Contribution")); EditorGUILayout.HelpBox("Use \"Edit > Rendering > Render Selected HDRP Camera to Log EXR\" to export a log-encoded frame for external grading.", MessageType.Info); } else if (m_Mode.value.intValue == (int)TonemappingMode.ACES) { PropertyField(m_UseFullACES); } if (hdrInPlayerSettings && m_Mode.value.intValue != (int)TonemappingMode.None) { EditorGUILayout.LabelField("HDR Output"); if (!HDROutputIsActive()) { EditorGUILayout.HelpBox("HDR is not currently active. Settings will take effect when a compatible device is found.", MessageType.Info); } int hdrTonemapMode = m_Mode.value.intValue; if (m_Mode.value.intValue == (int)TonemappingMode.Custom || hdrTonemapMode == (int)TonemappingMode.External) { EditorGUILayout.HelpBox("The selected tonemapping mode is not supported in HDR Output mode. Select a fallback mode.", MessageType.Warning); PropertyField(m_HDRFallbackMode); hdrTonemapMode = (m_HDRFallbackMode.value.intValue == (int)FallbackHDRTonemap.ACES) ? (int)TonemappingMode.ACES : (m_HDRFallbackMode.value.intValue == (int)FallbackHDRTonemap.Neutral) ? (int)TonemappingMode.Neutral : (int)TonemappingMode.None; } if (hdrTonemapMode == (int)TonemappingMode.Neutral) { PropertyField(m_NeutralHDRRangeReductionMode); PropertyField(m_HueShiftAmount); PropertyField(m_HDRDetectPaperWhite); EditorGUI.indentLevel++; using (new EditorGUI.DisabledScope(m_HDRDetectPaperWhite.value.boolValue)) { PropertyField(m_HDRPaperwhite); } EditorGUI.indentLevel--; PropertyField(m_HDRDetectNitLimits); EditorGUI.indentLevel++; using (new EditorGUI.DisabledScope(m_HDRDetectNitLimits.value.boolValue)) { PropertyField(m_HDRMinNits); PropertyField(m_HDRMaxNits); } EditorGUI.indentLevel--; } if (hdrTonemapMode == (int)TonemappingMode.ACES) { PropertyField(m_HDRAcesPreset); PropertyField(m_HDRDetectPaperWhite); EditorGUI.indentLevel++; using (new EditorGUI.DisabledScope(m_HDRDetectPaperWhite.value.boolValue)) { PropertyField(m_HDRPaperwhite); } EditorGUI.indentLevel--; } } } void CheckCurveRT(int width, int height) { if (m_CurveTex == null || !m_CurveTex.IsCreated() || m_CurveTex.width != width || m_CurveTex.height != height) { CoreUtils.Destroy(m_CurveTex); m_CurveTex = new RenderTexture(width, height, 0, GraphicsFormat.R8G8B8A8_SRGB); m_CurveTex.hideFlags = HideFlags.HideAndDontSave; } } } sealed class ExrExportMenu { [MenuItem("Edit/Rendering/Render Selected HDRP Camera to Log EXR %#&e", priority = CoreUtils.Sections.section2 + CoreUtils.Priorities.editMenuPriority + 1)] static void Export() { var camera = Selection.activeGameObject?.GetComponent(); if (camera == null) { Debug.LogError("Please select a camera before trying to export an EXR."); return; } var hdInstance = RenderPipelineManager.currentPipeline as HDRenderPipeline; if (hdInstance == null) { Debug.LogError("No HDRenderPipeline set in GraphicsSettings."); return; } string outPath = EditorUtility.SaveFilePanel("Export EXR...", "", "Frame", "exr"); if (string.IsNullOrEmpty(outPath)) return; var w = camera.pixelWidth; var h = camera.pixelHeight; var texOut = new Texture2D(w, h, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None); var target = RenderTexture.GetTemporary(w, h, 24, GraphicsFormat.R32G32B32A32_SFloat); var lastActive = RenderTexture.active; var lastTargetSet = camera.targetTexture; hdInstance.debugDisplaySettings.SetFullScreenDebugMode(FullScreenDebugMode.ColorLog); EditorUtility.DisplayProgressBar("Export EXR", "Rendering...", 0f); camera.targetTexture = target; camera.Render(); camera.targetTexture = lastTargetSet; EditorUtility.DisplayProgressBar("Export EXR", "Reading...", 0.25f); hdInstance.debugDisplaySettings.SetFullScreenDebugMode(FullScreenDebugMode.None); RenderTexture.active = target; texOut.ReadPixels(new Rect(0, 0, w, h), 0, 0); texOut.Apply(); RenderTexture.active = lastActive; EditorUtility.DisplayProgressBar("Export EXR", "Encoding...", 0.5f); var bytes = texOut.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat | Texture2D.EXRFlags.CompressZIP); EditorUtility.DisplayProgressBar("Export EXR", "Saving...", 0.75f); File.WriteAllBytes(outPath, bytes); EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); RenderTexture.ReleaseTemporary(target); UnityEngine.Object.DestroyImmediate(texOut); } } }