using System; using System.Collections.Generic; namespace UnityEngine.Rendering.Universal.PostProcessing { /// /// This render feature is responsible for: /// /// /// Injecting the render passes for custom post processing /// /// /// Ordering and categorizing the custom post processing renderers /// /// /// public class QuibliPostProcess : ScriptableRendererFeature { /// /// The settings for the custom post processing render feature. /// [Serializable] public class Settings { [SerializeField] public List renderersAfterOpaqueAndSky; [SerializeField] public List renderersBeforePostProcess; [SerializeField] public List renderersAfterPostProcess; public Settings() { renderersAfterOpaqueAndSky = new List(); renderersBeforePostProcess = new List(); renderersAfterPostProcess = new List(); } } /// /// The settings of the render feature. /// [SerializeField] public Settings settings = new Settings(); private CompoundPass _afterOpaqueAndSky; private CompoundPass _beforePostProcess; private CompoundPass _afterPostProcess; #if !UNITY_2021_2_OR_NEWER /// /// A handle to the "_AfterPostProcessTexture" used as the target for the builtin post /// processing pass in the last camera in the camera stack. /// private RenderTargetHandle _afterPostProcessColor; #endif /// /// Injects the custom post-processing render passes. /// /// The renderer /// Rendering state. Use this to setup render passes. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { // Only inject passes if post processing is enabled if (renderingData.cameraData.postProcessEnabled) { // For each pass, only inject if there is at least one custom post-processing renderer class in it. if (_afterOpaqueAndSky.HasPostProcessRenderers && _afterOpaqueAndSky.PrepareRenderers(in renderingData)) { renderer.EnqueuePass(_afterOpaqueAndSky); } if (_beforePostProcess.HasPostProcessRenderers && _beforePostProcess.PrepareRenderers(in renderingData)) { renderer.EnqueuePass(_beforePostProcess); } if (_afterPostProcess.HasPostProcessRenderers && _afterPostProcess.PrepareRenderers(in renderingData)) { renderer.EnqueuePass(_afterPostProcess); } #if !UNITY_2022_1_OR_NEWER SetupRenderPassesCore(renderer, in renderingData); #endif } } #if UNITY_2022_1_OR_NEWER public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData) { SetupRenderPassesCore(renderer, renderingData); } #endif void SetupRenderPassesCore(ScriptableRenderer renderer, in RenderingData renderingData) { #if UNITY_2022_1_OR_NEWER var cameraTarget = renderer.cameraColorTargetHandle; #else var cameraTarget = renderer.cameraColorTarget; #endif // For each pass, only inject if there is at least one custom post-processing renderer class in it. if (_afterOpaqueAndSky.HasPostProcessRenderers && _afterOpaqueAndSky.PrepareRenderers(in renderingData)) { _afterOpaqueAndSky.Setup(cameraTarget, cameraTarget); } if (_beforePostProcess.HasPostProcessRenderers && _beforePostProcess.PrepareRenderers(in renderingData)) { _beforePostProcess.Setup(cameraTarget, cameraTarget); } if (_afterPostProcess.HasPostProcessRenderers && _afterPostProcess.PrepareRenderers(in renderingData)) { #if UNITY_2021_2_OR_NEWER _afterPostProcess.Setup(cameraTarget, cameraTarget); #else // If this camera resolve to the final target, then both the source and // destination will be "_AfterPostProcessTexture" // (Note: a final blit/post pass is added by the renderer). var source = renderingData.cameraData.resolveFinalTarget ? _afterPostProcessColor.Identifier() : renderer.cameraColorTarget; _afterPostProcess.Setup(source, source); #endif } } /// /// Initializes the custom post-processing render passes. /// public override void Create() { #if !UNITY_2021_2_OR_NEWER // This is copied from the forward renderer. _afterPostProcessColor.Init("_AfterPostProcessTexture"); #endif // Create the three render passes and send the custom post-processing renderer classes to each. Dictionary shared = new Dictionary(); _afterOpaqueAndSky = new CompoundPass(InjectionPoint.AfterOpaqueAndSky, InstantiateRenderers(settings.renderersAfterOpaqueAndSky, shared)); _beforePostProcess = new CompoundPass(InjectionPoint.BeforePostProcess, InstantiateRenderers(settings.renderersBeforePostProcess, shared)); _afterPostProcess = new CompoundPass(InjectionPoint.AfterPostProcess, InstantiateRenderers(settings.renderersAfterPostProcess, shared)); } /// /// Converts the class name (AssemblyQualifiedName) to an instance. Filters out types that /// don't exist or don't match the requirements. /// /// The list of assembly-qualified class names /// Dictionary of shared instances keyed by class name /// List of renderers private List InstantiateRenderers(List names, Dictionary shared) { var renderers = new List(names.Count); foreach (var n in names) { if (shared.TryGetValue(n, out var renderer)) { renderers.Add(renderer); } else { var type = Type.GetType(n); if (type == null || !type.IsSubclassOf(typeof(CompoundRenderer))) continue; var attribute = CompoundRendererFeatureAttribute.GetAttribute(type); if (attribute == null) continue; renderer = Activator.CreateInstance(type) as CompoundRenderer; renderers.Add(renderer); if (attribute.ShareInstance) shared.Add(n, renderer); } } return renderers; } } }