I’ve been trying to achieve a simple highlight effect in HDRP that outlines and fills objects in two passes, but I’ve been struggling with the implementation. I would appreciate some help with the following questions:
- I need highlighting to be on a per-renderer basis, rather than per-layer (rendering layer masks are fine).
I created a custom pass that allocates a buffer and draws individual renderers into it using the command buffer from the custom pass context, and while that concept works perfectly for what I need, it’s been very buggy and limited. I can’t seem to do any depth testing, backfaces aren’t culled, and there’s an issue where alpha is fully opaque on scene start/load, but fixes itself after re-saving the scene.
protected override void Setup(ScriptableRenderContext RenderContext, CommandBuffer CMD)
{
highlightShaderFullscreen = Shader.Find("Fullscreen/Highlight");
highlightShaderRenderer = Shader.Find("Fullscreen/HighlightRenderer");
fullscreenMaterial = CoreUtils.CreateEngineMaterial(highlightShaderFullscreen);
rendererMaterial = CoreUtils.CreateEngineMaterial(highlightShaderRenderer);
highlightBuffer = RTHandles.Alloc
(
Vector2.one, TextureXR.slices, dimension: TextureXR.dimension,
colorFormat: GraphicsFormat.B8G8R8A8_SRGB,
useDynamicScale: true, name: "Highlight Buffer"
);
highlightDepthBuffer = RTHandles.Alloc
(
Vector2.one, TextureXR.slices, dimension: TextureXR.dimension,
colorFormat: GraphicsFormat.R16_UInt, useDynamicScale: true,
name: "Highlight Depth Buffer", depthBufferBits: DepthBits.Depth16
);
}
protected override void Execute(CustomPassContext CTX)
{
CoreUtils.SetRenderTarget(CTX.cmd, highlightBuffer, highlightDepthBuffer, ClearFlag.All);
highlightRenderers.ForEach(Renderer =>
{
for (int i = 0; i < Renderer.sharedMaterials.Length; i++)
{
CTX.cmd.DrawRenderer(Renderer, rendererMaterial, i);
}
});
CTX.propertyBlock.SetTexture("_HighlightBuffer", highlightBuffer);
CTX.propertyBlock.SetInt("_OutlineIterations", outlineIterations);
CTX.propertyBlock.SetFloat("_OutlineIntensity", outlineIntensity);
CTX.propertyBlock.SetFloat("_OutlineWidth", outlineWidth);
CoreUtils.DrawFullScreen(CTX.cmd, fullscreenMaterial, CTX.cameraColorBuffer, CTX.cameraDepthBuffer, shaderPassId: 0,
properties: CTX.propertyBlock);
}
All the traditional methods for drawing renderers in a custom pass require a special object layer, which doesn’t work for my case. I’ve read that you can use ScriptableRenderContext.DrawRenderers to filter with a rendering layer mask, but I couldn’t understand how to properly invoke it.
Is it possible to achieve a per-renderer custom pass?
- As a bonus and slightly less important question, is it possible to create depth-based outlines, so that objects in front have their outline rendered properly instead of merging with the other? (like the example on the right):
I imagine that’d require something along the lines of applying fullscreen effect and writing depth on each renderer individually, depth testing and combining results into the buffer, but I have no idea where to even begin with this concept or if it’s even possible. Some directions would be great
Thank you very much for your time!
Using:
- Unity 2022.3.48f1
- HDRP 14.0.11