| | |
| | | using UnityEngine; |
| | | using UnityEngine.PostProcessing; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq.Expressions; |
| | | |
| | | namespace UnityEditor.PostProcessing |
| | | { |
| | | using Settings = ColorGradingModel.Settings; |
| | | using Tonemapper = ColorGradingModel.Tonemapper; |
| | | using ColorWheelMode = ColorGradingModel.ColorWheelMode; |
| | | |
| | | [PostProcessingModelEditor(typeof(ColorGradingModel))] |
| | | public class ColorGradingModelEditor : PostProcessingModelEditor |
| | | { |
| | | static GUIContent[] s_Tonemappers = |
| | | { |
| | | new GUIContent("None"), |
| | | new GUIContent("Filmic (ACES)"), |
| | | new GUIContent("Neutral") |
| | | }; |
| | | |
| | | struct TonemappingSettings |
| | | { |
| | | public SerializedProperty tonemapper; |
| | | public SerializedProperty neutralBlackIn; |
| | | public SerializedProperty neutralWhiteIn; |
| | | public SerializedProperty neutralBlackOut; |
| | | public SerializedProperty neutralWhiteOut; |
| | | public SerializedProperty neutralWhiteLevel; |
| | | public SerializedProperty neutralWhiteClip; |
| | | } |
| | | |
| | | struct BasicSettings |
| | | { |
| | | public SerializedProperty exposure; |
| | | public SerializedProperty temperature; |
| | | public SerializedProperty tint; |
| | | public SerializedProperty hueShift; |
| | | public SerializedProperty saturation; |
| | | public SerializedProperty contrast; |
| | | } |
| | | |
| | | struct ChannelMixerSettings |
| | | { |
| | | public SerializedProperty[] channels; |
| | | public SerializedProperty currentEditingChannel; |
| | | } |
| | | |
| | | struct ColorWheelsSettings |
| | | { |
| | | public SerializedProperty mode; |
| | | public SerializedProperty log; |
| | | public SerializedProperty linear; |
| | | } |
| | | |
| | | static GUIContent[] s_Curves = |
| | | { |
| | | new GUIContent("YRGB"), |
| | | new GUIContent("Hue VS Hue"), |
| | | new GUIContent("Hue VS Sat"), |
| | | new GUIContent("Sat VS Sat"), |
| | | new GUIContent("Lum VS Sat") |
| | | }; |
| | | |
| | | struct CurvesSettings |
| | | { |
| | | public SerializedProperty master; |
| | | public SerializedProperty red; |
| | | public SerializedProperty green; |
| | | public SerializedProperty blue; |
| | | |
| | | public SerializedProperty hueVShue; |
| | | public SerializedProperty hueVSsat; |
| | | public SerializedProperty satVSsat; |
| | | public SerializedProperty lumVSsat; |
| | | |
| | | public SerializedProperty currentEditingCurve; |
| | | public SerializedProperty curveY; |
| | | public SerializedProperty curveR; |
| | | public SerializedProperty curveG; |
| | | public SerializedProperty curveB; |
| | | } |
| | | |
| | | TonemappingSettings m_Tonemapping; |
| | | BasicSettings m_Basic; |
| | | ChannelMixerSettings m_ChannelMixer; |
| | | ColorWheelsSettings m_ColorWheels; |
| | | CurvesSettings m_Curves; |
| | | |
| | | CurveEditor m_CurveEditor; |
| | | Dictionary<SerializedProperty, Color> m_CurveDict; |
| | | |
| | | // Neutral tonemapping curve helper |
| | | const int k_CurveResolution = 24; |
| | | const float k_NeutralRangeX = 2f; |
| | | const float k_NeutralRangeY = 1f; |
| | | Vector3[] m_RectVertices = new Vector3[4]; |
| | | Vector3[] m_LineVertices = new Vector3[2]; |
| | | Vector3[] m_CurveVertices = new Vector3[k_CurveResolution]; |
| | | Rect m_NeutralCurveRect; |
| | | |
| | | public override void OnEnable() |
| | | { |
| | | // Tonemapping settings |
| | | m_Tonemapping = new TonemappingSettings |
| | | { |
| | | tonemapper = FindSetting((Settings x) => x.tonemapping.tonemapper), |
| | | neutralBlackIn = FindSetting((Settings x) => x.tonemapping.neutralBlackIn), |
| | | neutralWhiteIn = FindSetting((Settings x) => x.tonemapping.neutralWhiteIn), |
| | | neutralBlackOut = FindSetting((Settings x) => x.tonemapping.neutralBlackOut), |
| | | neutralWhiteOut = FindSetting((Settings x) => x.tonemapping.neutralWhiteOut), |
| | | neutralWhiteLevel = FindSetting((Settings x) => x.tonemapping.neutralWhiteLevel), |
| | | neutralWhiteClip = FindSetting((Settings x) => x.tonemapping.neutralWhiteClip) |
| | | }; |
| | | |
| | | // Basic settings |
| | | m_Basic = new BasicSettings |
| | | { |
| | | exposure = FindSetting((Settings x) => x.basic.postExposure), |
| | | temperature = FindSetting((Settings x) => x.basic.temperature), |
| | | tint = FindSetting((Settings x) => x.basic.tint), |
| | | hueShift = FindSetting((Settings x) => x.basic.hueShift), |
| | | saturation = FindSetting((Settings x) => x.basic.saturation), |
| | | contrast = FindSetting((Settings x) => x.basic.contrast) |
| | | }; |
| | | |
| | | // Channel mixer |
| | | m_ChannelMixer = new ChannelMixerSettings |
| | | { |
| | | channels = new[] |
| | | { |
| | | FindSetting((Settings x) => x.channelMixer.red), |
| | | FindSetting((Settings x) => x.channelMixer.green), |
| | | FindSetting((Settings x) => x.channelMixer.blue) |
| | | }, |
| | | currentEditingChannel = FindSetting((Settings x) => x.channelMixer.currentEditingChannel) |
| | | }; |
| | | |
| | | // Color wheels |
| | | m_ColorWheels = new ColorWheelsSettings |
| | | { |
| | | mode = FindSetting((Settings x) => x.colorWheels.mode), |
| | | log = FindSetting((Settings x) => x.colorWheels.log), |
| | | linear = FindSetting((Settings x) => x.colorWheels.linear) |
| | | }; |
| | | |
| | | // Curves |
| | | m_Curves = new CurvesSettings |
| | | { |
| | | master = FindSetting((Settings x) => x.curves.master.curve), |
| | | red = FindSetting((Settings x) => x.curves.red.curve), |
| | | green = FindSetting((Settings x) => x.curves.green.curve), |
| | | blue = FindSetting((Settings x) => x.curves.blue.curve), |
| | | |
| | | hueVShue = FindSetting((Settings x) => x.curves.hueVShue.curve), |
| | | hueVSsat = FindSetting((Settings x) => x.curves.hueVSsat.curve), |
| | | satVSsat = FindSetting((Settings x) => x.curves.satVSsat.curve), |
| | | lumVSsat = FindSetting((Settings x) => x.curves.lumVSsat.curve), |
| | | |
| | | currentEditingCurve = FindSetting((Settings x) => x.curves.e_CurrentEditingCurve), |
| | | curveY = FindSetting((Settings x) => x.curves.e_CurveY), |
| | | curveR = FindSetting((Settings x) => x.curves.e_CurveR), |
| | | curveG = FindSetting((Settings x) => x.curves.e_CurveG), |
| | | curveB = FindSetting((Settings x) => x.curves.e_CurveB) |
| | | }; |
| | | |
| | | // Prepare the curve editor and extract curve display settings |
| | | m_CurveDict = new Dictionary<SerializedProperty, Color>(); |
| | | |
| | | var settings = CurveEditor.Settings.defaultSettings; |
| | | |
| | | m_CurveEditor = new CurveEditor(settings); |
| | | AddCurve(m_Curves.master, new Color(1f, 1f, 1f), 2, false); |
| | | AddCurve(m_Curves.red, new Color(1f, 0f, 0f), 2, false); |
| | | AddCurve(m_Curves.green, new Color(0f, 1f, 0f), 2, false); |
| | | AddCurve(m_Curves.blue, new Color(0f, 0.5f, 1f), 2, false); |
| | | AddCurve(m_Curves.hueVShue, new Color(1f, 1f, 1f), 0, true); |
| | | AddCurve(m_Curves.hueVSsat, new Color(1f, 1f, 1f), 0, true); |
| | | AddCurve(m_Curves.satVSsat, new Color(1f, 1f, 1f), 0, false); |
| | | AddCurve(m_Curves.lumVSsat, new Color(1f, 1f, 1f), 0, false); |
| | | } |
| | | |
| | | void AddCurve(SerializedProperty prop, Color color, uint minPointCount, bool loop) |
| | | { |
| | | var state = CurveEditor.CurveState.defaultState; |
| | | state.color = color; |
| | | state.visible = false; |
| | | state.minPointCount = minPointCount; |
| | | state.onlyShowHandlesOnSelection = true; |
| | | state.zeroKeyConstantValue = 0.5f; |
| | | state.loopInBounds = loop; |
| | | m_CurveEditor.Add(prop, state); |
| | | m_CurveDict.Add(prop, color); |
| | | } |
| | | |
| | | public override void OnDisable() |
| | | { |
| | | m_CurveEditor.RemoveAll(); |
| | | } |
| | | |
| | | public override void OnInspectorGUI() |
| | | { |
| | | DoGUIFor("Tonemapping", DoTonemappingGUI); |
| | | EditorGUILayout.Space(); |
| | | DoGUIFor("Basic", DoBasicGUI); |
| | | EditorGUILayout.Space(); |
| | | DoGUIFor("Channel Mixer", DoChannelMixerGUI); |
| | | EditorGUILayout.Space(); |
| | | DoGUIFor("Trackballs", DoColorWheelsGUI); |
| | | EditorGUILayout.Space(); |
| | | DoGUIFor("Grading Curves", DoCurvesGUI); |
| | | } |
| | | |
| | | void DoGUIFor(string title, Action func) |
| | | { |
| | | EditorGUILayout.LabelField(title, EditorStyles.boldLabel); |
| | | EditorGUI.indentLevel++; |
| | | func(); |
| | | EditorGUI.indentLevel--; |
| | | } |
| | | |
| | | void DoTonemappingGUI() |
| | | { |
| | | int tid = EditorGUILayout.Popup(EditorGUIHelper.GetContent("Tonemapper"), m_Tonemapping.tonemapper.intValue, s_Tonemappers); |
| | | |
| | | if (tid == (int)Tonemapper.Neutral) |
| | | { |
| | | DrawNeutralTonemappingCurve(); |
| | | |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralBlackIn, EditorGUIHelper.GetContent("Black In")); |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteIn, EditorGUIHelper.GetContent("White In")); |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralBlackOut, EditorGUIHelper.GetContent("Black Out")); |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteOut, EditorGUIHelper.GetContent("White Out")); |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteLevel, EditorGUIHelper.GetContent("White Level")); |
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteClip, EditorGUIHelper.GetContent("White Clip")); |
| | | } |
| | | |
| | | m_Tonemapping.tonemapper.intValue = tid; |
| | | } |
| | | |
| | | void DrawNeutralTonemappingCurve() |
| | | { |
| | | using (new GUILayout.HorizontalScope()) |
| | | { |
| | | GUILayout.Space(EditorGUI.indentLevel * 15f); |
| | | m_NeutralCurveRect = GUILayoutUtility.GetRect(128, 80); |
| | | } |
| | | |
| | | // Background |
| | | m_RectVertices[0] = PointInRect( 0f, 0f); |
| | | m_RectVertices[1] = PointInRect(k_NeutralRangeX, 0f); |
| | | m_RectVertices[2] = PointInRect(k_NeutralRangeX, k_NeutralRangeY); |
| | | m_RectVertices[3] = PointInRect( 0f, k_NeutralRangeY); |
| | | |
| | | Handles.DrawSolidRectangleWithOutline( |
| | | m_RectVertices, |
| | | Color.white * 0.1f, |
| | | Color.white * 0.4f |
| | | ); |
| | | |
| | | // Horizontal lines |
| | | for (var i = 1; i < k_NeutralRangeY; i++) |
| | | DrawLine(0, i, k_NeutralRangeX, i, 0.4f); |
| | | |
| | | // Vertical lines |
| | | for (var i = 1; i < k_NeutralRangeX; i++) |
| | | DrawLine(i, 0, i, k_NeutralRangeY, 0.4f); |
| | | |
| | | // Label |
| | | Handles.Label( |
| | | PointInRect(0, k_NeutralRangeY) + Vector3.right, |
| | | "Neutral Tonemapper", EditorStyles.miniLabel |
| | | ); |
| | | |
| | | // Precompute some values |
| | | var tonemap = ((ColorGradingModel)target).settings.tonemapping; |
| | | |
| | | const float scaleFactor = 20f; |
| | | const float scaleFactorHalf = scaleFactor * 0.5f; |
| | | |
| | | float inBlack = tonemap.neutralBlackIn * scaleFactor + 1f; |
| | | float outBlack = tonemap.neutralBlackOut * scaleFactorHalf + 1f; |
| | | float inWhite = tonemap.neutralWhiteIn / scaleFactor; |
| | | float outWhite = 1f - tonemap.neutralWhiteOut / scaleFactor; |
| | | float blackRatio = inBlack / outBlack; |
| | | float whiteRatio = inWhite / outWhite; |
| | | |
| | | const float a = 0.2f; |
| | | float b = Mathf.Max(0f, Mathf.LerpUnclamped(0.57f, 0.37f, blackRatio)); |
| | | float c = Mathf.LerpUnclamped(0.01f, 0.24f, whiteRatio); |
| | | float d = Mathf.Max(0f, Mathf.LerpUnclamped(0.02f, 0.20f, blackRatio)); |
| | | const float e = 0.02f; |
| | | const float f = 0.30f; |
| | | float whiteLevel = tonemap.neutralWhiteLevel; |
| | | float whiteClip = tonemap.neutralWhiteClip / scaleFactorHalf; |
| | | |
| | | // Tonemapping curve |
| | | var vcount = 0; |
| | | while (vcount < k_CurveResolution) |
| | | { |
| | | float x = k_NeutralRangeX * vcount / (k_CurveResolution - 1); |
| | | float y = NeutralTonemap(x, a, b, c, d, e, f, whiteLevel, whiteClip); |
| | | |
| | | if (y < k_NeutralRangeY) |
| | | { |
| | | m_CurveVertices[vcount++] = PointInRect(x, y); |
| | | } |
| | | else |
| | | { |
| | | if (vcount > 1) |
| | | { |
| | | // Extend the last segment to the top edge of the rect. |
| | | var v1 = m_CurveVertices[vcount - 2]; |
| | | var v2 = m_CurveVertices[vcount - 1]; |
| | | var clip = (m_NeutralCurveRect.y - v1.y) / (v2.y - v1.y); |
| | | m_CurveVertices[vcount - 1] = v1 + (v2 - v1) * clip; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (vcount > 1) |
| | | { |
| | | Handles.color = Color.white * 0.9f; |
| | | Handles.DrawAAPolyLine(2.0f, vcount, m_CurveVertices); |
| | | } |
| | | } |
| | | |
| | | void DrawLine(float x1, float y1, float x2, float y2, float grayscale) |
| | | { |
| | | m_LineVertices[0] = PointInRect(x1, y1); |
| | | m_LineVertices[1] = PointInRect(x2, y2); |
| | | Handles.color = Color.white * grayscale; |
| | | Handles.DrawAAPolyLine(2f, m_LineVertices); |
| | | } |
| | | |
| | | Vector3 PointInRect(float x, float y) |
| | | { |
| | | x = Mathf.Lerp(m_NeutralCurveRect.x, m_NeutralCurveRect.xMax, x / k_NeutralRangeX); |
| | | y = Mathf.Lerp(m_NeutralCurveRect.yMax, m_NeutralCurveRect.y, y / k_NeutralRangeY); |
| | | return new Vector3(x, y, 0); |
| | | } |
| | | |
| | | float NeutralCurve(float x, float a, float b, float c, float d, float e, float f) |
| | | { |
| | | return ((x * (a * x + c * b) + d * e) / (x * (a * x + b) + d * f)) - e / f; |
| | | } |
| | | |
| | | float NeutralTonemap(float x, float a, float b, float c, float d, float e, float f, float whiteLevel, float whiteClip) |
| | | { |
| | | x = Mathf.Max(0f, x); |
| | | |
| | | // Tonemap |
| | | float whiteScale = 1f / NeutralCurve(whiteLevel, a, b, c, d, e, f); |
| | | x = NeutralCurve(x * whiteScale, a, b, c, d, e, f); |
| | | x *= whiteScale; |
| | | |
| | | // Post-curve white point adjustment |
| | | x /= whiteClip; |
| | | |
| | | return x; |
| | | } |
| | | |
| | | void DoBasicGUI() |
| | | { |
| | | EditorGUILayout.PropertyField(m_Basic.exposure, EditorGUIHelper.GetContent("Post Exposure (EV)")); |
| | | EditorGUILayout.PropertyField(m_Basic.temperature); |
| | | EditorGUILayout.PropertyField(m_Basic.tint); |
| | | EditorGUILayout.PropertyField(m_Basic.hueShift); |
| | | EditorGUILayout.PropertyField(m_Basic.saturation); |
| | | EditorGUILayout.PropertyField(m_Basic.contrast); |
| | | } |
| | | |
| | | void DoChannelMixerGUI() |
| | | { |
| | | int currentChannel = m_ChannelMixer.currentEditingChannel.intValue; |
| | | |
| | | EditorGUI.BeginChangeCheck(); |
| | | { |
| | | using (new EditorGUILayout.HorizontalScope()) |
| | | { |
| | | EditorGUILayout.PrefixLabel("Channel"); |
| | | if (GUILayout.Toggle(currentChannel == 0, EditorGUIHelper.GetContent("Red|Red output channel."), EditorStyles.miniButtonLeft)) currentChannel = 0; |
| | | if (GUILayout.Toggle(currentChannel == 1, EditorGUIHelper.GetContent("Green|Green output channel."), EditorStyles.miniButtonMid)) currentChannel = 1; |
| | | if (GUILayout.Toggle(currentChannel == 2, EditorGUIHelper.GetContent("Blue|Blue output channel."), EditorStyles.miniButtonRight)) currentChannel = 2; |
| | | } |
| | | } |
| | | if (EditorGUI.EndChangeCheck()) |
| | | { |
| | | GUI.FocusControl(null); |
| | | } |
| | | |
| | | var serializedChannel = m_ChannelMixer.channels[currentChannel]; |
| | | m_ChannelMixer.currentEditingChannel.intValue = currentChannel; |
| | | |
| | | var v = serializedChannel.vector3Value; |
| | | v.x = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Red|Modify influence of the red channel within the overall mix."), v.x, -2f, 2f); |
| | | v.y = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Green|Modify influence of the green channel within the overall mix."), v.y, -2f, 2f); |
| | | v.z = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Blue|Modify influence of the blue channel within the overall mix."), v.z, -2f, 2f); |
| | | serializedChannel.vector3Value = v; |
| | | } |
| | | |
| | | void DoColorWheelsGUI() |
| | | { |
| | | int wheelMode = m_ColorWheels.mode.intValue; |
| | | |
| | | using (new EditorGUILayout.HorizontalScope()) |
| | | { |
| | | GUILayout.Space(15); |
| | | if (GUILayout.Toggle(wheelMode == (int)ColorWheelMode.Linear, "Linear", EditorStyles.miniButtonLeft)) wheelMode = (int)ColorWheelMode.Linear; |
| | | if (GUILayout.Toggle(wheelMode == (int)ColorWheelMode.Log, "Log", EditorStyles.miniButtonRight)) wheelMode = (int)ColorWheelMode.Log; |
| | | } |
| | | |
| | | m_ColorWheels.mode.intValue = wheelMode; |
| | | EditorGUILayout.Space(); |
| | | |
| | | if (wheelMode == (int)ColorWheelMode.Linear) |
| | | { |
| | | EditorGUILayout.PropertyField(m_ColorWheels.linear); |
| | | WheelSetTitle(GUILayoutUtility.GetLastRect(), "Linear Controls"); |
| | | } |
| | | else if (wheelMode == (int)ColorWheelMode.Log) |
| | | { |
| | | EditorGUILayout.PropertyField(m_ColorWheels.log); |
| | | WheelSetTitle(GUILayoutUtility.GetLastRect(), "Log Controls"); |
| | | } |
| | | } |
| | | |
| | | static void WheelSetTitle(Rect position, string label) |
| | | { |
| | | var matrix = GUI.matrix; |
| | | var rect = new Rect(position.x - 10f, position.y, TrackballGroupDrawer.m_Size, TrackballGroupDrawer.m_Size); |
| | | GUIUtility.RotateAroundPivot(-90f, rect.center); |
| | | GUI.Label(rect, label, FxStyles.centeredMiniLabel); |
| | | GUI.matrix = matrix; |
| | | } |
| | | |
| | | void ResetVisibleCurves() |
| | | { |
| | | foreach (var curve in m_CurveDict) |
| | | { |
| | | var state = m_CurveEditor.GetCurveState(curve.Key); |
| | | state.visible = false; |
| | | m_CurveEditor.SetCurveState(curve.Key, state); |
| | | } |
| | | } |
| | | |
| | | void SetCurveVisible(SerializedProperty prop) |
| | | { |
| | | var state = m_CurveEditor.GetCurveState(prop); |
| | | state.visible = true; |
| | | m_CurveEditor.SetCurveState(prop, state); |
| | | } |
| | | |
| | | bool SpecialToggle(bool value, string name, out bool rightClicked) |
| | | { |
| | | var rect = GUILayoutUtility.GetRect(EditorGUIHelper.GetContent(name), EditorStyles.toolbarButton); |
| | | |
| | | var e = Event.current; |
| | | rightClicked = (e.type == EventType.MouseUp && rect.Contains(e.mousePosition) && e.button == 1); |
| | | |
| | | return GUI.Toggle(rect, value, name, EditorStyles.toolbarButton); |
| | | } |
| | | |
| | | static Material s_MaterialSpline; |
| | | |
| | | void DoCurvesGUI() |
| | | { |
| | | EditorGUILayout.Space(); |
| | | EditorGUI.indentLevel -= 2; |
| | | ResetVisibleCurves(); |
| | | |
| | | using (new EditorGUI.DisabledGroupScope(serializedProperty.serializedObject.isEditingMultipleObjects)) |
| | | { |
| | | int curveEditingId = 0; |
| | | |
| | | // Top toolbar |
| | | using (new GUILayout.HorizontalScope(EditorStyles.toolbar)) |
| | | { |
| | | curveEditingId = EditorGUILayout.Popup(m_Curves.currentEditingCurve.intValue, s_Curves, EditorStyles.toolbarPopup, GUILayout.MaxWidth(150f)); |
| | | bool y = false, r = false, g = false, b = false; |
| | | |
| | | if (curveEditingId == 0) |
| | | { |
| | | EditorGUILayout.Space(); |
| | | |
| | | bool rightClickedY, rightClickedR, rightClickedG, rightClickedB; |
| | | |
| | | y = SpecialToggle(m_Curves.curveY.boolValue, "Y", out rightClickedY); |
| | | r = SpecialToggle(m_Curves.curveR.boolValue, "R", out rightClickedR); |
| | | g = SpecialToggle(m_Curves.curveG.boolValue, "G", out rightClickedG); |
| | | b = SpecialToggle(m_Curves.curveB.boolValue, "B", out rightClickedB); |
| | | |
| | | if (!y && !r && !g && !b) |
| | | { |
| | | r = g = b = false; |
| | | y = true; |
| | | } |
| | | |
| | | if (rightClickedY || rightClickedR || rightClickedG || rightClickedB) |
| | | { |
| | | y = rightClickedY; |
| | | r = rightClickedR; |
| | | g = rightClickedG; |
| | | b = rightClickedB; |
| | | } |
| | | |
| | | if (y) SetCurveVisible(m_Curves.master); |
| | | if (r) SetCurveVisible(m_Curves.red); |
| | | if (g) SetCurveVisible(m_Curves.green); |
| | | if (b) SetCurveVisible(m_Curves.blue); |
| | | |
| | | m_Curves.curveY.boolValue = y; |
| | | m_Curves.curveR.boolValue = r; |
| | | m_Curves.curveG.boolValue = g; |
| | | m_Curves.curveB.boolValue = b; |
| | | } |
| | | else |
| | | { |
| | | switch (curveEditingId) |
| | | { |
| | | case 1: SetCurveVisible(m_Curves.hueVShue); |
| | | break; |
| | | case 2: SetCurveVisible(m_Curves.hueVSsat); |
| | | break; |
| | | case 3: SetCurveVisible(m_Curves.satVSsat); |
| | | break; |
| | | case 4: SetCurveVisible(m_Curves.lumVSsat); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | GUILayout.FlexibleSpace(); |
| | | |
| | | if (GUILayout.Button("Reset", EditorStyles.toolbarButton)) |
| | | { |
| | | switch (curveEditingId) |
| | | { |
| | | case 0: |
| | | if (y) m_Curves.master.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f); |
| | | if (r) m_Curves.red.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f); |
| | | if (g) m_Curves.green.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f); |
| | | if (b) m_Curves.blue.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f); |
| | | break; |
| | | case 1: m_Curves.hueVShue.animationCurveValue = new AnimationCurve(); |
| | | break; |
| | | case 2: m_Curves.hueVSsat.animationCurveValue = new AnimationCurve(); |
| | | break; |
| | | case 3: m_Curves.satVSsat.animationCurveValue = new AnimationCurve(); |
| | | break; |
| | | case 4: m_Curves.lumVSsat.animationCurveValue = new AnimationCurve(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | m_Curves.currentEditingCurve.intValue = curveEditingId; |
| | | } |
| | | |
| | | // Curve area |
| | | var settings = m_CurveEditor.settings; |
| | | var rect = GUILayoutUtility.GetAspectRect(2f); |
| | | var innerRect = settings.padding.Remove(rect); |
| | | |
| | | if (Event.current.type == EventType.Repaint) |
| | | { |
| | | // Background |
| | | EditorGUI.DrawRect(rect, new Color(0.15f, 0.15f, 0.15f, 1f)); |
| | | |
| | | if (s_MaterialSpline == null) |
| | | s_MaterialSpline = new Material(Shader.Find("Hidden/Post FX/UI/Curve Background")) { hideFlags = HideFlags.HideAndDontSave }; |
| | | |
| | | if (curveEditingId == 1 || curveEditingId == 2) |
| | | DrawBackgroundTexture(innerRect, 0); |
| | | else if (curveEditingId == 3 || curveEditingId == 4) |
| | | DrawBackgroundTexture(innerRect, 1); |
| | | |
| | | // Bounds |
| | | Handles.color = Color.white; |
| | | Handles.DrawSolidRectangleWithOutline(innerRect, Color.clear, new Color(0.8f, 0.8f, 0.8f, 0.5f)); |
| | | |
| | | // Grid setup |
| | | Handles.color = new Color(1f, 1f, 1f, 0.05f); |
| | | int hLines = (int)Mathf.Sqrt(innerRect.width); |
| | | int vLines = (int)(hLines / (innerRect.width / innerRect.height)); |
| | | |
| | | // Vertical grid |
| | | int gridOffset = Mathf.FloorToInt(innerRect.width / hLines); |
| | | int gridPadding = ((int)(innerRect.width) % hLines) / 2; |
| | | |
| | | for (int i = 1; i < hLines; i++) |
| | | { |
| | | var offset = i * Vector2.right * gridOffset; |
| | | offset.x += gridPadding; |
| | | Handles.DrawLine(innerRect.position + offset, new Vector2(innerRect.x, innerRect.yMax - 1) + offset); |
| | | } |
| | | |
| | | // Horizontal grid |
| | | gridOffset = Mathf.FloorToInt(innerRect.height / vLines); |
| | | gridPadding = ((int)(innerRect.height) % vLines) / 2; |
| | | |
| | | for (int i = 1; i < vLines; i++) |
| | | { |
| | | var offset = i * Vector2.up * gridOffset; |
| | | offset.y += gridPadding; |
| | | Handles.DrawLine(innerRect.position + offset, new Vector2(innerRect.xMax - 1, innerRect.y) + offset); |
| | | } |
| | | } |
| | | |
| | | // Curve editor |
| | | if (m_CurveEditor.OnGUI(rect)) |
| | | { |
| | | Repaint(); |
| | | GUI.changed = true; |
| | | } |
| | | |
| | | if (Event.current.type == EventType.Repaint) |
| | | { |
| | | // Borders |
| | | Handles.color = Color.black; |
| | | Handles.DrawLine(new Vector2(rect.x, rect.y - 18f), new Vector2(rect.xMax, rect.y - 18f)); |
| | | Handles.DrawLine(new Vector2(rect.x, rect.y - 19f), new Vector2(rect.x, rect.yMax)); |
| | | Handles.DrawLine(new Vector2(rect.x, rect.yMax), new Vector2(rect.xMax, rect.yMax)); |
| | | Handles.DrawLine(new Vector2(rect.xMax, rect.yMax), new Vector2(rect.xMax, rect.y - 18f)); |
| | | |
| | | // Selection info |
| | | var selection = m_CurveEditor.GetSelection(); |
| | | |
| | | if (selection.curve != null && selection.keyframeIndex > -1) |
| | | { |
| | | var key = selection.keyframe.Value; |
| | | var infoRect = innerRect; |
| | | infoRect.x += 5f; |
| | | infoRect.width = 100f; |
| | | infoRect.height = 30f; |
| | | GUI.Label(infoRect, string.Format("{0}\n{1}", key.time.ToString("F3"), key.value.ToString("F3")), FxStyles.preLabel); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* |
| | | EditorGUILayout.HelpBox( |
| | | @"Curve editor cheat sheet: |
| | | - [Del] or [Backspace] to remove a key |
| | | - [Ctrl] to break a tangent handle |
| | | - [Shift] to align tangent handles |
| | | - [Double click] to create a key on the curve(s) at mouse position |
| | | - [Alt] + [Double click] to create a key on the curve(s) at a given time", |
| | | MessageType.Info); |
| | | */ |
| | | |
| | | EditorGUILayout.Space(); |
| | | EditorGUI.indentLevel += 2; |
| | | } |
| | | |
| | | void DrawBackgroundTexture(Rect rect, int pass) |
| | | { |
| | | float scale = EditorGUIUtility.pixelsPerPoint; |
| | | |
| | | var oldRt = RenderTexture.active; |
| | | var rt = RenderTexture.GetTemporary(Mathf.CeilToInt(rect.width * scale), Mathf.CeilToInt(rect.height * scale), 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); |
| | | s_MaterialSpline.SetFloat("_DisabledState", GUI.enabled ? 1f : 0.5f); |
| | | s_MaterialSpline.SetFloat("_PixelScaling", EditorGUIUtility.pixelsPerPoint); |
| | | |
| | | Graphics.Blit(null, rt, s_MaterialSpline, pass); |
| | | RenderTexture.active = oldRt; |
| | | |
| | | GUI.DrawTexture(rect, rt); |
| | | RenderTexture.ReleaseTemporary(rt); |
| | | } |
| | | } |
| | | } |
| | | using UnityEngine;
|
| | | using UnityEngine.PostProcessing;
|
| | | using System;
|
| | | using System.Collections.Generic;
|
| | | using System.Linq.Expressions;
|
| | |
|
| | | namespace UnityEditor.PostProcessing
|
| | | {
|
| | | using Settings = ColorGradingModel.Settings;
|
| | | using Tonemapper = ColorGradingModel.Tonemapper;
|
| | | using ColorWheelMode = ColorGradingModel.ColorWheelMode;
|
| | |
|
| | | [PostProcessingModelEditor(typeof(ColorGradingModel))]
|
| | | public class ColorGradingModelEditor : PostProcessingModelEditor
|
| | | {
|
| | | static GUIContent[] s_Tonemappers =
|
| | | {
|
| | | new GUIContent("None"),
|
| | | new GUIContent("Filmic (ACES)"),
|
| | | new GUIContent("Neutral")
|
| | | };
|
| | |
|
| | | struct TonemappingSettings
|
| | | {
|
| | | public SerializedProperty tonemapper;
|
| | | public SerializedProperty neutralBlackIn;
|
| | | public SerializedProperty neutralWhiteIn;
|
| | | public SerializedProperty neutralBlackOut;
|
| | | public SerializedProperty neutralWhiteOut;
|
| | | public SerializedProperty neutralWhiteLevel;
|
| | | public SerializedProperty neutralWhiteClip;
|
| | | }
|
| | |
|
| | | struct BasicSettings
|
| | | {
|
| | | public SerializedProperty exposure;
|
| | | public SerializedProperty temperature;
|
| | | public SerializedProperty tint;
|
| | | public SerializedProperty hueShift;
|
| | | public SerializedProperty saturation;
|
| | | public SerializedProperty contrast;
|
| | | }
|
| | |
|
| | | struct ChannelMixerSettings
|
| | | {
|
| | | public SerializedProperty[] channels;
|
| | | public SerializedProperty currentEditingChannel;
|
| | | }
|
| | |
|
| | | struct ColorWheelsSettings
|
| | | {
|
| | | public SerializedProperty mode;
|
| | | public SerializedProperty log;
|
| | | public SerializedProperty linear;
|
| | | }
|
| | |
|
| | | static GUIContent[] s_Curves =
|
| | | {
|
| | | new GUIContent("YRGB"),
|
| | | new GUIContent("Hue VS Hue"),
|
| | | new GUIContent("Hue VS Sat"),
|
| | | new GUIContent("Sat VS Sat"),
|
| | | new GUIContent("Lum VS Sat")
|
| | | };
|
| | |
|
| | | struct CurvesSettings
|
| | | {
|
| | | public SerializedProperty master;
|
| | | public SerializedProperty red;
|
| | | public SerializedProperty green;
|
| | | public SerializedProperty blue;
|
| | |
|
| | | public SerializedProperty hueVShue;
|
| | | public SerializedProperty hueVSsat;
|
| | | public SerializedProperty satVSsat;
|
| | | public SerializedProperty lumVSsat;
|
| | |
|
| | | public SerializedProperty currentEditingCurve;
|
| | | public SerializedProperty curveY;
|
| | | public SerializedProperty curveR;
|
| | | public SerializedProperty curveG;
|
| | | public SerializedProperty curveB;
|
| | | }
|
| | |
|
| | | TonemappingSettings m_Tonemapping;
|
| | | BasicSettings m_Basic;
|
| | | ChannelMixerSettings m_ChannelMixer;
|
| | | ColorWheelsSettings m_ColorWheels;
|
| | | CurvesSettings m_Curves;
|
| | |
|
| | | CurveEditor m_CurveEditor;
|
| | | Dictionary<SerializedProperty, Color> m_CurveDict;
|
| | |
|
| | | // Neutral tonemapping curve helper
|
| | | const int k_CurveResolution = 24;
|
| | | const float k_NeutralRangeX = 2f;
|
| | | const float k_NeutralRangeY = 1f;
|
| | | Vector3[] m_RectVertices = new Vector3[4];
|
| | | Vector3[] m_LineVertices = new Vector3[2];
|
| | | Vector3[] m_CurveVertices = new Vector3[k_CurveResolution];
|
| | | Rect m_NeutralCurveRect;
|
| | |
|
| | | public override void OnEnable()
|
| | | {
|
| | | // Tonemapping settings
|
| | | m_Tonemapping = new TonemappingSettings
|
| | | {
|
| | | tonemapper = FindSetting((Settings x) => x.tonemapping.tonemapper),
|
| | | neutralBlackIn = FindSetting((Settings x) => x.tonemapping.neutralBlackIn),
|
| | | neutralWhiteIn = FindSetting((Settings x) => x.tonemapping.neutralWhiteIn),
|
| | | neutralBlackOut = FindSetting((Settings x) => x.tonemapping.neutralBlackOut),
|
| | | neutralWhiteOut = FindSetting((Settings x) => x.tonemapping.neutralWhiteOut),
|
| | | neutralWhiteLevel = FindSetting((Settings x) => x.tonemapping.neutralWhiteLevel),
|
| | | neutralWhiteClip = FindSetting((Settings x) => x.tonemapping.neutralWhiteClip)
|
| | | };
|
| | |
|
| | | // Basic settings
|
| | | m_Basic = new BasicSettings
|
| | | {
|
| | | exposure = FindSetting((Settings x) => x.basic.postExposure),
|
| | | temperature = FindSetting((Settings x) => x.basic.temperature),
|
| | | tint = FindSetting((Settings x) => x.basic.tint),
|
| | | hueShift = FindSetting((Settings x) => x.basic.hueShift),
|
| | | saturation = FindSetting((Settings x) => x.basic.saturation),
|
| | | contrast = FindSetting((Settings x) => x.basic.contrast)
|
| | | };
|
| | |
|
| | | // Channel mixer
|
| | | m_ChannelMixer = new ChannelMixerSettings
|
| | | {
|
| | | channels = new[]
|
| | | {
|
| | | FindSetting((Settings x) => x.channelMixer.red),
|
| | | FindSetting((Settings x) => x.channelMixer.green),
|
| | | FindSetting((Settings x) => x.channelMixer.blue)
|
| | | },
|
| | | currentEditingChannel = FindSetting((Settings x) => x.channelMixer.currentEditingChannel)
|
| | | };
|
| | |
|
| | | // Color wheels
|
| | | m_ColorWheels = new ColorWheelsSettings
|
| | | {
|
| | | mode = FindSetting((Settings x) => x.colorWheels.mode),
|
| | | log = FindSetting((Settings x) => x.colorWheels.log),
|
| | | linear = FindSetting((Settings x) => x.colorWheels.linear)
|
| | | };
|
| | |
|
| | | // Curves
|
| | | m_Curves = new CurvesSettings
|
| | | {
|
| | | master = FindSetting((Settings x) => x.curves.master.curve),
|
| | | red = FindSetting((Settings x) => x.curves.red.curve),
|
| | | green = FindSetting((Settings x) => x.curves.green.curve),
|
| | | blue = FindSetting((Settings x) => x.curves.blue.curve),
|
| | |
|
| | | hueVShue = FindSetting((Settings x) => x.curves.hueVShue.curve),
|
| | | hueVSsat = FindSetting((Settings x) => x.curves.hueVSsat.curve),
|
| | | satVSsat = FindSetting((Settings x) => x.curves.satVSsat.curve),
|
| | | lumVSsat = FindSetting((Settings x) => x.curves.lumVSsat.curve),
|
| | |
|
| | | currentEditingCurve = FindSetting((Settings x) => x.curves.e_CurrentEditingCurve),
|
| | | curveY = FindSetting((Settings x) => x.curves.e_CurveY),
|
| | | curveR = FindSetting((Settings x) => x.curves.e_CurveR),
|
| | | curveG = FindSetting((Settings x) => x.curves.e_CurveG),
|
| | | curveB = FindSetting((Settings x) => x.curves.e_CurveB)
|
| | | };
|
| | |
|
| | | // Prepare the curve editor and extract curve display settings
|
| | | m_CurveDict = new Dictionary<SerializedProperty, Color>();
|
| | |
|
| | | var settings = CurveEditor.Settings.defaultSettings;
|
| | |
|
| | | m_CurveEditor = new CurveEditor(settings);
|
| | | AddCurve(m_Curves.master, new Color(1f, 1f, 1f), 2, false);
|
| | | AddCurve(m_Curves.red, new Color(1f, 0f, 0f), 2, false);
|
| | | AddCurve(m_Curves.green, new Color(0f, 1f, 0f), 2, false);
|
| | | AddCurve(m_Curves.blue, new Color(0f, 0.5f, 1f), 2, false);
|
| | | AddCurve(m_Curves.hueVShue, new Color(1f, 1f, 1f), 0, true);
|
| | | AddCurve(m_Curves.hueVSsat, new Color(1f, 1f, 1f), 0, true);
|
| | | AddCurve(m_Curves.satVSsat, new Color(1f, 1f, 1f), 0, false);
|
| | | AddCurve(m_Curves.lumVSsat, new Color(1f, 1f, 1f), 0, false);
|
| | | }
|
| | |
|
| | | void AddCurve(SerializedProperty prop, Color color, uint minPointCount, bool loop)
|
| | | {
|
| | | var state = CurveEditor.CurveState.defaultState;
|
| | | state.color = color;
|
| | | state.visible = false;
|
| | | state.minPointCount = minPointCount;
|
| | | state.onlyShowHandlesOnSelection = true;
|
| | | state.zeroKeyConstantValue = 0.5f;
|
| | | state.loopInBounds = loop;
|
| | | m_CurveEditor.Add(prop, state);
|
| | | m_CurveDict.Add(prop, color);
|
| | | }
|
| | |
|
| | | public override void OnDisable()
|
| | | {
|
| | | m_CurveEditor.RemoveAll();
|
| | | }
|
| | |
|
| | | public override void OnInspectorGUI()
|
| | | {
|
| | | DoGUIFor("Tonemapping", DoTonemappingGUI);
|
| | | EditorGUILayout.Space();
|
| | | DoGUIFor("Basic", DoBasicGUI);
|
| | | EditorGUILayout.Space();
|
| | | DoGUIFor("Channel Mixer", DoChannelMixerGUI);
|
| | | EditorGUILayout.Space();
|
| | | DoGUIFor("Trackballs", DoColorWheelsGUI);
|
| | | EditorGUILayout.Space();
|
| | | DoGUIFor("Grading Curves", DoCurvesGUI);
|
| | | }
|
| | |
|
| | | void DoGUIFor(string title, Action func)
|
| | | {
|
| | | EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
|
| | | EditorGUI.indentLevel++;
|
| | | func();
|
| | | EditorGUI.indentLevel--;
|
| | | }
|
| | |
|
| | | void DoTonemappingGUI()
|
| | | {
|
| | | int tid = EditorGUILayout.Popup(EditorGUIHelper.GetContent("Tonemapper"), m_Tonemapping.tonemapper.intValue, s_Tonemappers);
|
| | |
|
| | | if (tid == (int)Tonemapper.Neutral)
|
| | | {
|
| | | DrawNeutralTonemappingCurve();
|
| | |
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralBlackIn, EditorGUIHelper.GetContent("Black In"));
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteIn, EditorGUIHelper.GetContent("White In"));
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralBlackOut, EditorGUIHelper.GetContent("Black Out"));
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteOut, EditorGUIHelper.GetContent("White Out"));
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteLevel, EditorGUIHelper.GetContent("White Level"));
|
| | | EditorGUILayout.PropertyField(m_Tonemapping.neutralWhiteClip, EditorGUIHelper.GetContent("White Clip"));
|
| | | }
|
| | |
|
| | | m_Tonemapping.tonemapper.intValue = tid;
|
| | | }
|
| | |
|
| | | void DrawNeutralTonemappingCurve()
|
| | | {
|
| | | using (new GUILayout.HorizontalScope())
|
| | | {
|
| | | GUILayout.Space(EditorGUI.indentLevel * 15f);
|
| | | m_NeutralCurveRect = GUILayoutUtility.GetRect(128, 80);
|
| | | }
|
| | |
|
| | | // Background
|
| | | m_RectVertices[0] = PointInRect( 0f, 0f);
|
| | | m_RectVertices[1] = PointInRect(k_NeutralRangeX, 0f);
|
| | | m_RectVertices[2] = PointInRect(k_NeutralRangeX, k_NeutralRangeY);
|
| | | m_RectVertices[3] = PointInRect( 0f, k_NeutralRangeY);
|
| | |
|
| | | Handles.DrawSolidRectangleWithOutline(
|
| | | m_RectVertices,
|
| | | Color.white * 0.1f,
|
| | | Color.white * 0.4f
|
| | | );
|
| | |
|
| | | // Horizontal lines
|
| | | for (var i = 1; i < k_NeutralRangeY; i++)
|
| | | DrawLine(0, i, k_NeutralRangeX, i, 0.4f);
|
| | |
|
| | | // Vertical lines
|
| | | for (var i = 1; i < k_NeutralRangeX; i++)
|
| | | DrawLine(i, 0, i, k_NeutralRangeY, 0.4f);
|
| | |
|
| | | // Label
|
| | | Handles.Label(
|
| | | PointInRect(0, k_NeutralRangeY) + Vector3.right,
|
| | | "Neutral Tonemapper", EditorStyles.miniLabel
|
| | | );
|
| | |
|
| | | // Precompute some values
|
| | | var tonemap = ((ColorGradingModel)target).settings.tonemapping;
|
| | |
|
| | | const float scaleFactor = 20f;
|
| | | const float scaleFactorHalf = scaleFactor * 0.5f;
|
| | |
|
| | | float inBlack = tonemap.neutralBlackIn * scaleFactor + 1f;
|
| | | float outBlack = tonemap.neutralBlackOut * scaleFactorHalf + 1f;
|
| | | float inWhite = tonemap.neutralWhiteIn / scaleFactor;
|
| | | float outWhite = 1f - tonemap.neutralWhiteOut / scaleFactor;
|
| | | float blackRatio = inBlack / outBlack;
|
| | | float whiteRatio = inWhite / outWhite;
|
| | |
|
| | | const float a = 0.2f;
|
| | | float b = Mathf.Max(0f, Mathf.LerpUnclamped(0.57f, 0.37f, blackRatio));
|
| | | float c = Mathf.LerpUnclamped(0.01f, 0.24f, whiteRatio);
|
| | | float d = Mathf.Max(0f, Mathf.LerpUnclamped(0.02f, 0.20f, blackRatio));
|
| | | const float e = 0.02f;
|
| | | const float f = 0.30f;
|
| | | float whiteLevel = tonemap.neutralWhiteLevel;
|
| | | float whiteClip = tonemap.neutralWhiteClip / scaleFactorHalf;
|
| | |
|
| | | // Tonemapping curve
|
| | | var vcount = 0;
|
| | | while (vcount < k_CurveResolution)
|
| | | {
|
| | | float x = k_NeutralRangeX * vcount / (k_CurveResolution - 1);
|
| | | float y = NeutralTonemap(x, a, b, c, d, e, f, whiteLevel, whiteClip);
|
| | |
|
| | | if (y < k_NeutralRangeY)
|
| | | {
|
| | | m_CurveVertices[vcount++] = PointInRect(x, y);
|
| | | }
|
| | | else
|
| | | {
|
| | | if (vcount > 1)
|
| | | {
|
| | | // Extend the last segment to the top edge of the rect.
|
| | | var v1 = m_CurveVertices[vcount - 2];
|
| | | var v2 = m_CurveVertices[vcount - 1];
|
| | | var clip = (m_NeutralCurveRect.y - v1.y) / (v2.y - v1.y);
|
| | | m_CurveVertices[vcount - 1] = v1 + (v2 - v1) * clip;
|
| | | }
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | if (vcount > 1)
|
| | | {
|
| | | Handles.color = Color.white * 0.9f;
|
| | | Handles.DrawAAPolyLine(2.0f, vcount, m_CurveVertices);
|
| | | }
|
| | | }
|
| | |
|
| | | void DrawLine(float x1, float y1, float x2, float y2, float grayscale)
|
| | | {
|
| | | m_LineVertices[0] = PointInRect(x1, y1);
|
| | | m_LineVertices[1] = PointInRect(x2, y2);
|
| | | Handles.color = Color.white * grayscale;
|
| | | Handles.DrawAAPolyLine(2f, m_LineVertices);
|
| | | }
|
| | |
|
| | | Vector3 PointInRect(float x, float y)
|
| | | {
|
| | | x = Mathf.Lerp(m_NeutralCurveRect.x, m_NeutralCurveRect.xMax, x / k_NeutralRangeX);
|
| | | y = Mathf.Lerp(m_NeutralCurveRect.yMax, m_NeutralCurveRect.y, y / k_NeutralRangeY);
|
| | | return new Vector3(x, y, 0);
|
| | | }
|
| | |
|
| | | float NeutralCurve(float x, float a, float b, float c, float d, float e, float f)
|
| | | {
|
| | | return ((x * (a * x + c * b) + d * e) / (x * (a * x + b) + d * f)) - e / f;
|
| | | }
|
| | |
|
| | | float NeutralTonemap(float x, float a, float b, float c, float d, float e, float f, float whiteLevel, float whiteClip)
|
| | | {
|
| | | x = Mathf.Max(0f, x);
|
| | |
|
| | | // Tonemap
|
| | | float whiteScale = 1f / NeutralCurve(whiteLevel, a, b, c, d, e, f);
|
| | | x = NeutralCurve(x * whiteScale, a, b, c, d, e, f);
|
| | | x *= whiteScale;
|
| | |
|
| | | // Post-curve white point adjustment
|
| | | x /= whiteClip;
|
| | |
|
| | | return x;
|
| | | }
|
| | |
|
| | | void DoBasicGUI()
|
| | | {
|
| | | EditorGUILayout.PropertyField(m_Basic.exposure, EditorGUIHelper.GetContent("Post Exposure (EV)"));
|
| | | EditorGUILayout.PropertyField(m_Basic.temperature);
|
| | | EditorGUILayout.PropertyField(m_Basic.tint);
|
| | | EditorGUILayout.PropertyField(m_Basic.hueShift);
|
| | | EditorGUILayout.PropertyField(m_Basic.saturation);
|
| | | EditorGUILayout.PropertyField(m_Basic.contrast);
|
| | | }
|
| | |
|
| | | void DoChannelMixerGUI()
|
| | | {
|
| | | int currentChannel = m_ChannelMixer.currentEditingChannel.intValue;
|
| | |
|
| | | EditorGUI.BeginChangeCheck();
|
| | | {
|
| | | using (new EditorGUILayout.HorizontalScope())
|
| | | {
|
| | | EditorGUILayout.PrefixLabel("Channel");
|
| | | if (GUILayout.Toggle(currentChannel == 0, EditorGUIHelper.GetContent("Red|Red output channel."), EditorStyles.miniButtonLeft)) currentChannel = 0;
|
| | | if (GUILayout.Toggle(currentChannel == 1, EditorGUIHelper.GetContent("Green|Green output channel."), EditorStyles.miniButtonMid)) currentChannel = 1;
|
| | | if (GUILayout.Toggle(currentChannel == 2, EditorGUIHelper.GetContent("Blue|Blue output channel."), EditorStyles.miniButtonRight)) currentChannel = 2;
|
| | | }
|
| | | }
|
| | | if (EditorGUI.EndChangeCheck())
|
| | | {
|
| | | GUI.FocusControl(null);
|
| | | }
|
| | |
|
| | | var serializedChannel = m_ChannelMixer.channels[currentChannel];
|
| | | m_ChannelMixer.currentEditingChannel.intValue = currentChannel;
|
| | |
|
| | | var v = serializedChannel.vector3Value;
|
| | | v.x = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Red|Modify influence of the red channel within the overall mix."), v.x, -2f, 2f);
|
| | | v.y = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Green|Modify influence of the green channel within the overall mix."), v.y, -2f, 2f);
|
| | | v.z = EditorGUILayout.Slider(EditorGUIHelper.GetContent("Blue|Modify influence of the blue channel within the overall mix."), v.z, -2f, 2f);
|
| | | serializedChannel.vector3Value = v;
|
| | | }
|
| | |
|
| | | void DoColorWheelsGUI()
|
| | | {
|
| | | int wheelMode = m_ColorWheels.mode.intValue;
|
| | |
|
| | | using (new EditorGUILayout.HorizontalScope())
|
| | | {
|
| | | GUILayout.Space(15);
|
| | | if (GUILayout.Toggle(wheelMode == (int)ColorWheelMode.Linear, "Linear", EditorStyles.miniButtonLeft)) wheelMode = (int)ColorWheelMode.Linear;
|
| | | if (GUILayout.Toggle(wheelMode == (int)ColorWheelMode.Log, "Log", EditorStyles.miniButtonRight)) wheelMode = (int)ColorWheelMode.Log;
|
| | | }
|
| | |
|
| | | m_ColorWheels.mode.intValue = wheelMode;
|
| | | EditorGUILayout.Space();
|
| | |
|
| | | if (wheelMode == (int)ColorWheelMode.Linear)
|
| | | {
|
| | | EditorGUILayout.PropertyField(m_ColorWheels.linear);
|
| | | WheelSetTitle(GUILayoutUtility.GetLastRect(), "Linear Controls");
|
| | | }
|
| | | else if (wheelMode == (int)ColorWheelMode.Log)
|
| | | {
|
| | | EditorGUILayout.PropertyField(m_ColorWheels.log);
|
| | | WheelSetTitle(GUILayoutUtility.GetLastRect(), "Log Controls");
|
| | | }
|
| | | }
|
| | |
|
| | | static void WheelSetTitle(Rect position, string label)
|
| | | {
|
| | | var matrix = GUI.matrix;
|
| | | var rect = new Rect(position.x - 10f, position.y, TrackballGroupDrawer.m_Size, TrackballGroupDrawer.m_Size);
|
| | | GUIUtility.RotateAroundPivot(-90f, rect.center);
|
| | | GUI.Label(rect, label, FxStyles.centeredMiniLabel);
|
| | | GUI.matrix = matrix;
|
| | | }
|
| | |
|
| | | void ResetVisibleCurves()
|
| | | {
|
| | | foreach (var curve in m_CurveDict)
|
| | | {
|
| | | var state = m_CurveEditor.GetCurveState(curve.Key);
|
| | | state.visible = false;
|
| | | m_CurveEditor.SetCurveState(curve.Key, state);
|
| | | }
|
| | | }
|
| | |
|
| | | void SetCurveVisible(SerializedProperty prop)
|
| | | {
|
| | | var state = m_CurveEditor.GetCurveState(prop);
|
| | | state.visible = true;
|
| | | m_CurveEditor.SetCurveState(prop, state);
|
| | | }
|
| | |
|
| | | bool SpecialToggle(bool value, string name, out bool rightClicked)
|
| | | {
|
| | | var rect = GUILayoutUtility.GetRect(EditorGUIHelper.GetContent(name), EditorStyles.toolbarButton);
|
| | |
|
| | | var e = Event.current;
|
| | | rightClicked = (e.type == EventType.MouseUp && rect.Contains(e.mousePosition) && e.button == 1);
|
| | |
|
| | | return GUI.Toggle(rect, value, name, EditorStyles.toolbarButton);
|
| | | }
|
| | |
|
| | | static Material s_MaterialSpline;
|
| | |
|
| | | void DoCurvesGUI()
|
| | | {
|
| | | EditorGUILayout.Space();
|
| | | EditorGUI.indentLevel -= 2;
|
| | | ResetVisibleCurves();
|
| | |
|
| | | using (new EditorGUI.DisabledGroupScope(serializedProperty.serializedObject.isEditingMultipleObjects))
|
| | | {
|
| | | int curveEditingId = 0;
|
| | |
|
| | | // Top toolbar
|
| | | using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
|
| | | {
|
| | | curveEditingId = EditorGUILayout.Popup(m_Curves.currentEditingCurve.intValue, s_Curves, EditorStyles.toolbarPopup, GUILayout.MaxWidth(150f));
|
| | | bool y = false, r = false, g = false, b = false;
|
| | |
|
| | | if (curveEditingId == 0)
|
| | | {
|
| | | EditorGUILayout.Space();
|
| | |
|
| | | bool rightClickedY, rightClickedR, rightClickedG, rightClickedB;
|
| | |
|
| | | y = SpecialToggle(m_Curves.curveY.boolValue, "Y", out rightClickedY);
|
| | | r = SpecialToggle(m_Curves.curveR.boolValue, "R", out rightClickedR);
|
| | | g = SpecialToggle(m_Curves.curveG.boolValue, "G", out rightClickedG);
|
| | | b = SpecialToggle(m_Curves.curveB.boolValue, "B", out rightClickedB);
|
| | |
|
| | | if (!y && !r && !g && !b)
|
| | | {
|
| | | r = g = b = false;
|
| | | y = true;
|
| | | }
|
| | |
|
| | | if (rightClickedY || rightClickedR || rightClickedG || rightClickedB)
|
| | | {
|
| | | y = rightClickedY;
|
| | | r = rightClickedR;
|
| | | g = rightClickedG;
|
| | | b = rightClickedB;
|
| | | }
|
| | |
|
| | | if (y) SetCurveVisible(m_Curves.master);
|
| | | if (r) SetCurveVisible(m_Curves.red);
|
| | | if (g) SetCurveVisible(m_Curves.green);
|
| | | if (b) SetCurveVisible(m_Curves.blue);
|
| | |
|
| | | m_Curves.curveY.boolValue = y;
|
| | | m_Curves.curveR.boolValue = r;
|
| | | m_Curves.curveG.boolValue = g;
|
| | | m_Curves.curveB.boolValue = b;
|
| | | }
|
| | | else
|
| | | {
|
| | | switch (curveEditingId)
|
| | | {
|
| | | case 1: SetCurveVisible(m_Curves.hueVShue);
|
| | | break;
|
| | | case 2: SetCurveVisible(m_Curves.hueVSsat);
|
| | | break;
|
| | | case 3: SetCurveVisible(m_Curves.satVSsat);
|
| | | break;
|
| | | case 4: SetCurveVisible(m_Curves.lumVSsat);
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | GUILayout.FlexibleSpace();
|
| | |
|
| | | if (GUILayout.Button("Reset", EditorStyles.toolbarButton))
|
| | | {
|
| | | switch (curveEditingId)
|
| | | {
|
| | | case 0:
|
| | | if (y) m_Curves.master.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
| | | if (r) m_Curves.red.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
| | | if (g) m_Curves.green.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
| | | if (b) m_Curves.blue.animationCurveValue = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
| | | break;
|
| | | case 1: m_Curves.hueVShue.animationCurveValue = new AnimationCurve();
|
| | | break;
|
| | | case 2: m_Curves.hueVSsat.animationCurveValue = new AnimationCurve();
|
| | | break;
|
| | | case 3: m_Curves.satVSsat.animationCurveValue = new AnimationCurve();
|
| | | break;
|
| | | case 4: m_Curves.lumVSsat.animationCurveValue = new AnimationCurve();
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | m_Curves.currentEditingCurve.intValue = curveEditingId;
|
| | | }
|
| | |
|
| | | // Curve area
|
| | | var settings = m_CurveEditor.settings;
|
| | | var rect = GUILayoutUtility.GetAspectRect(2f);
|
| | | var innerRect = settings.padding.Remove(rect);
|
| | |
|
| | | if (Event.current.type == EventType.Repaint)
|
| | | {
|
| | | // Background
|
| | | EditorGUI.DrawRect(rect, new Color(0.15f, 0.15f, 0.15f, 1f));
|
| | |
|
| | | if (s_MaterialSpline == null)
|
| | | s_MaterialSpline = new Material(Shader.Find("Hidden/Post FX/UI/Curve Background")) { hideFlags = HideFlags.HideAndDontSave };
|
| | |
|
| | | if (curveEditingId == 1 || curveEditingId == 2)
|
| | | DrawBackgroundTexture(innerRect, 0);
|
| | | else if (curveEditingId == 3 || curveEditingId == 4)
|
| | | DrawBackgroundTexture(innerRect, 1);
|
| | |
|
| | | // Bounds
|
| | | Handles.color = Color.white;
|
| | | Handles.DrawSolidRectangleWithOutline(innerRect, Color.clear, new Color(0.8f, 0.8f, 0.8f, 0.5f));
|
| | |
|
| | | // Grid setup
|
| | | Handles.color = new Color(1f, 1f, 1f, 0.05f);
|
| | | int hLines = (int)Mathf.Sqrt(innerRect.width);
|
| | | int vLines = (int)(hLines / (innerRect.width / innerRect.height));
|
| | |
|
| | | // Vertical grid
|
| | | int gridOffset = Mathf.FloorToInt(innerRect.width / hLines);
|
| | | int gridPadding = ((int)(innerRect.width) % hLines) / 2;
|
| | |
|
| | | for (int i = 1; i < hLines; i++)
|
| | | {
|
| | | var offset = i * Vector2.right * gridOffset;
|
| | | offset.x += gridPadding;
|
| | | Handles.DrawLine(innerRect.position + offset, new Vector2(innerRect.x, innerRect.yMax - 1) + offset);
|
| | | }
|
| | |
|
| | | // Horizontal grid
|
| | | gridOffset = Mathf.FloorToInt(innerRect.height / vLines);
|
| | | gridPadding = ((int)(innerRect.height) % vLines) / 2;
|
| | |
|
| | | for (int i = 1; i < vLines; i++)
|
| | | {
|
| | | var offset = i * Vector2.up * gridOffset;
|
| | | offset.y += gridPadding;
|
| | | Handles.DrawLine(innerRect.position + offset, new Vector2(innerRect.xMax - 1, innerRect.y) + offset);
|
| | | }
|
| | | }
|
| | |
|
| | | // Curve editor
|
| | | if (m_CurveEditor.OnGUI(rect))
|
| | | {
|
| | | Repaint();
|
| | | GUI.changed = true;
|
| | | }
|
| | |
|
| | | if (Event.current.type == EventType.Repaint)
|
| | | {
|
| | | // Borders
|
| | | Handles.color = Color.black;
|
| | | Handles.DrawLine(new Vector2(rect.x, rect.y - 18f), new Vector2(rect.xMax, rect.y - 18f));
|
| | | Handles.DrawLine(new Vector2(rect.x, rect.y - 19f), new Vector2(rect.x, rect.yMax));
|
| | | Handles.DrawLine(new Vector2(rect.x, rect.yMax), new Vector2(rect.xMax, rect.yMax));
|
| | | Handles.DrawLine(new Vector2(rect.xMax, rect.yMax), new Vector2(rect.xMax, rect.y - 18f));
|
| | |
|
| | | // Selection info
|
| | | var selection = m_CurveEditor.GetSelection();
|
| | |
|
| | | if (selection.curve != null && selection.keyframeIndex > -1)
|
| | | {
|
| | | var key = selection.keyframe.Value;
|
| | | var infoRect = innerRect;
|
| | | infoRect.x += 5f;
|
| | | infoRect.width = 100f;
|
| | | infoRect.height = 30f;
|
| | | GUI.Label(infoRect, string.Format("{0}\n{1}", key.time.ToString("F3"), key.value.ToString("F3")), FxStyles.preLabel);
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | /*
|
| | | EditorGUILayout.HelpBox(
|
| | | @"Curve editor cheat sheet:
|
| | | - [Del] or [Backspace] to remove a key
|
| | | - [Ctrl] to break a tangent handle
|
| | | - [Shift] to align tangent handles
|
| | | - [Double click] to create a key on the curve(s) at mouse position
|
| | | - [Alt] + [Double click] to create a key on the curve(s) at a given time",
|
| | | MessageType.Info);
|
| | | */
|
| | |
|
| | | EditorGUILayout.Space();
|
| | | EditorGUI.indentLevel += 2;
|
| | | }
|
| | |
|
| | | void DrawBackgroundTexture(Rect rect, int pass)
|
| | | {
|
| | | float scale = EditorGUIUtility.pixelsPerPoint;
|
| | |
|
| | | var oldRt = RenderTexture.active;
|
| | | var rt = RenderTexture.GetTemporary(Mathf.CeilToInt(rect.width * scale), Mathf.CeilToInt(rect.height * scale), 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
| | | s_MaterialSpline.SetFloat("_DisabledState", GUI.enabled ? 1f : 0.5f);
|
| | | s_MaterialSpline.SetFloat("_PixelScaling", EditorGUIUtility.pixelsPerPoint);
|
| | |
|
| | | Graphics.Blit(null, rt, s_MaterialSpline, pass);
|
| | | RenderTexture.active = oldRt;
|
| | |
|
| | | GUI.DrawTexture(rect, rt);
|
| | | RenderTexture.ReleaseTemporary(rt);
|
| | | }
|
| | | }
|
| | | }
|