using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR;
///
/// FullScreenPass is a renderer feature used to change screen appearance such as post processing effect. This implementation
/// lets it's user create an effect with minimal code involvement.
///
[URPHelpURL("renderer-features/renderer-feature-full-screen-pass")]
public class FullScreenPassRendererFeature : ScriptableRendererFeature
{
///
/// An injection point for the full screen pass. This is similar to RenderPassEvent enum but limits to only supported events.
///
public enum InjectionPoint
{
///
/// Inject a full screen pass before transparents are rendered
///
BeforeRenderingTransparents = RenderPassEvent.BeforeRenderingTransparents,
///
/// Inject a full screen pass before post processing is rendered
///
BeforeRenderingPostProcessing = RenderPassEvent.BeforeRenderingPostProcessing,
///
/// Inject a full screen pass after post processing is rendered
///
AfterRenderingPostProcessing = RenderPassEvent.AfterRenderingPostProcessing
}
///
/// Material the Renderer Feature uses to render the effect.
///
public Material passMaterial;
///
/// Selection for when the effect is rendered.
///
public InjectionPoint injectionPoint = InjectionPoint.AfterRenderingPostProcessing;
///
/// One or more requirements for pass. Based on chosen flags certain passes will be added to the pipeline.
///
public ScriptableRenderPassInput requirements = ScriptableRenderPassInput.Color;
///
/// An index that tells renderer feature which pass to use if passMaterial contains more than one. Default is 0.
/// We draw custom pass index entry with the custom dropdown inside FullScreenPassRendererFeatureEditor that sets this value.
/// Setting it directly will be overridden by the editor class.
///
[HideInInspector]
public int passIndex = 0;
private FullScreenRenderPass fullScreenPass;
private bool requiresColor;
private bool injectedBeforeTransparents;
///
public override void Create()
{
fullScreenPass = new FullScreenRenderPass();
fullScreenPass.renderPassEvent = (RenderPassEvent)injectionPoint;
// This copy of requirements is used as a parameter to configure input in order to avoid copy color pass
ScriptableRenderPassInput modifiedRequirements = requirements;
requiresColor = (requirements & ScriptableRenderPassInput.Color) != 0;
injectedBeforeTransparents = injectionPoint <= InjectionPoint.BeforeRenderingTransparents;
if (requiresColor && !injectedBeforeTransparents)
{
// Removing Color flag in order to avoid unnecessary CopyColor pass
// Does not apply to before rendering transparents, due to how depth and color are being handled until
// that injection point.
modifiedRequirements ^= ScriptableRenderPassInput.Color;
}
fullScreenPass.ConfigureInput(modifiedRequirements);
}
///
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (passMaterial == null)
{
Debug.LogWarningFormat("Missing Post Processing effect Material. {0} Fullscreen pass will not execute. Check for missing reference in the assigned renderer.", GetType().Name);
return;
}
fullScreenPass.Setup(passMaterial, passIndex, requiresColor, injectedBeforeTransparents, "FullScreenPassRendererFeature", renderingData);
renderer.EnqueuePass(fullScreenPass);
}
///
protected override void Dispose(bool disposing)
{
fullScreenPass.Dispose();
}
class FullScreenRenderPass : ScriptableRenderPass
{
private Material m_PassMaterial;
private int m_PassIndex;
private bool m_RequiresColor;
private bool m_IsBeforeTransparents;
private PassData m_PassData;
private ProfilingSampler m_ProfilingSampler;
private RTHandle m_CopiedColor;
private static readonly int m_BlitTextureShaderID = Shader.PropertyToID("_BlitTexture");
public void Setup(Material mat, int index, bool requiresColor, bool isBeforeTransparents, string featureName, in RenderingData renderingData)
{
m_PassMaterial = mat;
m_PassIndex = index;
m_RequiresColor = requiresColor;
m_IsBeforeTransparents = isBeforeTransparents;
m_ProfilingSampler ??= new ProfilingSampler(featureName);
var colorCopyDescriptor = renderingData.cameraData.cameraTargetDescriptor;
colorCopyDescriptor.depthBufferBits = (int) DepthBits.None;
RenderingUtils.ReAllocateIfNeeded(ref m_CopiedColor, colorCopyDescriptor, name: "_FullscreenPassColorCopy");
m_PassData ??= new PassData();
}
public void Dispose()
{
m_CopiedColor?.Release();
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
m_PassData.effectMaterial = m_PassMaterial;
m_PassData.passIndex = m_PassIndex;
m_PassData.requiresColor = m_RequiresColor;
m_PassData.isBeforeTransparents = m_IsBeforeTransparents;
m_PassData.profilingSampler = m_ProfilingSampler;
m_PassData.copiedColor = m_CopiedColor;
ExecutePass(m_PassData, ref renderingData, ref context);
}
// RG friendly method
private static void ExecutePass(PassData passData, ref RenderingData renderingData, ref ScriptableRenderContext context)
{
var passMaterial = passData.effectMaterial;
var passIndex = passData.passIndex;
var requiresColor = passData.requiresColor;
var isBeforeTransparents = passData.isBeforeTransparents;
var copiedColor = passData.copiedColor;
var profilingSampler = passData.profilingSampler;
if (passMaterial == null)
{
// should not happen as we check it in feature
return;
}
if (renderingData.cameraData.isPreviewCamera)
{
return;
}
CommandBuffer cmd = renderingData.commandBuffer;
var cameraData = renderingData.cameraData;
using (new ProfilingScope(cmd, profilingSampler))
{
if (requiresColor)
{
// For some reason BlitCameraTexture(cmd, dest, dest) scenario (as with before transparents effects) blitter fails to correctly blit the data
// Sometimes it copies only one effect out of two, sometimes second, sometimes data is invalid (as if sampling failed?).
// Adding RTHandle in between solves this issue.
var source = isBeforeTransparents ? cameraData.renderer.GetCameraColorBackBuffer(cmd) : cameraData.renderer.cameraColorTargetHandle;
Blitter.BlitCameraTexture(cmd, source, copiedColor);
passMaterial.SetTexture(m_BlitTextureShaderID, copiedColor);
}
CoreUtils.SetRenderTarget(cmd, cameraData.renderer.GetCameraColorBackBuffer(cmd));
CoreUtils.DrawFullScreen(cmd, passMaterial);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
}
private class PassData
{
internal Material effectMaterial;
internal int passIndex;
internal bool requiresColor;
internal bool isBeforeTransparents;
public ProfilingSampler profilingSampler;
public RTHandle copiedColor;
}
}
}