using System; using System.Collections.Generic; namespace UnityEngine.Rendering.PostProcessing { /// /// A list of debug overlays. /// public enum DebugOverlay { /// /// No overlay. /// None, /// /// Displays the depth buffer. /// Depth, /// /// Displays the screen-space normals buffer. /// Normals, /// /// Displays the screen-space motion vectors. /// MotionVectors, /// /// Dims the screen and displays NaN and Inf pixels with a bright pink color. /// NANTracker, /// /// A color blindness simulator. /// ColorBlindnessSimulation, /// /// A menu item separator for the inspector. Do not use. /// _, /// /// Displays the raw ambient occlusion map. /// AmbientOcclusion, /// /// Displays the bloom buffer. /// BloomBuffer, /// /// Displays the thresholded buffer used to generate bloom. /// BloomThreshold, /// /// Displays depth of field helpers. /// DepthOfField } /// /// A list of color blindness types. /// public enum ColorBlindnessType { /// /// Deuteranopia (red-green color blindness). /// Deuteranopia, /// /// Protanopia (red-green color blindness). /// Protanopia, /// /// Tritanopia (blue-yellow color blindness). /// Tritanopia } /// /// This class centralizes rendering commands for debug modes. /// [Serializable] public sealed class PostProcessDebugLayer { /// /// Light meter renderer. /// public LightMeterMonitor lightMeter; /// /// Histogram renderer. /// public HistogramMonitor histogram; /// /// Waveform renderer. /// public WaveformMonitor waveform; /// /// Vectorscope monitor. /// public VectorscopeMonitor vectorscope; Dictionary m_Monitors; // Current frame size int frameWidth; int frameHeight; /// /// The render target used to render debug overlays in. /// public RenderTexture debugOverlayTarget { get; private set; } /// /// Returns true if the frame that was just drawn had an active debug overlay. /// public bool debugOverlayActive { get; private set; } /// /// The debug overlay requested for the current frame. It is reset to None once the /// frame has finished rendering. /// public DebugOverlay debugOverlay { get; private set; } /// /// Debug overlay settings wrapper. /// [Serializable] public class OverlaySettings { /// /// Should we remap depth to a linear range? /// public bool linearDepth = false; /// /// The intensity of motion vector colors. /// [Range(0f, 16f)] public float motionColorIntensity = 4f; /// /// The size of the motion vector grid. /// [Range(4, 128)] public int motionGridSize = 64; /// /// The color blindness type to simulate. /// public ColorBlindnessType colorBlindnessType = ColorBlindnessType.Deuteranopia; /// /// The strength of the selected color blindness type. /// [Range(0f, 1f)] public float colorBlindnessStrength = 1f; } /// /// Debug overlay settings. /// 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.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; } /// /// Requests the drawing of a monitor for the current frame. /// /// The monitor to request public void RequestMonitorPass(MonitorType monitor) { m_Monitors[monitor].requested = true; } /// /// Requests the drawing of a debug overlay for the current frame. /// /// The debug overlay to request 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; } /// /// Blit a source render target to the debug overlay target. /// /// The command buffer to send render commands to /// The source target /// The property sheet to use for the blit /// The pass to use for the property sheet 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 !UNITY_2022_1_OR_NEWER if (context.camera.actualRenderingPath == RenderingPath.DeferredLighting) sheet.EnableKeyword("SOURCE_GBUFFER"); #endif 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; } } }