Search Unity

URP How to Add Layer Mask to Custom Render Feature

Discussion in 'Universal Render Pipeline' started by MikeMalaska, Mar 18, 2020.

  1. MikeMalaska

    MikeMalaska

    Joined:
    May 16, 2017
    Posts:
    2
    I have been working with URP on a project, and I have a working custom Render Feature that I want to apply to a layer mask. Seems simple enough to get a layer mask setting to show up on my ScriptableRenderFeature:
    upload_2020-3-18_13-57-31.png
    Question is, where do I start in using that layer mask in my feature?

    Here is the C# from my ScriptableRenderFeature:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public class OutlineFeature : ScriptableRendererFeature
    6. {
    7.     class OutlinePass : ScriptableRenderPass
    8.     {
    9.         private RenderTargetIdentifier source { get; set; }
    10.         private RenderTargetHandle destination { get; set; }
    11.         public Material outlineMaterial = null;
    12.         RenderTargetHandle temporaryColorTexture;
    13.  
    14.         public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination)
    15.         {
    16.             this.source = source;
    17.             this.destination = destination;
    18.         }
    19.  
    20.         public OutlinePass(Material outlineMaterial)
    21.         {
    22.             this.outlineMaterial = outlineMaterial;
    23.         }
    24.  
    25.  
    26.  
    27.         // This method is called before executing the render pass.
    28.         // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
    29.         // When empty this render pass will render to the active camera render target.
    30.         // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
    31.         // The render pipeline will ensure target setup and clearing happens in an performance manner.
    32.         public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    33.         {
    34.            
    35.         }
    36.  
    37.         // Here you can implement the rendering logic.
    38.         // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
    39.         // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
    40.         // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
    41.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    42.         {
    43.             CommandBuffer cmd = CommandBufferPool.Get("_OutlinePass");
    44.  
    45.             RenderTextureDescriptor opaqueDescriptor = renderingData.cameraData.cameraTargetDescriptor;
    46.             opaqueDescriptor.depthBufferBits = 0;
    47.            
    48.  
    49.             if (destination == RenderTargetHandle.CameraTarget)
    50.             {
    51.                 cmd.GetTemporaryRT(temporaryColorTexture.id, opaqueDescriptor, FilterMode.Point);
    52.                 Blit(cmd, source, temporaryColorTexture.Identifier(), outlineMaterial, 0);
    53.                 Blit(cmd, temporaryColorTexture.Identifier(), source);
    54.  
    55.             }
    56.             else Blit(cmd, source, destination.Identifier(), outlineMaterial, 0);
    57.  
    58.             context.ExecuteCommandBuffer(cmd);
    59.             CommandBufferPool.Release(cmd);
    60.         }
    61.  
    62.         /// Cleanup any allocated resources that were created during the execution of this render pass.
    63.         public override void FrameCleanup(CommandBuffer cmd)
    64.         {
    65.  
    66.             if (destination == RenderTargetHandle.CameraTarget)
    67.                 cmd.ReleaseTemporaryRT(temporaryColorTexture.id);
    68.         }
    69.     }
    70.  
    71.     [System.Serializable]
    72.     public class OutlineSettings
    73.     {
    74.         public Material outlineMaterial = null;
    75.         public LayerMask layerMask;
    76.     }
    77.  
    78.     public OutlineSettings settings = new OutlineSettings();
    79.     OutlinePass outlinePass;
    80.     RenderTargetHandle outlineTexture;
    81.  
    82.     public override void Create()
    83.     {
    84.         outlinePass = new OutlinePass(settings.outlineMaterial);
    85.         outlinePass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    86.         outlineTexture.Init("_OutlineTexture");
    87.     }
    88.  
    89.     // Here you can inject one or multiple render passes in the renderer.
    90.     // This method is called when setting up the renderer once per-camera.
    91.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    92.     {
    93.         if (settings.outlineMaterial == null)
    94.         {
    95.             Debug.LogWarningFormat("Missing Outline Material");
    96.             return;
    97.         }
    98.         outlinePass.Setup(renderer.cameraColorTarget, RenderTargetHandle.CameraTarget);
    99.         renderer.EnqueuePass(outlinePass);
    100.     }
    101. }
    102.  
    103.  
    If anyone could help me understand where I can use my LayerMask parameter, or at least point in the right direction. I have looked at documentation, but I can't find the right information to help me work through the problem. Maybe I am simply not familiar with the correct terms to search for.

    Thanks!
     
    jq911 and alexanderameye like this.
  2. MikeMalaska

    MikeMalaska

    Joined:
    May 16, 2017
    Posts:
    2
    Update: I figured out that I can set a layerMask settings in the ScriptableRenderContext like this:

    FilteringSettings filters = new FilteringSettings();
    filters.layerMask = 2; // or my layerMask variable
    context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filters);

    But I don't see any change in my rendered scene. My custom render pass is still applying to all objects.
     
    Last edited: Mar 19, 2020
  3. jq911

    jq911

    Joined:
    Jun 11, 2015
    Posts:
    3
    I have the same question, hope someone can help.
    BTW, I guess it's not an easy one, cause I noticed alexanderameye had been here and like this Thread.:p He has a tutorial of this outline effect, his website is really helpful.
     
  4. dpotuznik

    dpotuznik

    Joined:
    Jan 18, 2018
    Posts:
    17
  5. Elvar_Orn

    Elvar_Orn

    Unity Technologies

    Joined:
    Dec 9, 2019
    Posts:
    162
    Try this,

    Code (CSharp):
    1. int myLayerIndexVariable = 2; // ...or LayerMask.NameToLayer(myLayerName)
    2. int myLayerMaskVariable = 1 << myLayerIndexVariable;
    3. RenderQueueRange myRenderQueueRange = RenderQueueRange.transparent;
    4. FilteringSettings filters = new FilteringSettings(myRenderQueueRange, myLayerMaskVariable);
    5. context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filters);
    The key things here are:
    1. You must convert the layer index to a bit mask hence the 1 << myLayerIndexVariable
    2. Creating the FilteringSettings without parameters doesn't set the renderingLayerMask, but creating like this will.

    Let me know if this works for you.
     
  6. danielbindy

    danielbindy

    Joined:
    Jun 2, 2020
    Posts:
    2
    I tried this on a similar Blit script and it does not work. Can you please provide more details on how to do this
    Elvar_Orn?
     
    shotoutgames likes this.
  7. peterbay

    peterbay

    Unity Technologies

    Joined:
    Nov 2, 2017
    Posts:
    100
    Which part are you having trouble with? :)
     
    shotoutgames likes this.
  8. shotoutgames

    shotoutgames

    Joined:
    Dec 29, 2013
    Posts:
    290
    Where does the snippet get added?
     
  9. peterbay

    peterbay

    Unity Technologies

    Joined:
    Nov 2, 2017
    Posts:
    100
    The code should be used in the Execute method of your ScriptableRenderPass
     
  10. shotoutgames

    shotoutgames

    Joined:
    Dec 29, 2013
    Posts:
    290
    Thanks but I have no idea what I am doing :)
    probably a separate question but I don't understand shadertag (is that something set to whatever)
    Otherwise what do I need to change (besides everything) ?
    Just trying to filter out where the material is only applied to the player layer

    Code (CSharp):
    1. using UnityEngine.Rendering;
    2. using UnityEngine.Rendering.Universal;
    3.  
    4. namespace UnityEngine.Experiemntal.Rendering.Universal
    5. {
    6.     /// <summary>
    7.     /// Copy the given color buffer to the given destination color buffer.
    8.     ///
    9.     /// You can use this pass to copy a color buffer to the destination,
    10.     /// so you can use it later in rendering. For example, you can copy
    11.     /// the opaque texture to use it for distortion effects.
    12.     /// </summary>
    13.     internal class BlitPass : ScriptableRenderPass
    14.     {
    15.         public enum RenderTarget
    16.         {
    17.             Color,
    18.             RenderTexture,
    19.         }
    20.  
    21.         private readonly ShaderTagId m_shaderTag = new ShaderTagId("Custom Render");
    22.  
    23.  
    24.         public Material blitMaterial = null;
    25.         public int blitShaderPassIndex = 0;
    26.         public FilterMode filterMode { get; set; }
    27.  
    28.         private RenderTargetIdentifier source { get; set; }
    29.         private RenderTargetHandle destination { get; set; }
    30.  
    31.         RenderTargetHandle m_TemporaryColorTexture;
    32.         string m_ProfilerTag;
    33.  
    34.         /// <summary>
    35.         /// Create the CopyColorPass
    36.         /// </summary>
    37.         public BlitPass(RenderPassEvent renderPassEvent, Material blitMaterial, int blitShaderPassIndex, string tag)
    38.         {
    39.             this.renderPassEvent = renderPassEvent;
    40.             this.blitMaterial = blitMaterial;
    41.             this.blitShaderPassIndex = blitShaderPassIndex;
    42.             m_ProfilerTag = tag;
    43.             m_TemporaryColorTexture.Init("_TemporaryColorTexture");
    44.         }
    45.  
    46.         /// <summary>
    47.         /// Configure the pass with the source and destination to execute on.
    48.         /// </summary>
    49.         /// <param name="source">Source Render Target</param>
    50.         /// <param name="destination">Destination Render Target</param>
    51.         public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination)
    52.         {
    53.             this.source = source;
    54.             this.destination = destination;
    55.         }
    56.  
    57.         /// <inheritdoc/>
    58.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    59.         {
    60.             CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
    61.            
    62.             RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
    63.             opaqueDesc.depthBufferBits = 0;
    64.  
    65.             // Can't read and write to same color target, create a temp render target to blit.
    66.             if (destination == RenderTargetHandle.CameraTarget)
    67.             {
    68.                 cmd.GetTemporaryRT(m_TemporaryColorTexture.id, opaqueDesc, filterMode);
    69.                 Blit(cmd, source, m_TemporaryColorTexture.Identifier(), blitMaterial, blitShaderPassIndex);
    70.                 Blit(cmd, m_TemporaryColorTexture.Identifier(), source);
    71.             }
    72.             else
    73.             {
    74.                 Blit(cmd, source, destination.Identifier(), blitMaterial, blitShaderPassIndex);
    75.             }
    76.            
    77.             context.ExecuteCommandBuffer(cmd);
    78.             CommandBufferPool.Release(cmd);
    79.  
    80.             //int myLayerIndexVariable = 2; // ...or LayerMask.NameToLayer(myLayerName)
    81.             int myLayerIndexVariable = LayerMask.NameToLayer("Player");
    82.             int myLayerMaskVariable = 1 << myLayerIndexVariable;
    83.             RenderQueueRange myRenderQueueRange = RenderQueueRange.opaque;
    84.             FilteringSettings filters = new FilteringSettings(myRenderQueueRange, myLayerMaskVariable);
    85.  
    86.             var drawSettings = CreateDrawingSettings(m_shaderTag, ref renderingData, SortingCriteria.None);
    87.             var ds = new DrawingSettings();
    88.             context.DrawRenderers(renderingData.cullResults, ref ds, ref filters);
    89.  
    90.  
    91.         }
    92.  
    93.         /// <inheritdoc/>
    94.         public override void FrameCleanup(CommandBuffer cmd)
    95.         {
    96.             if (destination == RenderTargetHandle.CameraTarget)
    97.                 cmd.ReleaseTemporaryRT(m_TemporaryColorTexture.id);
    98.         }
    99.     }
    100. }
    101.  
     
    KKP0, oleg_v and Rimehp like this.
  11. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
  12. peterbay

    peterbay

    Unity Technologies

    Joined:
    Nov 2, 2017
    Posts:
    100
    Apologies for the late reply. The ShaderTag determines which pass to use from the shader of each object being drawn. It looks at the value in the LightMode tag.

    It looks like what your original code is doing is applying an outline via a screen space pass. Is that correctly understood? In that case, it is not possible to use a layer mask directly in your render pass to limit what the effect applies to. The reason for that is that it is likely using the depth buffer to determine where the edges should be, and that has already been filled out by the objects drawn by previous passes.

    You could achieve this by using the Render Objects feature to render your "Player" layer (and make sure it doesn't get drawn in the main opaque pass), and then use the stencil override to put a value in the stencil buffer that signals that edges should not be drawn around this object. You would then need to modify your outline shader to respect that value.

    Another perhaps easier option is to draw apply the outline pass before drawing the "Player" layer, as it would then only apply to the previous objects. This does however mean that the depth texture will have to be resolved twice, which could have a performance impact. But that depends on platform and target, and it's worth seeing if it works for your case.
     
  13. transporter_gate_studios

    transporter_gate_studios

    Joined:
    Oct 17, 2016
    Posts:
    219
    If you cannot limit by layermask, would you be able to limit to a specific camera? like if you have an overlay camera in the stack that is culling all but a specific layer?
     
  14. transporter_gate_studios

    transporter_gate_studios

    Joined:
    Oct 17, 2016
    Posts:
    219
    if not, could you explain how you would access the stencil buffer in the Execute function?
     
  15. naveengouni22

    naveengouni22

    Joined:
    Sep 12, 2018
    Posts:
    1
    bro, do you got any solution
     
    ChristinaPCatalyst and quellie like this.