Unity 2022.1.14f1
Universal Rendering Pipeline 13.1.8
In my experiments and testing to try and introduce myself to ScriptableRendererFeatures (and, by extension, ScriptableRenderPasses), I’ve failed to find a single, up-to-date example online to use as a starting point and am clearly on the wrong track to some extent, since the first successful modification to my current render consumed more and more memory in RenderTextures until my third out-of-VRAM-induced computer restart.
So far, my best first attempt combined the primary concepts from https://alexanderameye.github.io/notes/scriptable-render-passes/ with the URP’s “Upgrade Guide” offering bare minimum suggestions for updating to RTHandle use. Research has also spanned more than a dozen other sources (most of which were lost to the aforementioned computer restarts, but all of which involved earlier versions of URP, prior to non-RTHandles being deprecated), but those two have actually resulted in something that’s offered a visual result.
With all of that in mind, this is the current state of my script(s) (with most non-deprecation-related comments removed):
–TemplateFeature.cs–
using UnityEngine;
using UnityEngine.Rendering.Universal;
// https://alexanderameye.github.io/notes/scriptable-render-passes/
public class TemplateFeature : ScriptableRendererFeature
{
[System.Serializable]
public class PassSettings
{
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
[Range(1, 4)]
public int downsample = 1;
[Range(0, 20)]
public int blurStrength = 5;
}
TemplatePass pass;
public PassSettings passSettings = new PassSettings();
public override void Create()
{
pass = new TemplatePass(passSettings);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(pass);
}
}
–TemplatePass.cs–
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// https://alexanderameye.github.io/notes/scriptable-render-passes/
public class TemplatePass : ScriptableRenderPass
{
const string profilerTag = "Template Pass";
TemplateFeature.PassSettings passSettings;
//^RenderTargetIdentifier colorBuffer;
//^RenderTargetIdentifier temporaryBuffer;
// ^Updating deprecated RenderTargetIdentifier usage to RTHandle-based
RTHandle colorBuffer;
RTHandle temporaryBuffer;
//^int temporaryBufferID = Shader.PropertyToID("_TemporaryBuffer");
Material mat;
static readonly int blurStrengthProperty = Shader.PropertyToID("_BlurStrength");
public TemplatePass(TemplateFeature.PassSettings passSettings)
{
this.passSettings = passSettings;
renderPassEvent = passSettings.renderPassEvent;
if(mat == null)
{
mat = CoreUtils.CreateEngineMaterial("Hidden/TemplateBlur");
}
mat.SetInt(blurStrengthProperty, passSettings.blurStrength);
}
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
// Downsample the original camera target descriptor
descriptor.width /= passSettings.downsample;
descriptor.height /= passSettings.downsample;
descriptor.depthBufferBits = 0; // Color and depth cannot be combined in RTHandles
// Grab the color buffer from the renderer camera color target
//^colorBuffer = renderingData.cameraData.renderer.cameraColorTarget;
colorBuffer = renderingData.cameraData.renderer.cameraColorTargetHandle;
//^cmd.GetTemporaryRT(temporaryBufferID, descriptor, FilterMode.Bilinear);
//^temporaryBuffer = new RenderTargetIdentifier(temporaryBufferID);
// This included variations on descriptor definitions and scaling definitions
RenderingUtils.ReAllocateIfNeeded(ref temporaryBuffer, Vector2.one / passSettings.downsample, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_TemporaryBuffer");
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
// Currently there's an issue which results in mismatched markers.
CommandBuffer cmd = CommandBufferPool.Get();
using(new ProfilingScope(cmd, new ProfilingSampler(profilerTag)))
{
Blit(cmd, colorBuffer, temporaryBuffer, mat, 0); // shader pass 0
Blit(cmd, temporaryBuffer, colorBuffer, mat, 1); // shader pass 1
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
if(cmd == null)
{
throw new System.ArgumentNullException("cmd");
}
//^cmd.ReleaseTemporaryRT(temporaryBufferID);
temporaryBuffer = null;
//colorBuffer = null; // I don't know whether it's necessary, but either way doesn't help
}
}
… And the shader, although it’s not specifically the cause of any problems here
–TemplateBlur.shader–
Shader "Hidden/TemplateBlur"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
float4 _MainTex_TexelSize;
float4 _MainTex_ST;
int _BlurStrength;
Varyings Vert(Attributes input)
{
Varyings output;
output.positionHCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = TRANSFORM_TEX(input.uv, _MainTex);
return output;
}
half4 FragHorizontal(Varyings input) : SV_TARGET
{
float2 res = _MainTex_TexelSize.xy;
half4 sum = 0;
int samples = 2 * _BlurStrength + 1;
for(float x = 0; x < samples; x++)
{
float2 offset = float2(x - _BlurStrength, 0);
sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv + offset * res);
}
return sum / samples;
}
half4 FragVertical(Varyings input) : SV_TARGET
{
float2 res = _MainTex_TexelSize.xy;
half4 sum = 0;
int samples = 2 * _BlurStrength + 1;
for(float y = 0; y < samples; y++)
{
float2 offset = float2(0, y - _BlurStrength);
sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv + offset * res);
}
return sum / samples;
}
ENDHLSL
SubShader
{
Tags
{
"RenderType"="Opaque"
"RenderPipeline"="UniversalPipeline"
}
Pass // 0
{
Name "Horizontal Box Blur"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment FragHorizontal
ENDHLSL
}
Pass // 1
{
Name "Vertical Box Blur"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment FragVertical
ENDHLSL
}
}
}
To say this again now, DON’T USE THIS AS-IS. You will regret it quickly.
Anyway, with all of this in mind, what exactly have I overlooked at this point? I have been completely unable to find a source for a functioning example of a ScriptableRendererFeature (as of changes made over the course of URP 13 for the current non-beta Unity Editor build), and without prior experience using them, I’m at a loss regarding what I’m missing in terms of assembling this.
For that matter, is there anything else I should also be taking into consideration when working with this to begin with? For example, is ScriptableRenderContext.DrawRenderers() a viable way to draw a specific subset of GameObjects in my scene (i.e. using LayerMask) with a Renderer Feature, rather than only using the current frame (at a given time)?
Edit: Where KeepFrameFeature.cs in the URP “Samples” package contains something like a dozen lines reporting as “deprecated”, Test107Renderer in Unity’s URP Github “Graphics” branch, while not a ScriptableRendererFeature, looks like it might get me on the right track… despite not seeming to strictly adhere to the Upgrade Guide. I can’t say I’m enthusiastic about the risk of crashing my computer again, though, so I’ll hold off on testing it while I focus on other things (unless there are any better suggestions offered here by the time I get to trying that approach - namely, incorporating CommandBuffer.GetTemporaryRT() and CommandBuffer.ReleaseTemporaryRT() when they’re specifically filtered out in the Upgrade Guide itself).