diff --git a/README.md b/README.md
index 046d976..2e767f3 100644
--- a/README.md
+++ b/README.md
@@ -29,10 +29,10 @@ Other custom effects in samples but not used in screenshots:
## Compatibility
-* Unity 2020.2
-* URP 10.2.2
+This branch is under development and it targets:
-**Note:** There is a branch with support for URP 8.2.0 (Unity 2020.1) which you can find [here](https://github.com/yahiaetman/URPCustomPostProcessingStack/tree/URP-8.2.0).
+* Unity 2021.2 & 2021.3
+* URP 12.1
## Features
diff --git a/Runtime/RenderFeatures/CustomPostProcess.cs b/Runtime/RenderFeatures/CustomPostProcess.cs
index eb3e7f9..63e3eeb 100644
--- a/Runtime/RenderFeatures/CustomPostProcess.cs
+++ b/Runtime/RenderFeatures/CustomPostProcess.cs
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
-namespace UnityEngine.Rendering.Universal.PostProcessing {
+namespace UnityEngine.Rendering.Universal.PostProcessing
+{
///
/// This render feature is responsible for:
@@ -21,7 +22,8 @@ public class CustomPostProcess : ScriptableRendererFeature
/// The settings for the custom post processing render feature.
///
[Serializable]
- public class CustomPostProcessSettings {
+ public class CustomPostProcessSettings
+ {
///
/// Three list (one for each injection point) that holds the custom post processing renderers in order of execution during the render pass
@@ -29,7 +31,8 @@ public class CustomPostProcessSettings {
[SerializeField]
public List renderersAfterOpaqueAndSky, renderersBeforePostProcess, renderersAfterPostProcess;
- public CustomPostProcessSettings(){
+ public CustomPostProcessSettings()
+ {
renderersAfterOpaqueAndSky = new List();
renderersBeforePostProcess = new List();
renderersAfterPostProcess = new List();
@@ -46,11 +49,6 @@ public CustomPostProcessSettings(){
///
private CustomPostProcessRenderPass m_AfterOpaqueAndSky, m_BeforePostProcess, m_AfterPostProcess;
- ///
- /// 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 m_AfterPostProcessColor;
-
///
/// Injects the custom post-processing render passes.
///
@@ -58,21 +56,26 @@ public CustomPostProcessSettings(){
/// Rendering state. Use this to setup render passes.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
+ // UniversalRenderer u_renderer = renderer as UniversalRenderer;
+ // if(u_renderer == null) {
+ // Debug.LogWarning("The CustomPostProcess renderer feature supports the UniversalRenderer only");
+ // return;
+ // }
+
// Only inject passes if post processing is enabled
- if(renderingData.cameraData.postProcessEnabled) {
+ if (renderingData.cameraData.postProcessEnabled)
+ {
// For each pass, only inject if there is at least one custom post-processing renderer class in it.
- if(m_AfterOpaqueAndSky.HasPostProcessRenderers && m_AfterOpaqueAndSky.PrepareRenderers(ref renderingData)){
- m_AfterOpaqueAndSky.Setup(renderer.cameraColorTarget, renderer.cameraColorTarget);
+ if (m_AfterOpaqueAndSky.HasPostProcessRenderers && m_AfterOpaqueAndSky.PrepareRenderers(ref renderingData))
+ {
renderer.EnqueuePass(m_AfterOpaqueAndSky);
}
- if(m_BeforePostProcess.HasPostProcessRenderers && m_BeforePostProcess.PrepareRenderers(ref renderingData)){
- m_BeforePostProcess.Setup(renderer.cameraColorTarget, renderer.cameraColorTarget);
+ if (m_BeforePostProcess.HasPostProcessRenderers && m_BeforePostProcess.PrepareRenderers(ref renderingData))
+ {
renderer.EnqueuePass(m_BeforePostProcess);
}
- if(m_AfterPostProcess.HasPostProcessRenderers && m_AfterPostProcess.PrepareRenderers(ref renderingData)){
- // If this camera resolve to the final target, then both the source & destination will be "_AfterPostProcessTexture" (Note: a final blit/post pass is added by the renderer).
- var source = renderingData.cameraData.resolveFinalTarget ? m_AfterPostProcessColor.Identifier() : renderer.cameraColorTarget;
- m_AfterPostProcess.Setup(source, source);
+ if (m_AfterPostProcess.HasPostProcessRenderers && m_AfterPostProcess.PrepareRenderers(ref renderingData))
+ {
renderer.EnqueuePass(m_AfterPostProcess);
}
}
@@ -83,36 +86,39 @@ public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingD
///
public override void Create()
{
- // This is copied from the forward renderer
- m_AfterPostProcessColor.Init("_AfterPostProcessTexture");
// Create the three render passes and send the custom post-processing renderer classes to each
Dictionary shared = new Dictionary();
m_AfterOpaqueAndSky = new CustomPostProcessRenderPass(CustomPostProcessInjectionPoint.AfterOpaqueAndSky, InstantiateRenderers(settings.renderersAfterOpaqueAndSky, shared));
m_BeforePostProcess = new CustomPostProcessRenderPass(CustomPostProcessInjectionPoint.BeforePostProcess, InstantiateRenderers(settings.renderersBeforePostProcess, shared));
m_AfterPostProcess = new CustomPostProcessRenderPass(CustomPostProcessInjectionPoint.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){
+ private List InstantiateRenderers(List names, Dictionary shared)
+ {
var renderers = new List(names.Count);
- foreach(var name in names){
- if(shared.TryGetValue(name, out var renderer)){
+ foreach (var name in names)
+ {
+ if (shared.TryGetValue(name, out var renderer))
+ {
renderers.Add(renderer);
- } else {
+ }
+ else
+ {
var type = Type.GetType(name);
- if(type == null || !type.IsSubclassOf(typeof(CustomPostProcessRenderer))) continue;
+ if (type == null || !type.IsSubclassOf(typeof(CustomPostProcessRenderer))) continue;
var attribute = CustomPostProcessAttribute.GetAttribute(type);
- if(attribute == null) continue;
+ if (attribute == null) continue;
renderer = Activator.CreateInstance(type) as CustomPostProcessRenderer;
renderers.Add(renderer);
-
- if(attribute.ShareInstance)
+
+ if (attribute.ShareInstance)
shared.Add(name, renderer);
}
}
@@ -148,28 +154,13 @@ public class CustomPostProcessRenderPass : ScriptableRenderPass
///
/// Array of 2 intermediate render targets used to hold intermediate results.
///
- private RenderTargetHandle[] m_Intermediate;
-
- ///
- /// Indentifies whether the intermediate render targets are allocated or not.
- ///
- private bool[] m_IntermediateAllocated;
+ private RTHandle[] m_Intermediate;
///
/// The texture descriptor for the intermediate render targets.
///
private RenderTextureDescriptor m_IntermediateDesc;
- ///
- /// The source of the color data for the render pass
- ///
- private RenderTargetIdentifier m_Source;
-
- ///
- /// The destination of the color data for the render pass
- ///
- private RenderTargetIdentifier m_Destination;
-
///
/// A list of profiling samplers, one for each post process renderer
///
@@ -180,16 +171,20 @@ public class CustomPostProcessRenderPass : ScriptableRenderPass
///
public bool HasPostProcessRenderers => m_PostProcessRenderers.Count != 0;
+ private readonly string[] _IntermediateRTNames = new string[2] { "_IntermediateRT0", "_IntermediateRT1" };
+
///
/// Construct the custom post-processing render pass
///
/// The post processing injection point
/// The list of classes for the renderers to be executed by this render pass
- public CustomPostProcessRenderPass(CustomPostProcessInjectionPoint injectionPoint, List renderers){
+ public CustomPostProcessRenderPass(CustomPostProcessInjectionPoint injectionPoint, List renderers)
+ {
this.injectionPoint = injectionPoint;
this.m_ProfilingSamplers = new List(renderers.Count);
this.m_PostProcessRenderers = renderers;
- foreach(var renderer in renderers){
+ foreach (var renderer in renderers)
+ {
// Get renderer name and add it to the names list
var attribute = CustomPostProcessAttribute.GetAttribute(renderer.GetType());
m_ProfilingSamplers.Add(new ProfilingSampler(attribute?.Name));
@@ -197,65 +192,50 @@ public CustomPostProcessRenderPass(CustomPostProcessInjectionPoint injectionPoin
// Pre-allocate a list for active renderers
this.m_ActivePostProcessRenderers = new List(renderers.Count);
// Set render pass event and name based on the injection point.
- switch(injectionPoint){
- case CustomPostProcessInjectionPoint.AfterOpaqueAndSky:
- renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
+ switch (injectionPoint)
+ {
+ case CustomPostProcessInjectionPoint.AfterOpaqueAndSky:
+ renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
m_PassName = "Custom PostProcess after Opaque & Sky";
break;
- case CustomPostProcessInjectionPoint.BeforePostProcess:
+ case CustomPostProcessInjectionPoint.BeforePostProcess:
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
m_PassName = "Custom PostProcess before PostProcess";
break;
case CustomPostProcessInjectionPoint.AfterPostProcess:
// NOTE: This was initially "AfterRenderingPostProcessing" but it made the builtin post-processing to blit directly to the camera target.
- renderPassEvent = RenderPassEvent.AfterRendering;
+ // NOTE: For URP 12, we are back to using "AfterRenderingPostProcessing"
+ renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
m_PassName = "Custom PostProcess after PostProcess";
break;
}
// Initialize the IDs and allocation state of the intermediate render targets
- m_Intermediate = new RenderTargetHandle[2];
- m_Intermediate[0].Init("_IntermediateRT0");
- m_Intermediate[1].Init("_IntermediateRT1");
- m_IntermediateAllocated = new bool[2];
- m_IntermediateAllocated[0] = false;
- m_IntermediateAllocated[1] = false;
+ m_Intermediate = new RTHandle[2];
}
- ///
- /// Gets the corresponding intermediate RT and allocates it if not already allocated
- ///
- /// The command buffer to use for allocation
- /// The intermediate RT index
- ///
- private RenderTargetIdentifier GetIntermediate(CommandBuffer cmd, int index){
- if(!m_IntermediateAllocated[index]){
- cmd.GetTemporaryRT(m_Intermediate[index].id, m_IntermediateDesc);
- m_IntermediateAllocated[index] = true;
- }
- return m_Intermediate[index].Identifier();
+ public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
+ {
+ base.OnCameraSetup(cmd, ref renderingData);
+ // Copy camera target description for intermediate RTs. Disable multisampling and depth buffer for the intermediate targets.
+ m_IntermediateDesc = renderingData.cameraData.cameraTargetDescriptor;
+ m_IntermediateDesc.msaaSamples = 1;
+ m_IntermediateDesc.depthBufferBits = 0;
+ m_Intermediate[0] = RTHandles.Alloc(m_IntermediateDesc, FilterMode.Point, TextureWrapMode.Clamp, name: _IntermediateRTNames[0]);
+ m_Intermediate[1] = RTHandles.Alloc(m_IntermediateDesc, FilterMode.Point, TextureWrapMode.Clamp, name: _IntermediateRTNames[1]);
}
- ///
- /// Release allocated intermediate RTs
- ///
- /// The command buffer to use for deallocation
- private void CleanupIntermediate(CommandBuffer cmd){
- for(int index = 0; index < 2; ++index){
- if(m_IntermediateAllocated[index]){
- cmd.ReleaseTemporaryRT(m_Intermediate[index].id);
- m_IntermediateAllocated[index] = false;
- }
+ public override void OnCameraCleanup(CommandBuffer cmd)
+ {
+ base.OnCameraCleanup(cmd);
+ for (int i = 0; i < m_Intermediate.Length; i++)
+ {
+ m_Intermediate[i].Release();
}
}
- ///
- /// Setup the source and destination render targets
- ///
- /// Source render target
- /// Destination render target
- public void Setup(RenderTargetIdentifier source, RenderTargetIdentifier destination){
- this.m_Source = source;
- this.m_Destination = destination;
+ public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
+ {
+ base.Configure(cmd, cameraTextureDescriptor);
}
///
@@ -263,7 +243,8 @@ public void Setup(RenderTargetIdentifier source, RenderTargetIdentifier destinat
///
/// Current rendering data
/// True if any renderer will be executed for the given camera. False Otherwise.
- public bool PrepareRenderers(ref RenderingData renderingData){
+ public bool PrepareRenderers(ref RenderingData renderingData)
+ {
// See if current camera is a scene view camera to skip renderers with "visibleInSceneView" = false.
bool isSceneView = renderingData.cameraData.cameraType == CameraType.SceneView;
@@ -272,12 +253,14 @@ public bool PrepareRenderers(ref RenderingData renderingData){
// Collect the active renderers
m_ActivePostProcessRenderers.Clear();
- for(int index = 0; index < m_PostProcessRenderers.Count; index++){
+ for (int index = 0; index < m_PostProcessRenderers.Count; index++)
+ {
var ppRenderer = m_PostProcessRenderers[index];
// Skips current renderer if "visibleInSceneView" = false and the current camera is a scene view camera.
- if(isSceneView && !ppRenderer.visibleInSceneView) continue;
+ if (isSceneView && !ppRenderer.visibleInSceneView) continue;
// Setup the camera for the renderer and if it will render anything, add to active renderers and get its required inputs
- if(ppRenderer.Setup(ref renderingData, injectionPoint)){
+ if (ppRenderer.Setup(ref renderingData, injectionPoint))
+ {
m_ActivePostProcessRenderers.Add(index);
passInput |= ppRenderer.input;
}
@@ -288,7 +271,7 @@ public bool PrepareRenderers(ref RenderingData renderingData){
// return if no renderers are active
return m_ActivePostProcessRenderers.Count != 0;
- }
+ }
///
/// Execute the custom post processing renderers
@@ -297,80 +280,77 @@ public bool PrepareRenderers(ref RenderingData renderingData){
/// Current rendering data
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
- // Copy camera target description for intermediate RTs. Disable multisampling and depth buffer for the intermediate targets.
- m_IntermediateDesc = renderingData.cameraData.cameraTargetDescriptor;
- m_IntermediateDesc.msaaSamples = 1;
- m_IntermediateDesc.depthBufferBits = 0;
-
+ RTHandle target = renderingData.cameraData.renderer.cameraColorTargetHandle;
CommandBuffer cmd = CommandBufferPool.Get(m_PassName);
-
+
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
int width = m_IntermediateDesc.width;
int height = m_IntermediateDesc.height;
- cmd.SetGlobalVector("_ScreenSize", new Vector4(width, height, 1.0f/width, 1.0f/height));
-
+ cmd.SetGlobalVector("_ScreenSize", new Vector4(width, height, 1.0f / width, 1.0f / height));
+
// The variable will be true if the last renderer couldn't blit to destination.
// This happens if there is only 1 renderer and the source is the same as the destination.
bool requireBlitBack = false;
// The current intermediate RT to use as a source.
int intermediateIndex = 0;
- for(int index = 0; index < m_ActivePostProcessRenderers.Count; ++index){
+ for (int index = 0; index < m_ActivePostProcessRenderers.Count; ++index)
+ {
var rendererIndex = m_ActivePostProcessRenderers[index];
var renderer = m_PostProcessRenderers[rendererIndex];
-
- RenderTargetIdentifier source, destination;
- if(index == 0){
+
+ RTHandle source, destination;
+ if (index == 0)
+ {
// If this is the first renderers then the source will be the external source (not intermediate).
- source = m_Source;
- if(m_ActivePostProcessRenderers.Count == 1){
- // There is only one renderer, check if the source is the same as the destination
- if(m_Source == m_Destination){
- // Since we can't bind the same RT as a texture and a render target at the same time, we will blit to an intermediate RT.
- destination = GetIntermediate(cmd, 0);
- // Then we will blit back to the destination.
- requireBlitBack = true;
- } else {
- // Otherwise, we can directly blit from source to destination.
- destination = m_Destination;
- }
- } else {
+ source = target;
+ if (m_ActivePostProcessRenderers.Count == 1)
+ {
+ // Since we can't bind the same RT as a texture and a render target at the same time, we will blit to an intermediate RT.
+ destination = m_Intermediate[0];
+ // Then we will blit back to the destination.
+ requireBlitBack = true;
+ }
+ else
+ {
// If there is more than one renderer, we will need to the intermediate RT anyway.
- destination = GetIntermediate(cmd, intermediateIndex);
+ destination = m_Intermediate[intermediateIndex];
}
- } else {
+ }
+ else
+ {
// If this is not the first renderer, we will want to the read from the intermediate RT.
- source = GetIntermediate(cmd, intermediateIndex);
- if(index == m_ActivePostProcessRenderers.Count - 1){
+ source = m_Intermediate[intermediateIndex];
+ if (index == m_ActivePostProcessRenderers.Count - 1)
+ {
// If this is the last renderer, blit to the destination directly.
- destination = m_Destination;
- } else {
+ destination = target;
+ }
+ else
+ {
// Otherwise, flip the intermediate RT index and set as destination.
// This will act as a ping pong process between the 2 RT where color data keeps moving back and forth while being processed on each pass.
intermediateIndex = 1 - intermediateIndex;
- destination = GetIntermediate(cmd, intermediateIndex);
+ destination = m_Intermediate[intermediateIndex];
}
}
- using(new ProfilingScope(cmd, m_ProfilingSamplers[rendererIndex]))
+ using (new ProfilingScope(cmd, m_ProfilingSamplers[rendererIndex]))
{
// If the renderer was not already initialized, initialize it.
- if(!renderer.Initialized)
+ if (!renderer.Initialized)
renderer.InitializeInternal();
// Execute the renderer.
renderer.Render(cmd, source, destination, ref renderingData, injectionPoint);
}
-
+
}
// If blit back is needed, blit from the intermediate RT to the destination (see above for explanation)
- if(requireBlitBack)
- Blit(cmd, m_Intermediate[0].Identifier(), m_Destination);
-
- // Release allocated Intermediate RTs.
- CleanupIntermediate(cmd);
+ if (requireBlitBack)
+ Blit(cmd, m_Intermediate[0], target);
// Send command buffer for execution, then release it.
context.ExecuteCommandBuffer(cmd);
diff --git a/package.json b/package.json
index 6dcbaec..c44822d 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,11 @@
"version": "2.0.0",
"displayName": "Universal RP Post-Processing Stack",
"description": "A post-processing stack for Universal RP",
- "unity": "2020.2",
+ "unity": "2021.2",
"unityRelease": "0f1",
"dependencies": {
- "com.unity.render-pipelines.universal": "10.2.2",
- "com.unity.render-pipelines.core": "10.2.2"
+ "com.unity.render-pipelines.universal": "12.1.0",
+ "com.unity.render-pipelines.core": "12.1.0"
},
"keywords": [
"graphics",