Search Unity

Custom Render Pass & Custom buffers

Discussion in 'High Definition Render Pipeline' started by Ziboo, Oct 10, 2019.

  1. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Hi,

    I'm using the new Custom Pass feature in HDRP.
    https://github.com/Unity-Technologies/ScriptableRenderPipeline/pull/4317

    My goal is to render the all image in Black and White except for certain objects.

    I created a FullScreenCustomPass with a shader that output the grayscale version of the image.

    Now, I would need to not do that on the objects I tag.

    If I can avoid using Layers to do it, that would be great.

    I saw that there is a "SampleCustomColor(float2 uv)" in the shader template for FullScreenPass.

    It seems that I could write to a custom buffer, which would be perfect for my case.

    I could write red on every objects I want to isolate from the grayscale.

    But I didn't find anything on how, where, to write to the custom buffer.

    Can someone have an insight on that ?

    Or maybe a better solution ?

    Thank you
     
    Last edited: Oct 10, 2019
    Kaldrin likes this.
  2. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    265
    Hello,

    Concerning the HDRP custom pass feature, there are some example of how to implement some effects here: https://github.com/alelievr/HDRP-Custom-Passes, The closest effect to what you want to acheve is the Slight Blur which have a mask faeture to not blur some objects.

    You can use the DrawRenderers custom pass to render only objects you want to render in color and store them in the custom buffer allocated by HDRP, once you have that, you can sample the buffer into the fullscreen pass using SampleCustomColor. The only problem is that you can't filter objects by anything else than the layer.

    If you don't mind writing some C#, you could create a new custom pass type which calls direcrtly the DrawRenderers function (There is an exmaple in the Slight Blur effect I pointed), then you will have access to all the filter available in the API. If you don't want to use layers, you still have the rendering layer mask that you can setup on the Renderer.

    One more thing, it may be a bit early but here is the V1 of the doc for custom passes i've been working on: https://github.com/Unity-Technologi...high-definition/Documentation~/Custom-Pass.md.
     
    eizenhorn likes this.
  3. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    356
    Thanks Antoine !
    I'll check that ASAP.
     
  4. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    883
    @antoinel_unity I am trying to copy the result of DrawRenderersCustomPass to the render texture, to use it in the UI. I am trying the following code:
    Code (CSharp):
    1. public class RenderToTexturePass : DrawRenderersCustomPass {
    2.         public RenderTexture renderTexture;
    3.  
    4.         protected override void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult) {
    5.             base.Execute(renderContext, cmd, hdCamera, cullingResult);
    6.  
    7.             GetCustomBuffers(out RTHandle color, out _);
    8.  
    9.             cmd.CopyTexture(color, renderTexture);
    10.         }
    11.     }
    The render texture and the color buffer formats (r16g16b16a16_SFloat) and width/height are the same . But I am getting the following error in the console:
    Code (CSharp):
    1. Graphics.CopyTexture called with mismatching texture types (src=5 dst=2)
    2.  
    What am I missing?
     
  5. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    265
    In HDRP, when you allocate a render target, you must use the RTHandle system that automatically handles screen resizing, support of VR and reuse between different cameras.
    Here is an example of an RTHandle allocation:
    Code (CSharp):
    1. RTHandles.Alloc(Vector2.one, TextureXR.slices, dimension: TextureXR.dimension, colorFormat: GraphicsFormat.R16G16B16A16_SFloat, useDynamicScale: true, name: "Buffer Debug Name");
    The error you have is due to the dimension of the texture you're copying, i guess your RenderTexture is a Tex2D but the color buffer of the camera which is a RTHandle is a Tex2DArray (for VR support). Also since RTHandles supports multiple camera of different size, when you do a copy, it may result in unused memory being copied, to avoid that you can do a copy of only the size of the current camera (Note tat you have access to the RThandle size in the property struct).
     
  6. Elynwir

    Elynwir

    Joined:
    Nov 3, 2019
    Posts:
    1
    @antoinel_unity I'm trying to understand the custom pass API, basing my work off the examples you have provided. In my case, I need an outline on a single object, not a whole layer, and I can't put it in a separate layer (because I'm using it for physics logic). You mentioned using the rendering layer mask but I can't figure out how to actually use this. I tried replacing this block of code
    Code (CSharp):
    1. void DrawOutlineMeshes(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult) {
    2.     var result = new RendererListDesc(shaderTags, cullingResult, hdCamera.camera) {
    3.         // We need the lighting render configuration to support rendering lit objects
    4.         rendererConfiguration = PerObjectData.LightProbe | PerObjectData.LightProbeProxyVolume | PerObjectData.Lightmaps,
    5.         renderQueueRange = RenderQueueRange.all,
    6.         sortingCriteria = SortingCriteria.BackToFront,
    7.         excludeObjectMotionVectors = false,
    8.         layerMask = outlineLayer,
    9.     };
    10.     CoreUtils.SetRenderTarget(cmd, outlineBuffer, ClearFlag.Color);
    11.     HDUtils.DrawRendererList(renderContext, cmd, RendererList.Create(result));
    12. }
    with the following:
    Code (CSharp):
    1. void DrawOutlineMeshes(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult) {
    2.     DrawingSettings drawingSettings = new DrawingSettings() {
    3.         perObjectData=(PerObjectData.LightProbe | PerObjectData.LightProbeProxyVolume | PerObjectData.Lightmaps),
    4.         sortingSettings=new SortingSettings(hdCamera.camera),
    5.     };
    6.     FilteringSettings filteringSettings = FilteringSettings.defaultValue;
    7.     CoreUtils.SetRenderTarget(cmd, outlineBuffer, ClearFlag.Color);
    8.     renderContext.ExecuteCommandBuffer(cmd);
    9.     cmd.Clear();
    10.     renderContext.DrawRenderers(cullingResult, ref drawingSettings, ref filteringSettings);
    11. }
    and tried all kinds of variations of the filter/drawing settings, but I couldn't get the outlines to actually show up. I'm not sure if my renderingLayerMask was wrong, since I couldn't expose this as an enum field in the editor - I ended up opening the debug mode inspector, copying the uint value of the mask of a renderer, and pasting it into my code, to no avail.

    My question is: what am I doing wrong here? Or alternatively, is there a way to specify a list of renderers explicitly?
     
  7. punk

    punk

    Joined:
    Jun 28, 2013
    Posts:
    408
    @Elynwir and anyone else that comes here I just figured this out

    Code (CSharp):
    1.         void DrawOutlineMeshes(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult)
    2.         {
    3.             var result = new RendererListDesc(shaderTags, cullingResult, hdCamera.camera)
    4.             {
    5.                 // We need the lighting render configuration to support rendering lit objects
    6.                 rendererConfiguration = PerObjectData.LightProbe | PerObjectData.LightProbeProxyVolume | PerObjectData.Lightmaps,
    7.                 renderQueueRange = RenderQueueRange.all,
    8.                 sortingCriteria = SortingCriteria.BackToFront,
    9.                 excludeObjectMotionVectors = false
    10.             };
    11.  
    12.             CoreUtils.SetRenderTarget(cmd, outlineBuffer, ClearFlag.Color);
    13.  
    14.             RendererList renderList = RendererList.Create(result);
    15.             renderList.filteringSettings.renderingLayerMask = 1 << 7;
    16.  
    17.             HDUtils.DrawRendererList(renderContext, cmd, renderList);
    18.         }
     
    Hubster likes this.
  8. Dorodo

    Dorodo

    Joined:
    Mar 8, 2015
    Posts:
    44
    Hi @antoinel_unity, sorry for the necro bump on this thread. I've been trying to achieve the same results as the one above in Unity 2021.3, where the RendererList and DrawRendererList have gone through some refactors, so we can use Rendering Layers to filter out objects when applying a custom pass effect.

    I'm attaching below a sample script which has 3 different tests:

    1- CoreUtils.DrawRendererList with a UnityEngine.Rendering.RendererUtils RendererList.
    • In 2022.2 and above I believe this method works, since Rendering Layers are now exposed into RendererListDesc and you also have RendererListParams as another option. Unfortunately, 2021.3 does not have this functionality yet, so we can only filter out elements by regular Layers.
    2- ScriptableRenderContext.DrawRenderers
    • As I believe you've mentioned in this thread (and this other thread here), this seems to be the ideal approach where you have more control over how you're drawing and filtering objects. Unfortunately, the test below does not render anything, and I couldn't find a sample online that could get some insights on what I'm missing.
    3- CoreUtils.DrawRendererList with a deprecated UnityEngine.Experimental.Rendering RendererList.
    • This method is the only one I've managed to get semi-working, and is based on the sample above by punk. The deprecated RendererList contains a FilteringSettings field which you can assign rendering layers and seems to be working fine in the Scene View. Unfortunately, the effect does not show in the Game View.

    Is there anything in the second or third approach that we're doing wrong and we could fix to make the effect work in game? Any insights on this subject would help a ton :).

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Rendering;
    4. using UnityEngine.Rendering.HighDefinition;
    5. using UnityEngine.Rendering.RendererUtils; // uncomment this if you're using Test 1 or 2
    6. //using UnityEngine.Experimental.Rendering; // uncomment this if you're using Test 3
    7.  
    8. public class RenderingLayerExampleCustomPass : CustomPass
    9. {
    10.     // A duplicate of the rendering layer mask enum.
    11.     // I couldn't find a way to access it, so I created a copy. Sorry.
    12.     [Flags]
    13.     public enum RenderingLayerMaskCopy
    14.     {
    15.         None = 0,
    16.         Everything = -1,
    17.         LightLayerDefault = 1 << 0,
    18.         LightLayer1 = 1 << 1,
    19.         LightLayer2 = 1 << 2,
    20.         LightLayer3 = 1 << 3,
    21.         LightLayer4 = 1 << 4,
    22.         LightLayer5 = 1 << 5,
    23.         LightLayer6 = 1 << 6,
    24.         LightLayer7 = 1 << 7,
    25.         DecalLayerDefault = 1 << 8,
    26.         DecalLayer1 = 1 << 9,
    27.         DecalLayer2 = 1 << 10,
    28.         DecalLayer3 = 1 << 11,
    29.         DecalLayer4 = 1 << 12,
    30.         DecalLayer5 = 1 << 13,
    31.         DecalLayer6 = 1 << 14,
    32.         DecalLayer7 = 1 << 15,
    33.         RenderingLayer16 = 1 << 16,
    34.         RenderingLayer17 = 1 << 17,
    35.         RenderingLayer18 = 1 << 18,
    36.         RenderingLayer19 = 1 << 19,
    37.         RenderingLayer20 = 1 << 20,
    38.         RenderingLayer21 = 1 << 21,
    39.         RenderingLayer22 = 1 << 22,
    40.         RenderingLayer23 = 1 << 23,
    41.         RenderingLayer24 = 1 << 24,
    42.         RenderingLayer25 = 1 << 25,
    43.         RenderingLayer26 = 1 << 26,
    44.         RenderingLayer27 = 1 << 27,
    45.         RenderingLayer28 = 1 << 28,
    46.         RenderingLayer29 = 1 << 29,
    47.         RenderingLayer30 = 1 << 30,
    48.         RenderingLayer31 = 1 << 31,
    49.     }
    50.  
    51.     public bool DrawInSceneView = true;
    52.     public LayerMask LayerMaskFilter;
    53.     public RenderingLayerMaskCopy RenderingLayerMaskFilter;
    54.  
    55.     [SerializeField]
    56.     private Material _replacementMaterial;
    57.  
    58.     private ShaderTagId[] _shaderTags;
    59.     private RendererList _rendererList;
    60.  
    61.     protected override bool executeInSceneView
    62.     {
    63.         get { return DrawInSceneView; }
    64.     }
    65.  
    66.     protected override void Setup (ScriptableRenderContext renderContext, CommandBuffer cmd)
    67.     {
    68.         // Warning: You can use all tags in Test 1 and 3, but Test 2 only allows 1 ShaderTag per DrawRenderers command.
    69.         _shaderTags = new ShaderTagId[5]
    70.         {
    71.             new ShaderTagId("Forward"),
    72.             new ShaderTagId("ForwardOnly"),     // Unlit Shaders
    73.             new ShaderTagId("GBuffer"),         // Lit Shaders
    74.             new ShaderTagId("SRPDefaultUnlit"),
    75.             new ShaderTagId("FirstPass")
    76.         };
    77.     }
    78.  
    79.     protected override void Execute (CustomPassContext ctx)
    80.     {
    81.         // TEST 1: Regular DrawRendererList from Core Utils.
    82.         // This is successful in creating a custom pass, but there's no place in the current pipeline that allows you to set a Rendering Layer mask in Unity 2021. this is fixed in Unity 2022 though.
    83.         //
    84.         // ----------------------------------------------------------------------------------------------------
    85.         //
    86.         // var result = new RendererListDesc(_shaderTags, ctx.cullingResults, ctx.hdCamera.camera)
    87.         // {
    88.         //     rendererConfiguration = PerObjectData.None,
    89.         //     sortingCriteria = SortingCriteria.BackToFront,
    90.         //     overrideMaterial = _replacementMaterial,
    91.         //     overrideMaterialPassIndex = 0,
    92.         //     renderQueueRange = RenderQueueRange.all,
    93.         //     excludeObjectMotionVectors = false,
    94.         //     layerMask = LayerMaskFilter,
    95.         //     stateBlock = new RenderStateBlock(RenderStateMask.Depth){ depthState = new DepthState(true,  CompareFunction.LessEqual)},
    96.         // };
    97.         //
    98.         // _rendererList = ctx.renderContext.CreateRendererList(result);
    99.         // CoreUtils.DrawRendererList(ctx.renderContext,ctx.cmd, _rendererList);
    100.  
    101.  
    102.  
    103.         // TEST 2: Lower level call using ScriptableRenderContext.DrawRenderers.
    104.         // This is actually the recommended suggestion by a Unity Engineer in these threads:
    105.         // https://forum.unity.com/threads/custom-render-pass-custom-buffers.758558/
    106.         // https://forum.unity.com/threads/whats-the-proper-way-to-draw-renderers-to-the-custom-rt-in-custompass.860383/
    107.         //
    108.         // This requires you to manually setup the drawing settings, filtering settings and render state.
    109.         // The Filtering settings is the most important part of this call, because you can set a Rendering Layer mask in it.
    110.         //
    111.         // ----------------------------------------------------------------------------------------------------
    112.  
    113.         FilteringSettings filteringSettings = FilteringSettings.defaultValue;
    114.         var drawSettings = new DrawingSettings(_shaderTags[1], new SortingSettings(ctx.hdCamera.camera));
    115.  
    116.         drawSettings.perObjectData = PerObjectData.None;
    117.         drawSettings.overrideMaterial = _replacementMaterial;
    118.         drawSettings.overrideMaterialPassIndex = 0;
    119.         filteringSettings.renderQueueRange = RenderQueueRange.all;
    120.         filteringSettings.excludeMotionVectorObjects = false;
    121.         filteringSettings.layerMask = LayerMaskFilter;
    122.         filteringSettings.renderingLayerMask = (uint)RenderingLayerMaskFilter;
    123.         RenderStateBlock stateBlock = new RenderStateBlock(RenderStateMask.Depth)
    124.         {
    125.             depthState = new DepthState(true, CompareFunction.LessEqual)
    126.         };
    127.         ctx.renderContext.DrawRenderers(ctx.cullingResults, ref drawSettings, ref filteringSettings, ref stateBlock);
    128.  
    129.  
    130.         // TEST 3: Using a deprecated Renderer List struct that contains a Filtering Settings field.
    131.         // Warning: Uncomment UnityEngine.Experimental.Rendering and comment out UnityEngine.Rendering.RendererUtils as they have conflicting definitions.
    132.         //
    133.         // In recent versions, Unity has moved the RendererList struct away from the RenderPipeline package, and into the internal UnityEngine.Rendering namespace.
    134.         // The previous version is still semi-functional in which you can actually get it to work in scene view, but for some reason I can't get it to show in game view.
    135.         //
    136.         // ----------------------------------------------------------------------------------------------------
    137.         //
    138.         // var result = new RendererListDesc(_shaderTags, ctx.cullingResults, ctx.hdCamera.camera)
    139.         // {
    140.         //      rendererConfiguration = PerObjectData.None,
    141.         //      sortingCriteria = SortingCriteria.BackToFront,
    142.         //      overrideMaterial = _replacementMaterial,
    143.         //      overrideMaterialPassIndex = 0,
    144.         //      renderQueueRange = RenderQueueRange.all,
    145.         //      excludeObjectMotionVectors = false,
    146.         //      stateBlock = new RenderStateBlock(RenderStateMask.Depth){ depthState = new DepthState(true,  CompareFunction.LessEqual)},
    147.         // };
    148.         //
    149.         // _rendererList = RendererList.Create(result);
    150.         //
    151.         // FilteringSettings filteringSettings = FilteringSettings.defaultValue;
    152.         //
    153.         // filteringSettings.renderQueueRange = RenderQueueRange.all;
    154.         // filteringSettings.excludeMotionVectorObjects = false;
    155.         // filteringSettings.renderingLayerMask = (uint)RenderingLayerMaskFilter;
    156.         // filteringSettings.layerMask = LayerMaskFilter;
    157.         // RenderStateBlock stateBlock = new RenderStateBlock(RenderStateMask.Depth)
    158.         // {
    159.         //     depthState = new DepthState(true, CompareFunction.LessEqual)
    160.         // };
    161.         //
    162.         // _rendererList.filteringSettings = filteringSettings;
    163.         // _rendererList.stateBlock = stateBlock;
    164.         //
    165.         // CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, _rendererList);
    166.     }
    167. }
    168.  
     
    Last edited: Mar 4, 2024
  9. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    265
    Hello @Dorodo,

    In your script, you're using the renderContext.DrawRenderers() function to draw the objects, this method is immediate rather than using the command buffer of the current rendering. This means that your drawRenderers call is actually executed before the rendering of the whole frame (command buffer execution is delayed until it's added to the scriptable render context).

    To fix this, you can force the command buffer to flush before doing your DrawRenderers call like so:

    Code (CSharp):
    1. // Append the command list to the context
    2. ctx.renderContext.ExecuteCommandBuffer(ctx.cmd);
    3. // Clear the command buffer to avoid command duplication when it's added in the context later on
    4. ctx.cmd.Clear();
    5.  
    6. // Here you can use the render context to draw your renderers.
    7.