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;
}
}
}