EDIT: AlrightAlright, I sort of lost an entire afternoon to this, but I believe I've got it nailed down now.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[CreateAssetMenu]
public class GammaUIFix : ScriptableRendererFeature
{
// We are not allowed to access the camera target from rendererFeature, only inside the pass
// so, in order to tell the pass to use the camera color target as source/target, we can use this enum
public enum RTHandleType { Custom, CameraColor }
[SerializeField] LayerMask layerMask;
[SerializeField] Material toGammaMat;
[SerializeField] Material toLinearMat;
RTHandle intermediateRT;
RTHandle customDepthAndStencil;
public GammaUIFix()
{ }
public override void Create()
{
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType != CameraType.Game)
return;
RenderTextureDescriptor textureDescriptorcolorDescriptor = new(Screen.width, Screen.height, RenderTextureFormat.Default, 0);
RenderingUtils.ReAllocateIfNeeded(ref intermediateRT, colorDescriptor, name: "intermediate");
RenderTextureDescriptor depthDescriptor = new(Screen.width, Screen.height, RenderTextureFormat.DefaultDepth, 032);
RenderingUtils.ReAllocateIfNeeded(ref intermediateRTcustomDepthAndStencil, textureDescriptordepthDescriptor, name: "customDepth");
BlitPass toGamma = new(new BlitPass.Settings()
{
renderPassEvent = RenderPassEvent.AfterRenderingTransparents,
material = toGammaMat,
sourceType = RTHandleType.CameraColor,
targetType = RTHandleType.Custom,
target = intermediateRT
});
DrawRenderersPass drawUI = new(new DrawRenderersPass.Settings()
{
renderPassEvent = RenderPassEvent.AfterRenderingTransparents,
layerMask = layerMask,
target = intermediateRT,
depthAndStencil = customDepthAndStencil
});
BlitPass toLinear = new(new BlitPass.Settings()
{
renderPassEvent = RenderPassEvent.AfterRenderingTransparents,
material = toLinearMat,
sourceType = RTHandleType.Custom,
source = intermediateRT,
targetType = RTHandleType.CameraColor
});
renderer.EnqueuePass(toGamma);
renderer.EnqueuePass(drawUI);
renderer.EnqueuePass(toLinear);
}
protected override void Dispose(bool disposing)
{
if (intermediateRT != null && disposing?.Release();
intermediateRTcustomDepthAndStencil?.Release();
}
//Draws all renderers of a specific layer into target RTHandle, even if that layer is culled by the camera
class DrawRenderersPass : ScriptableRenderPass
{
Settings settings;
List<ShaderTagId> forwardOnlyShaderTagIds = new()
{
new ShaderTagId("SRPDefaultUnlit"),
new ShaderTagId("UniversalForward"),
new ShaderTagId("UniversalForwardOnly"), // Legacy shaders (do not have a gbuffer pass) are considered forward-only for backward compatibility
};
public DrawRenderersPass(Settings _settings)
{
this.settings = _settings;
renderPassEvent = _settings.renderPassEvent;
}
public override void ConfigureOnCameraSetup(CommandBuffer cmd, RenderTextureDescriptorref cameraTextureDescriptorRenderingData renderingData)
{
base.Configure(cmd, cameraTextureDescriptor);
ConfigureTarget(settings.target, settings.depthAndStencil);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get("Draw Renderers Pass");
DrawingSettings drawingSettings = CreateDrawingSettings(forwardOnlyShaderTagIds, ref renderingData, SortingCriteria.CommonTransparent);
FilteringSettings filteringSettings = new(RenderQueueRange.all, settings.layerMask);
RenderStateBlock renderStateBlock = new(RenderStateMask.Nothing);
renderingData.cameraData.camera.TryGetCullingParameters(out ScriptableCullingParameters cullingParameters);
cullingParameters.cullingMask = (uint)(int)settings.layerMask;
context.DrawRenderers(
context.Cull(ref cullingParameters),
ref drawingSettings,
ref filteringSettings,
ref renderStateBlock);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
base.FrameCleanup(cmd);
}
[System.Serializable]
public class Settings
{
public RenderPassEvent renderPassEvent;
public RTHandle target;
public RTHandle depthAndStencil;
public LayerMask layerMask;
}
}
//blits from source to target using the provided material
class BlitPass : ScriptableRenderPass
{
Settings settings;
public BlitPass(Settings _settings)
{
this.settings = _settings;
renderPassEvent = _settings.renderPassEvent;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get("Blit Pass");
RTHandle source;
if (settings.sourceType == RTHandleType.CameraColor)
source = renderingData.cameraData.renderer.cameraColorTargetHandle;
else
source = settings.source;
RTHandle target;
if (settings.targetType == RTHandleType.CameraColor)
target = renderingData.cameraData.renderer.cameraColorTargetHandle;
else
target = settings.target;
//This seems to happen when selecting certain objects in the inspector? I have no idea, man
if (source.rt == null || target.rt == null)
return;
if (source == target)
{
Debug.LogError("Cannot blit from source to itself! Use an intermediate target");
return;
}
Blitter.BlitCameraTexture(cmd, source, target, settings.material, 0);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
base.FrameCleanup(cmd);
}
[System.Serializable]
public class Settings
{
public RenderPassEvent renderPassEvent;
public Material material;
public RTHandleType sourceType;
public RTHandle source;
public RTHandleType targetType;
public RTHandle target;
}
}
//-------------------------------------------------------------------------
}
Although I should mention at one point I got a mysterious persistent error aboutAlthough I should mention at one point I got a mysterious persistent error about the RTHandle being null that then disappeared upon restarting the editor. I don't know what that was about, and cannot guarantee it won't happen again :(
EDIT (again): Fixed the pesky RTHandle being null that then disappeared upon restartingerrors and got masks to work by adding a custom depth/stencil buffer to the editorDrawRenderers pass. I don't know what that was about, and cannot guarantee it won't happen again :(

