using System; using System.Collections.Generic; namespace UnityEngine.Rendering.PostProcessing { /// <summary> /// A list of debug overlays. /// </summary> public enum DebugOverlay { /// <summary> /// No overlay. /// </summary> None, /// <summary> /// Displays the depth buffer. /// </summary> Depth, /// <summary> /// Displays the screen-space normals buffer. /// </summary> Normals, /// <summary> /// Displays the screen-space motion vectors. /// </summary> MotionVectors, /// <summary> /// Dims the screen and displays NaN and Inf pixels with a bright pink color. /// </summary> NANTracker, /// <summary> /// A color blindness simulator. /// </summary> ColorBlindnessSimulation, /// <summary> /// A menu item separator for the inspector. Do not use. /// </summary> _, /// <summary> /// Displays the raw ambient occlusion map. /// </summary> AmbientOcclusion, /// <summary> /// Displays the bloom buffer. /// </summary> BloomBuffer, /// <summary> /// Displays the thresholded buffer used to generate bloom. /// </summary> BloomThreshold, /// <summary> /// Displays depth of field helpers. /// </summary> DepthOfField } /// <summary> /// A list of color blindness types. /// </summary> public enum ColorBlindnessType { /// <summary> /// Deuteranopia (red-green color blindness). /// </summary> Deuteranopia, /// <summary> /// Protanopia (red-green color blindness). /// </summary> Protanopia, /// <summary> /// Tritanopia (blue-yellow color blindness). /// </summary> Tritanopia } /// <summary> /// This class centralizes rendering commands for debug modes. /// </summary> [Serializable] public sealed class PostProcessDebugLayer { /// <summary> /// Light meter renderer. /// </summary> public LightMeterMonitor lightMeter; /// <summary> /// Histogram renderer. /// </summary> public HistogramMonitor histogram; /// <summary> /// Waveform renderer. /// </summary> public WaveformMonitor waveform; /// <summary> /// Vectorscope monitor. /// </summary> public VectorscopeMonitor vectorscope; Dictionary<MonitorType, Monitor> m_Monitors; // Current frame size int frameWidth; int frameHeight; /// <summary> /// The render target used to render debug overlays in. /// </summary> public RenderTexture debugOverlayTarget { get; private set; } /// <summary> /// Returns <c>true</c> if the frame that was just drawn had an active debug overlay. /// </summary> public bool debugOverlayActive { get; private set; } /// <summary> /// The debug overlay requested for the current frame. It is reset to <c>None</c> once the /// frame has finished rendering. /// </summary> public DebugOverlay debugOverlay { get; private set; } /// <summary> /// Debug overlay settings wrapper. /// </summary> [Serializable] public class OverlaySettings { /// <summary> /// Should we remap depth to a linear range? /// </summary> public bool linearDepth = false; /// <summary> /// The intensity of motion vector colors. /// </summary> [Range(0f, 16f)] public float motionColorIntensity = 4f; /// <summary> /// The size of the motion vector grid. /// </summary> [Range(4, 128)] public int motionGridSize = 64; /// <summary> /// The color blindness type to simulate. /// </summary> public ColorBlindnessType colorBlindnessType = ColorBlindnessType.Deuteranopia; /// <summary> /// The strength of the selected color blindness type. /// </summary> [Range(0f, 1f)] public float colorBlindnessStrength = 1f; } /// <summary> /// Debug overlay settings. /// </summary> public OverlaySettings overlaySettings; internal void OnEnable() { RuntimeUtilities.CreateIfNull(ref lightMeter); RuntimeUtilities.CreateIfNull(ref histogram); RuntimeUtilities.CreateIfNull(ref waveform); RuntimeUtilities.CreateIfNull(ref vectorscope); RuntimeUtilities.CreateIfNull(ref overlaySettings); m_Monitors = new Dictionary<MonitorType, Monitor> { { MonitorType.LightMeter, lightMeter }, { MonitorType.Histogram, histogram }, { MonitorType.Waveform, waveform }, { MonitorType.Vectorscope, vectorscope } }; foreach (var kvp in m_Monitors) kvp.Value.OnEnable(); } internal void OnDisable() { foreach (var kvp in m_Monitors) kvp.Value.OnDisable(); DestroyDebugOverlayTarget(); } void DestroyDebugOverlayTarget() { RuntimeUtilities.Destroy(debugOverlayTarget); debugOverlayTarget = null; } /// <summary> /// Requests the drawing of a monitor for the current frame. /// </summary> /// <param name="monitor">The monitor to request</param> public void RequestMonitorPass(MonitorType monitor) { m_Monitors[monitor].requested = true; } /// <summary> /// Requests the drawing of a debug overlay for the current frame. /// </summary> /// <param name="mode">The debug overlay to request</param> public void RequestDebugOverlay(DebugOverlay mode) { debugOverlay = mode; } // Sets the current frame size - used to make sure the debug overlay target is always the // correct size - mostly useful in the editor as the user can easily resize the gameview. internal void SetFrameSize(int width, int height) { frameWidth = width; frameHeight = height; debugOverlayActive = false; } /// <summary> /// Blit a source render target to the debug overlay target. /// </summary> /// <param name="cmd">The command buffer to send render commands to</param> /// <param name="source">The source target</param> /// <param name="sheet">The property sheet to use for the blit</param> /// <param name="pass">The pass to use for the property sheet</param> public void PushDebugOverlay(CommandBuffer cmd, RenderTargetIdentifier source, PropertySheet sheet, int pass) { if (debugOverlayTarget == null || !debugOverlayTarget.IsCreated() || debugOverlayTarget.width != frameWidth || debugOverlayTarget.height != frameHeight) { RuntimeUtilities.Destroy(debugOverlayTarget); debugOverlayTarget = new RenderTexture(frameWidth, frameHeight, 0, RenderTextureFormat.ARGB32) { name = "Debug Overlay Target", anisoLevel = 1, filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp, hideFlags = HideFlags.HideAndDontSave }; debugOverlayTarget.Create(); } cmd.BlitFullscreenTriangle(source, debugOverlayTarget, sheet, pass); debugOverlayActive = true; } internal DepthTextureMode GetCameraFlags() { if (debugOverlay == DebugOverlay.Depth) return DepthTextureMode.Depth; if (debugOverlay == DebugOverlay.Normals) return DepthTextureMode.DepthNormals; if (debugOverlay == DebugOverlay.MotionVectors) return DepthTextureMode.MotionVectors | DepthTextureMode.Depth; return DepthTextureMode.None; } internal void RenderMonitors(PostProcessRenderContext context) { // Monitors bool anyActive = false; bool needsHalfRes = false; foreach (var kvp in m_Monitors) { bool active = kvp.Value.IsRequestedAndSupported(context); anyActive |= active; needsHalfRes |= active && kvp.Value.NeedsHalfRes(); } if (!anyActive) return; var cmd = context.command; cmd.BeginSample("Monitors"); if (needsHalfRes) { cmd.GetTemporaryRT(ShaderIDs.HalfResFinalCopy, context.width / 2, context.height / 2, 0, FilterMode.Bilinear, context.sourceFormat); cmd.Blit(context.destination, ShaderIDs.HalfResFinalCopy); } foreach (var kvp in m_Monitors) { var monitor = kvp.Value; if (monitor.requested) monitor.Render(context); } if (needsHalfRes) cmd.ReleaseTemporaryRT(ShaderIDs.HalfResFinalCopy); cmd.EndSample("Monitors"); } internal void RenderSpecialOverlays(PostProcessRenderContext context) { if (debugOverlay == DebugOverlay.Depth) { var sheet = context.propertySheets.Get(context.resources.shaders.debugOverlays); sheet.properties.SetVector(ShaderIDs.Params, new Vector4(overlaySettings.linearDepth ? 1f : 0f, 0f, 0f, 0f)); PushDebugOverlay(context.command, BuiltinRenderTextureType.None, sheet, 0); } else if (debugOverlay == DebugOverlay.Normals) { var sheet = context.propertySheets.Get(context.resources.shaders.debugOverlays); sheet.ClearKeywords(); if (context.camera.actualRenderingPath == RenderingPath.DeferredLighting) sheet.EnableKeyword("SOURCE_GBUFFER"); PushDebugOverlay(context.command, BuiltinRenderTextureType.None, sheet, 1); } else if (debugOverlay == DebugOverlay.MotionVectors) { var sheet = context.propertySheets.Get(context.resources.shaders.debugOverlays); sheet.properties.SetVector(ShaderIDs.Params, new Vector4(overlaySettings.motionColorIntensity, overlaySettings.motionGridSize, 0f, 0f)); PushDebugOverlay(context.command, context.source, sheet, 2); } else if (debugOverlay == DebugOverlay.NANTracker) { var sheet = context.propertySheets.Get(context.resources.shaders.debugOverlays); PushDebugOverlay(context.command, context.source, sheet, 3); } else if (debugOverlay == DebugOverlay.ColorBlindnessSimulation) { var sheet = context.propertySheets.Get(context.resources.shaders.debugOverlays); sheet.properties.SetVector(ShaderIDs.Params, new Vector4(overlaySettings.colorBlindnessStrength, 0f, 0f, 0f)); PushDebugOverlay(context.command, context.source, sheet, 4 + (int)overlaySettings.colorBlindnessType); } } internal void EndFrame() { foreach (var kvp in m_Monitors) kvp.Value.requested = false; if (!debugOverlayActive) DestroyDebugOverlayTarget(); debugOverlay = DebugOverlay.None; } } }