using UnityEditorInternal; using UnityEngine; using UnityEngine.PostProcessing; namespace UnityEditor.PostProcessing { public class VectorscopeMonitor : PostProcessingMonitor { static GUIContent s_MonitorTitle = new GUIContent("Vectorscope"); ComputeShader m_ComputeShader; ComputeBuffer m_Buffer; Material m_Material; RenderTexture m_VectorscopeTexture; Rect m_MonitorAreaRect; public VectorscopeMonitor() { m_ComputeShader = EditorResources.Load("Monitors/VectorscopeCompute.compute"); } public override void Dispose() { GraphicsUtils.Destroy(m_Material); GraphicsUtils.Destroy(m_VectorscopeTexture); if (m_Buffer != null) m_Buffer.Release(); m_Material = null; m_VectorscopeTexture = null; m_Buffer = null; } public override bool IsSupported() { return m_ComputeShader != null && GraphicsUtils.supportsDX11; } public override GUIContent GetMonitorTitle() { return s_MonitorTitle; } public override void OnMonitorSettings() { EditorGUI.BeginChangeCheck(); bool refreshOnPlay = m_MonitorSettings.refreshOnPlay; float exposure = m_MonitorSettings.vectorscopeExposure; bool showBackground = m_MonitorSettings.vectorscopeShowBackground; refreshOnPlay = GUILayout.Toggle(refreshOnPlay, new GUIContent(FxStyles.playIcon, "Keep refreshing the vectorscope in play mode; this may impact performances."), FxStyles.preButton); exposure = GUILayout.HorizontalSlider(exposure, 0.05f, 0.3f, FxStyles.preSlider, FxStyles.preSliderThumb, GUILayout.Width(40f)); showBackground = GUILayout.Toggle(showBackground, new GUIContent(FxStyles.checkerIcon, "Show an YUV background in the vectorscope."), FxStyles.preButton); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(m_BaseEditor.serializedObject.targetObject, "Vectorscope Settings Changed"); m_MonitorSettings.refreshOnPlay = refreshOnPlay; m_MonitorSettings.vectorscopeExposure = exposure; m_MonitorSettings.vectorscopeShowBackground = showBackground; InternalEditorUtility.RepaintAllViews(); } } public override void OnMonitorGUI(Rect r) { if (Event.current.type == EventType.Repaint) { // If m_MonitorAreaRect isn't set the preview was just opened so refresh the render to get the vectoscope data if (Mathf.Approximately(m_MonitorAreaRect.width, 0) && Mathf.Approximately(m_MonitorAreaRect.height, 0)) InternalEditorUtility.RepaintAllViews(); // Sizing float size = 0f; if (r.width < r.height) { size = m_VectorscopeTexture != null ? Mathf.Min(m_VectorscopeTexture.width, r.width - 35f) : r.width; } else { size = m_VectorscopeTexture != null ? Mathf.Min(m_VectorscopeTexture.height, r.height - 25f) : r.height; } m_MonitorAreaRect = new Rect( Mathf.Floor(r.x + r.width / 2f - size / 2f), Mathf.Floor(r.y + r.height / 2f - size / 2f - 5f), size, size ); if (m_VectorscopeTexture != null) { m_Material.SetFloat("_Exposure", m_MonitorSettings.vectorscopeExposure); var oldActive = RenderTexture.active; Graphics.Blit(null, m_VectorscopeTexture, m_Material, m_MonitorSettings.vectorscopeShowBackground ? 0 : 1); RenderTexture.active = oldActive; Graphics.DrawTexture(m_MonitorAreaRect, m_VectorscopeTexture); var color = Color.white; const float kTickSize = 10f; const int kTickCount = 24; float radius = m_MonitorAreaRect.width / 2f; float midX = m_MonitorAreaRect.x + radius; float midY = m_MonitorAreaRect.y + radius; var center = new Vector2(midX, midY); // Cross color.a *= 0.5f; Handles.color = color; Handles.DrawLine(new Vector2(midX, m_MonitorAreaRect.y), new Vector2(midX, m_MonitorAreaRect.y + m_MonitorAreaRect.height)); Handles.DrawLine(new Vector2(m_MonitorAreaRect.x, midY), new Vector2(m_MonitorAreaRect.x + m_MonitorAreaRect.width, midY)); if (m_MonitorAreaRect.width > 100f) { color.a = 1f; // Ticks Handles.color = color; for (int i = 0; i < kTickCount; i++) { float a = (float)i / (float)kTickCount; float theta = a * (Mathf.PI * 2f); float tx = Mathf.Cos(theta + (Mathf.PI / 2f)); float ty = Mathf.Sin(theta - (Mathf.PI / 2f)); var innerVec = center + new Vector2(tx, ty) * (radius - kTickSize); var outerVec = center + new Vector2(tx, ty) * radius; Handles.DrawAAPolyLine(3f, innerVec, outerVec); } // Labels (where saturation reaches 75%) color.a = 1f; var oldColor = GUI.color; GUI.color = color * 2f; var point = new Vector2(-0.254f, -0.750f) * radius + center; var rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[R]", FxStyles.tickStyleCenter); point = new Vector2(-0.497f, 0.629f) * radius + center; rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[G]", FxStyles.tickStyleCenter); point = new Vector2(0.750f, 0.122f) * radius + center; rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[B]", FxStyles.tickStyleCenter); point = new Vector2(-0.750f, -0.122f) * radius + center; rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[Y]", FxStyles.tickStyleCenter); point = new Vector2(0.254f, 0.750f) * radius + center; rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[C]", FxStyles.tickStyleCenter); point = new Vector2(0.497f, -0.629f) * radius + center; rect = new Rect(point.x - 10f, point.y - 10f, 20f, 20f); GUI.Label(rect, "[M]", FxStyles.tickStyleCenter); GUI.color = oldColor; } } } } public override void OnFrameData(RenderTexture source) { if (Application.isPlaying && !m_MonitorSettings.refreshOnPlay) return; if (Mathf.Approximately(m_MonitorAreaRect.width, 0) || Mathf.Approximately(m_MonitorAreaRect.height, 0)) return; float ratio = (float)source.width / (float)source.height; int h = 384; int w = Mathf.FloorToInt(h * ratio); var rt = RenderTexture.GetTemporary(w, h, 0, source.format); Graphics.Blit(source, rt); ComputeVectorscope(rt); m_BaseEditor.Repaint(); RenderTexture.ReleaseTemporary(rt); } void CreateBuffer(int width, int height) { m_Buffer = new ComputeBuffer(width * height, sizeof(uint)); } void ComputeVectorscope(RenderTexture source) { if (m_Buffer == null) { CreateBuffer(source.width, source.height); } else if (m_Buffer.count != (source.width * source.height)) { m_Buffer.Release(); CreateBuffer(source.width, source.height); } var cs = m_ComputeShader; int kernel = cs.FindKernel("KVectorscopeClear"); cs.SetBuffer(kernel, "_Vectorscope", m_Buffer); cs.SetVector("_Res", new Vector4(source.width, source.height, 0f, 0f)); cs.Dispatch(kernel, Mathf.CeilToInt(source.width / 32f), Mathf.CeilToInt(source.height / 32f), 1); kernel = cs.FindKernel("KVectorscope"); cs.SetBuffer(kernel, "_Vectorscope", m_Buffer); cs.SetTexture(kernel, "_Source", source); cs.SetInt("_IsLinear", GraphicsUtils.isLinearColorSpace ? 1 : 0); cs.SetVector("_Res", new Vector4(source.width, source.height, 0f, 0f)); cs.Dispatch(kernel, Mathf.CeilToInt(source.width / 32f), Mathf.CeilToInt(source.height / 32f), 1); if (m_VectorscopeTexture == null || m_VectorscopeTexture.width != source.width || m_VectorscopeTexture.height != source.height) { GraphicsUtils.Destroy(m_VectorscopeTexture); m_VectorscopeTexture = new RenderTexture(source.width, source.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear) { hideFlags = HideFlags.DontSave, wrapMode = TextureWrapMode.Clamp, filterMode = FilterMode.Bilinear }; } if (m_Material == null) m_Material = new Material(Shader.Find("Hidden/Post FX/Monitors/Vectorscope Render")) { hideFlags = HideFlags.DontSave }; m_Material.SetBuffer("_Vectorscope", m_Buffer); m_Material.SetVector("_Size", new Vector2(m_VectorscopeTexture.width, m_VectorscopeTexture.height)); } } }