using UnityEditorInternal; using UnityEngine; using UnityEngine.PostProcessing; namespace UnityEditor.PostProcessing { public class WaveformMonitor : PostProcessingMonitor { static GUIContent s_MonitorTitle = new GUIContent("Waveform"); ComputeShader m_ComputeShader; ComputeBuffer m_Buffer; Material m_Material; RenderTexture m_WaveformTexture; Rect m_MonitorAreaRect; public WaveformMonitor() { m_ComputeShader = EditorResources.Load("Monitors/WaveformCompute.compute"); } public override void Dispose() { GraphicsUtils.Destroy(m_Material); GraphicsUtils.Destroy(m_WaveformTexture); if (m_Buffer != null) m_Buffer.Release(); m_Material = null; m_WaveformTexture = 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.waveformExposure; bool Y = m_MonitorSettings.waveformY; bool R = m_MonitorSettings.waveformR; bool G = m_MonitorSettings.waveformG; bool B = m_MonitorSettings.waveformB; refreshOnPlay = GUILayout.Toggle(refreshOnPlay, new GUIContent(FxStyles.playIcon, "Keep refreshing the waveform in play mode; this may impact performances."), FxStyles.preButton); exposure = GUILayout.HorizontalSlider(exposure, 0.05f, 0.3f, FxStyles.preSlider, FxStyles.preSliderThumb, GUILayout.Width(40f)); Y = GUILayout.Toggle(Y, new GUIContent("Y", "Show the luminance waveform only."), FxStyles.preButton); if (Y) { R = false; G = false; B = false; } R = GUILayout.Toggle(R, new GUIContent("R", "Show the red waveform."), FxStyles.preButton); G = GUILayout.Toggle(G, new GUIContent("G", "Show the green waveform."), FxStyles.preButton); B = GUILayout.Toggle(B, new GUIContent("B", "Show the blue waveform."), FxStyles.preButton); if (R || G || B) Y = false; if (!Y && !R && !G && !B) { R = true; G = true; B = true; } if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(m_BaseEditor.serializedObject.targetObject, "Waveforme Settings Changed"); m_MonitorSettings.refreshOnPlay = refreshOnPlay; m_MonitorSettings.waveformExposure = exposure; m_MonitorSettings.waveformY = Y; m_MonitorSettings.waveformR = R; m_MonitorSettings.waveformG = G; m_MonitorSettings.waveformB = B; 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 waveform data if (Mathf.Approximately(m_MonitorAreaRect.width, 0) && Mathf.Approximately(m_MonitorAreaRect.height, 0)) InternalEditorUtility.RepaintAllViews(); // Sizing float width = m_WaveformTexture != null ? Mathf.Min(m_WaveformTexture.width, r.width - 65f) : r.width; float height = m_WaveformTexture != null ? Mathf.Min(m_WaveformTexture.height, r.height - 45f) : r.height; m_MonitorAreaRect = new Rect( Mathf.Floor(r.x + r.width / 2f - width / 2f), Mathf.Floor(r.y + r.height / 2f - height / 2f - 5f), width, height ); if (m_WaveformTexture != null) { m_Material.SetFloat("_Exposure", m_MonitorSettings.waveformExposure); var oldActive = RenderTexture.active; Graphics.Blit(null, m_WaveformTexture, m_Material, 0); RenderTexture.active = oldActive; Graphics.DrawTexture(m_MonitorAreaRect, m_WaveformTexture); var color = Color.white; const float kTickSize = 5f; // Rect, lines & ticks points // A B C D E // P F // O G // N H // M L K J I var A = new Vector3(m_MonitorAreaRect.x, m_MonitorAreaRect.y); var E = new Vector3(A.x + m_MonitorAreaRect.width + 1f, m_MonitorAreaRect.y); var I = new Vector3(E.x, E.y + m_MonitorAreaRect.height + 1f); var M = new Vector3(A.x, I.y); var C = new Vector3(A.x + (E.x - A.x) / 2f, A.y); var G = new Vector3(E.x, E.y + (I.y - E.y) / 2f); var K = new Vector3(M.x + (I.x - M.x) / 2f, M.y); var O = new Vector3(A.x, A.y + (M.y - A.y) / 2f); var P = new Vector3(A.x, A.y + (O.y - A.y) / 2f); var F = new Vector3(E.x, E.y + (G.y - E.y) / 2f); var N = new Vector3(A.x, O.y + (M.y - O.y) / 2f); var H = new Vector3(E.x, G.y + (I.y - G.y) / 2f); var B = new Vector3(A.x + (C.x - A.x) / 2f, A.y); var L = new Vector3(M.x + (K.x - M.x) / 2f, M.y); var D = new Vector3(C.x + (E.x - C.x) / 2f, A.y); var J = new Vector3(K.x + (I.x - K.x) / 2f, M.y); // Borders Handles.color = color; Handles.DrawLine(A, E); Handles.DrawLine(E, I); Handles.DrawLine(I, M); Handles.DrawLine(M, new Vector3(A.x, A.y - 1f)); // Vertical ticks Handles.DrawLine(A, new Vector3(A.x - kTickSize, A.y)); Handles.DrawLine(P, new Vector3(P.x - kTickSize, P.y)); Handles.DrawLine(O, new Vector3(O.x - kTickSize, O.y)); Handles.DrawLine(N, new Vector3(N.x - kTickSize, N.y)); Handles.DrawLine(M, new Vector3(M.x - kTickSize, M.y)); Handles.DrawLine(E, new Vector3(E.x + kTickSize, E.y)); Handles.DrawLine(F, new Vector3(F.x + kTickSize, F.y)); Handles.DrawLine(G, new Vector3(G.x + kTickSize, G.y)); Handles.DrawLine(H, new Vector3(H.x + kTickSize, H.y)); Handles.DrawLine(I, new Vector3(I.x + kTickSize, I.y)); // Horizontal ticks Handles.DrawLine(A, new Vector3(A.x, A.y - kTickSize)); Handles.DrawLine(B, new Vector3(B.x, B.y - kTickSize)); Handles.DrawLine(C, new Vector3(C.x, C.y - kTickSize)); Handles.DrawLine(D, new Vector3(D.x, D.y - kTickSize)); Handles.DrawLine(E, new Vector3(E.x, E.y - kTickSize)); Handles.DrawLine(M, new Vector3(M.x, M.y + kTickSize)); Handles.DrawLine(L, new Vector3(L.x, L.y + kTickSize)); Handles.DrawLine(K, new Vector3(K.x, K.y + kTickSize)); Handles.DrawLine(J, new Vector3(J.x, J.y + kTickSize)); Handles.DrawLine(I, new Vector3(I.x, I.y + kTickSize)); // Labels GUI.color = color; GUI.Label(new Rect(A.x - kTickSize - 34f, A.y - 15f, 30f, 30f), "1.0", FxStyles.tickStyleRight); GUI.Label(new Rect(O.x - kTickSize - 34f, O.y - 15f, 30f, 30f), "0.5", FxStyles.tickStyleRight); GUI.Label(new Rect(M.x - kTickSize - 34f, M.y - 15f, 30f, 30f), "0.0", FxStyles.tickStyleRight); GUI.Label(new Rect(E.x + kTickSize + 4f, E.y - 15f, 30f, 30f), "1.0", FxStyles.tickStyleLeft); GUI.Label(new Rect(G.x + kTickSize + 4f, G.y - 15f, 30f, 30f), "0.5", FxStyles.tickStyleLeft); GUI.Label(new Rect(I.x + kTickSize + 4f, I.y - 15f, 30f, 30f), "0.0", FxStyles.tickStyleLeft); GUI.Label(new Rect(M.x - 15f, M.y + kTickSize - 4f, 30f, 30f), "0.0", FxStyles.tickStyleCenter); GUI.Label(new Rect(K.x - 15f, K.y + kTickSize - 4f, 30f, 30f), "0.5", FxStyles.tickStyleCenter); GUI.Label(new Rect(I.x - 15f, I.y + kTickSize - 4f, 30f, 30f), "1.0", FxStyles.tickStyleCenter); } } } 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); ComputeWaveform(rt); m_BaseEditor.Repaint(); RenderTexture.ReleaseTemporary(rt); } void CreateBuffer(int width, int height) { m_Buffer = new ComputeBuffer(width * height, sizeof(uint) << 2); } void ComputeWaveform(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 channels = m_MonitorSettings.waveformY ? new Vector4(0f, 0f, 0f, 1f) : new Vector4(m_MonitorSettings.waveformR ? 1f : 0f, m_MonitorSettings.waveformG ? 1f : 0f, m_MonitorSettings.waveformB ? 1f : 0f, 0f); var cs = m_ComputeShader; int kernel = cs.FindKernel("KWaveformClear"); cs.SetBuffer(kernel, "_Waveform", m_Buffer); cs.Dispatch(kernel, source.width, 1, 1); kernel = cs.FindKernel("KWaveform"); cs.SetBuffer(kernel, "_Waveform", m_Buffer); cs.SetTexture(kernel, "_Source", source); cs.SetInt("_IsLinear", GraphicsUtils.isLinearColorSpace ? 1 : 0); cs.SetVector("_Channels", channels); cs.Dispatch(kernel, source.width, 1, 1); if (m_WaveformTexture == null || m_WaveformTexture.width != source.width || m_WaveformTexture.height != source.height) { GraphicsUtils.Destroy(m_WaveformTexture); m_WaveformTexture = 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/Waveform Render")) { hideFlags = HideFlags.DontSave }; m_Material.SetBuffer("_Waveform", m_Buffer); m_Material.SetVector("_Size", new Vector2(m_WaveformTexture.width, m_WaveformTexture.height)); m_Material.SetVector("_Channels", channels); } } }