using UnityEngine; using UnityEngine.PostProcessing; namespace UnityEditor.PostProcessing { using Settings = MotionBlurModel.Settings; [PostProcessingModelEditor(typeof(MotionBlurModel))] public class MotionBlurModelEditor : PostProcessingModelEditor { SerializedProperty m_ShutterAngle; SerializedProperty m_SampleCount; SerializedProperty m_FrameBlending; GraphDrawer m_GraphDrawer; class GraphDrawer { const float k_Height = 32f; Texture m_BlendingIcon; GUIStyle m_LowerCenterStyle; GUIStyle m_MiddleCenterStyle; Color m_ColorDark; Color m_ColorGray; Vector3[] m_RectVertices = new Vector3[4]; public GraphDrawer() { m_BlendingIcon = EditorResources.Load("UI/MotionBlendingIcon.png"); m_LowerCenterStyle = new GUIStyle(EditorStyles.miniLabel) { alignment = TextAnchor.LowerCenter }; m_MiddleCenterStyle = new GUIStyle(EditorStyles.miniLabel) { alignment = TextAnchor.MiddleCenter }; if (EditorGUIUtility.isProSkin) { m_ColorDark = new Color(0.18f, 0.18f, 0.18f); m_ColorGray = new Color(0.43f, 0.43f, 0.43f); } else { m_ColorDark = new Color(0.64f, 0.64f, 0.64f); m_ColorGray = new Color(0.92f, 0.92f, 0.92f); } } public void DrawShutterGraph(float angle) { var center = GUILayoutUtility.GetRect(128, k_Height).center; // Parameters used to make transitions smooth. var zeroWhenOff = Mathf.Min(1f, angle * 0.1f); var zeroWhenFull = Mathf.Min(1f, (360f - angle) * 0.02f); // Shutter angle graph var discCenter = center - new Vector2(k_Height * 2.4f, 0f); // - exposure duration indicator DrawDisc(discCenter, k_Height * Mathf.Lerp(0.5f, 0.38f, zeroWhenFull), m_ColorGray); // - shutter disc DrawDisc(discCenter, k_Height * 0.16f * zeroWhenFull, m_ColorDark); // - shutter blade DrawArc(discCenter, k_Height * 0.5f, 360f - angle, m_ColorDark); // - shutter axis DrawDisc(discCenter, zeroWhenOff, m_ColorGray); // Shutter label (off/full) var labelSize = new Vector2(k_Height, k_Height); var labelOrigin = discCenter - labelSize * 0.5f; var labelRect = new Rect(labelOrigin, labelSize); if (Mathf.Approximately(angle, 0f)) GUI.Label(labelRect, "Off", m_MiddleCenterStyle); else if (Mathf.Approximately(angle, 360f)) GUI.Label(labelRect, "Full", m_MiddleCenterStyle); // Exposure time bar graph var outerBarSize = new Vector2(4.75f, 0.5f) * k_Height; var innerBarSize = outerBarSize; innerBarSize.x *= angle / 360f; var barCenter = center + new Vector2(k_Height * 0.9f, 0f); var barOrigin = barCenter - outerBarSize * 0.5f; DrawRect(barOrigin, outerBarSize, m_ColorDark); DrawRect(barOrigin, innerBarSize, m_ColorGray); var barText = "Exposure time = " + (angle / 3.6f).ToString("0") + "% of ΔT"; GUI.Label(new Rect(barOrigin, outerBarSize), barText, m_MiddleCenterStyle); } public void DrawBlendingGraph(float strength) { var center = GUILayoutUtility.GetRect(128, k_Height).center; var iconSize = new Vector2(k_Height, k_Height); var iconStride = new Vector2(k_Height * 0.9f, 0f); var iconOrigin = center - iconSize * 0.5f - iconStride * 2f; for (var i = 0; i < 5; i++) { var weight = BlendingWeight(strength, i / 60f); var rect = new Rect(iconOrigin + iconStride * i, iconSize); var color = m_ColorGray; color.a = weight; GUI.color = color; GUI.Label(rect, m_BlendingIcon); GUI.color = Color.white; GUI.Label(rect, (weight * 100).ToString("0") + "%", m_LowerCenterStyle); } // EditorGUIUtility.isProSkin } // Weight function for multi frame blending float BlendingWeight(float strength, float time) { if (strength > 0f || Mathf.Approximately(time, 0f)) return Mathf.Exp(-time * Mathf.Lerp(80f, 10f, strength)); return 0; } // Draw a solid disc in the graph rect. void DrawDisc(Vector2 center, float radius, Color fill) { Handles.color = fill; Handles.DrawSolidDisc(center, Vector3.forward, radius); } // Draw an arc in the graph rect. void DrawArc(Vector2 center, float radius, float angle, Color fill) { var start = new Vector2( -Mathf.Cos(Mathf.Deg2Rad * angle / 2f), Mathf.Sin(Mathf.Deg2Rad * angle / 2f) ); Handles.color = fill; Handles.DrawSolidArc(center, Vector3.forward, start, angle, radius); } // Draw a rectangle in the graph rect. void DrawRect(Vector2 origin, Vector2 size, Color color) { var p0 = origin; var p1 = origin + size; m_RectVertices[0] = p0; m_RectVertices[1] = new Vector2(p1.x, p0.y); m_RectVertices[2] = p1; m_RectVertices[3] = new Vector2(p0.x, p1.y); Handles.color = Color.white; Handles.DrawSolidRectangleWithOutline(m_RectVertices, color, Color.clear); } } public override void OnEnable() { m_ShutterAngle = FindSetting((Settings x) => x.shutterAngle); m_SampleCount = FindSetting((Settings x) => x.sampleCount); m_FrameBlending = FindSetting((Settings x) => x.frameBlending); } public override void OnInspectorGUI() { if (m_GraphDrawer == null) m_GraphDrawer = new GraphDrawer(); EditorGUILayout.LabelField("Shutter Speed Simulation", EditorStyles.boldLabel); EditorGUI.indentLevel++; m_GraphDrawer.DrawShutterGraph(m_ShutterAngle.floatValue); EditorGUILayout.PropertyField(m_ShutterAngle); EditorGUILayout.PropertyField(m_SampleCount); EditorGUI.indentLevel--; EditorGUILayout.Space(); EditorGUILayout.LabelField("Multiple Frame Blending", EditorStyles.boldLabel); EditorGUI.indentLevel++; float fbValue = m_FrameBlending.floatValue; m_GraphDrawer.DrawBlendingGraph(fbValue); EditorGUILayout.PropertyField(m_FrameBlending); if (fbValue > 0f) EditorGUILayout.HelpBox("Multi-Frame Blending lowers precision of the final picture for optimization purposes.", MessageType.Info); EditorGUI.indentLevel--; } } }