The artstyle of my project requires a pixelization effect on a selected group of objects (pickups, powerups, UI elements).
Here is a simillar effect from the game Prodeus
Originally I have tried creating a bunch of cameras that would render selected layers onto a low res render texture and then render all of them with the main camera. There were two problems with this approach:
- Each camera cuts a significant chunk of performance
- Complicated setup
Custom render pass seems like a perfect solution (I don’t know about the performance, but at least the setup will 100% be simpler), but all tutorials/stack overflow questions seem to be using some older version of RenderGraph API
This is my code so far:
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule;
public class PixelizationRenderFeature : ScriptableRendererFeature
{
[System.Serializable]
public class CustomRenderPassSettings
{
public LayerMask layerMask;
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
public Material material;
[Range(1f, 15f)]
public float pixelDensity = 1f;
}
public CustomRenderPassSettings settings = new CustomRenderPassSettings();
class CustomRenderPass : ScriptableRenderPass
{
public Material material;
public float pixelDensity = 1f;
public CustomRenderPass(CustomRenderPassSettings settings)
{
profilingSampler = new ProfilingSampler("Pixelization Pass");
renderPassEvent = settings.renderPassEvent;
material = settings.material;
pixelDensity = settings.pixelDensity;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (material == null)
{
Debug.LogWarningFormat("Missing Blit Material. CustomRenderPass render pass will not execute.");
return;
}
const string passName = "Pixelization Pass";
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
TextureHandle srcColor = resourceData.activeColorTexture;
var descriptor = cameraData.cameraTargetDescriptor;
descriptor.width = Mathf.Max(1, (int)(cameraData.camera.pixelWidth / pixelDensity));
descriptor.height = Mathf.Max(1, (int)(cameraData.camera.pixelHeight / pixelDensity));
descriptor.depthBufferBits = 0;
descriptor.msaaSamples = 1;
TextureHandle pixelatedRT = UniversalRenderer.CreateRenderGraphTexture(renderGraph, descriptor, "PixelatedRT", false);
UnityEngine.Rendering.RenderGraphModule.Util.RenderGraphUtils.AddBlitPass(
renderGraph,
new UnityEngine.Rendering.RenderGraphModule.Util.RenderGraphUtils.BlitMaterialParameters(srcColor, pixelatedRT, material, 0),
passName
);
UnityEngine.Rendering.RenderGraphModule.Util.RenderGraphUtils.AddBlitPass(
renderGraph,
new UnityEngine.Rendering.RenderGraphModule.Util.RenderGraphUtils.BlitMaterialParameters(pixelatedRT, srcColor, Blitter.GetBlitMaterial(descriptor.dimension), 0),
"Final Blit to Screen"
);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new(settings);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(m_ScriptablePass);
}
}
AS far as I understand, it takes the rendered color texture from the camera, copies it onto a lower resolution texture and then copies it back onto the main frame. Feel free to correct me if I’m wrong.
What I don’t understand is how instead of taking color from the camera I can render all objects on a specified layer mask instead and then use that result for downscaling
