Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to Outline Objects in Unity 2023 URP17?

Discussion in 'Universal Render Pipeline' started by equal, Sep 7, 2023.

  1. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Hi,

    the first half is ranting,
    the second part is where i explain what i want to achive in Unity 2023 in URP 17 with a Custom Renderer Feature and RenderPass.
    I am very thankful for any help!

    Thou i would like some reasoning or feedback on part 1, im much more happy about support in the actual implementation (part 2)

    -------------------- Part 1
    i am very frustrated with the URP stuff. It is great that you guys work on it, but there is not a single reliable Documentation that explains how>and< when to use it, especially for the "newer" RTHandle+Blitter class.

    Initially all i wanted is to outline/highlight the gameobjects that a player has selected, throu everything.
    Like this:
    upload_2023-9-7_15-17-20.png
    This can not possibly be that hard and in fact, its not.
    So i assign a second material to a Renderer and instantly unity tells me that this is bad, with a Warning sign.
    upload_2023-9-7_15-18-54.png
    Unity instructs me to instead use a MultiPass Shader.
    (FUNNY NOTE: The CookBook crops this warning out, wha... why ?????? ???????)

    So i make a MultiPass Shader and Unity tells me that this will not work, its not supported.
    If this is not supported, then why suggest it in the first place?

    But ok , i did not want to use multimaterials for this anyway.

    I was on a different Version of Unity in the past (so not 2023) and following every possible Tutorial that i could find. The tutorials use RenderTargetTexture or whatnot and the Blit command.
    But Unity complains again.Now the RTHandle is the way to go and please use Blitter.
    Also we have not only AddRenderPasses in the RendererFeature but also SetupRenderPasses.
    The Documentation says basically "please use SetupRenderePasses, too" ; never delivering any examples of why someone should put code here rather then the Add Method.
    My blind assumption would be that Add gets called 1 single time, and Setup gets called multiple times at random situations.

    There must have been a lot of work put into this, with defined intentions and intendet uses. Please communicate those to us.

    The Blitter Documentation is exceptionally cryptic, the
    BlitTexture(CommandBuffer, RTHandle, Vector4, Material, Int32) method has a source RTHandle but no destination?
    This is confusting, it is not clear to me what the destination is.
    A later method does not even have a source RTHandle, but at least the documentation explains that it needs a destination. What is the Source in this case?

    Also the cookbooks that you guys make, they are hidden from Google. They were the second last thing i found after many many searches over multiple days. I never found them throu google either, it was by sheer chance that i decided to check out the Unity Forums for URP and Sharer stuff.

    -------------------- Part 2

    I try to achive this effect
    upload_2023-9-7_16-19-20.png

    The Player selects some Objects, those Objects go into a RenderLayer or any kind of Collection.
    This is important because i dont want all elements to he outlined, just some.
    I have a CustomRendererFeature for this with a RenderPass.
    The Idea is that the RenderPass draws those objects on a new RTHandle and a Shader goes in and blurs them.
    The Objects_Blured layer then gets laid on top of other objects and the skybox.

    The bluring or the general concept is not the problem, the main problem is the precise implementation, when should i call what method of the Blitter?
    How would i go about achiving this in Unity 2023 URP 17 ?
     
    Last edited: Sep 9, 2023
  2. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    631
    Hi, I think it's unavoidable that you may need to render these (glowing) players twice.

    One way is to:

    1. Add a shader pass (with a custom LightMode tag like "MyUniversalForward") to the player's shader.

    2. Adjust the fragment output of this shader pass according to your requirements.

    3. In your custom renderer feature, create a render pass:
    • Allocate 2 RTHandle (1 color + 1 depth), and set the render target of this render pass to them. (you may ignore setting the depth RTHandle if depth sorting is unimportant)
    • Use
      CommandBuffer.DrawRendererList()
      to draw objects only with "MyUniversalForward" shader tag.
    Here's an example:
    Code (CSharp):
    1. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    2. {
    3.     CommandBuffer cmd = CommandBufferPool.Get();
    4.  
    5.     // Profiler support
    6.     using (new ProfilingScope(cmd, new ProfilingSampler("Draw Custom Objects")))
    7.     {
    8.         // Use shader tag to control which object should be drawn.
    9.         // You can use other ways, but a custom shader pass is still required.
    10.         RendererListDesc rendererListDesc = new RendererListDesc(new ShaderTagId("MyUniversalForward"), renderingData.cullResults, renderingData.cameraData.camera);
    11.  
    12.         // Here you can control more things, like ZWrite, ZTest, Cull, and RenderQueue.
    13.         rendererListDesc.sortingCriteria = renderingData.cameraData.defaultOpaqueSortFlags;
    14.         //...
    15.  
    16.         // This is equivalent to the old "context.DrawRenderers()" in URP 12.
    17.         RendererList rendererList = context.CreateRendererList(rendererListDesc);
    18.         cmd.DrawRendererList(rendererList);
    19.     }
    20.     context.ExecuteCommandBuffer(cmd);
    21.     cmd.Clear();
    22.     CommandBufferPool.Release(cmd);
    23. }
    4. In your custom renderer feature, create another render pass which is executed after the first one:
    • Create a shader that perform your custom image effect.
    • Use
      CommandBuffer.Blit()
      (doesn't handle XR correctly) or
      Blitter.BlitCameraTexture()
      to do a full screen blit from an RTHandle to another (cameraColorTargetHandle) with your shader.
    Note: If you choose the Blitter method, you need to change the vertex shader of yours to the one in

    "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"


    Also, you should use "_BlitTexture" (blit source) rather than "_MainTex" in your custom shader when using Blitter.

    For example:
    Code (CSharp):
    1. HLSLPROGRAM
    2. // The Blit.hlsl file provides the vertex shader (Vert),
    3. // input structure (Attributes) and output strucutre (Varyings)
    4. #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
    5.  
    6. // Use Blitter's vertex shader
    7. #pragma vertex Vert
    8. #pragma fragment frag
    9. //...
     
    Last edited: Sep 7, 2023
  3. equal

    equal

    Joined:
    Dec 14, 2012
    Posts:
    77
    Thank you very much @wwWwwwW1

    For unknown reasons i didnt got an alert for your reply.

    Anyway, the ShaderTagId method is something id like to avoid for now. The usecase in my game is a general Selection of Objects and Units. Think of it as an RTS game where the player can select up to 100+ Objects, all with their own Materials/Shaders. If nothing else works thou, i will go down this route and add custom Tags to all the Shaders.

    Here is what i tried to get running:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Experimental.Rendering;
    6. using UnityEngine.Experimental.Rendering.RenderGraphModule;
    7. using UnityEngine.Rendering;
    8. using UnityEngine.Rendering.RendererUtils;
    9. using UnityEngine.Rendering.Universal;
    10.  
    11. public class MyRendererFeature : ScriptableRendererFeature
    12. {
    13.     // Serializable class to hold the settings for our custom rendering feature
    14.     [System.Serializable]
    15.     public class MyFeatureSettings
    16.     {
    17.         public LayerMask layerMask;
    18.         public Material overrideMaterial;
    19.         public RenderPassEvent renderPassEvent;
    20.     }
    21.  
    22.     public MyFeatureSettings settings = new MyFeatureSettings();
    23.  
    24.     private MyRenderPass renderPass;
    25.  
    26.     public override void Create()
    27.     {
    28.         renderPass = new MyRenderPass(settings.renderPassEvent, settings.layerMask, settings.overrideMaterial);
    29.     }
    30.  
    31.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    32.     {
    33.         renderer.EnqueuePass(renderPass);
    34.     }
    35.  
    36.  
    37.  
    38.     // Custom render pass to use RendererList
    39.     class MyRenderPass : ScriptableRenderPass
    40.     {
    41.         private LayerMask layerMask;
    42.         private Material overrideMaterial;
    43.  
    44.         RTHandle _occ;
    45.  
    46.         public MyRenderPass(RenderPassEvent renderPassEvent, LayerMask layerMask, Material overrideMaterial)
    47.         {
    48.             this.renderPassEvent = renderPassEvent;
    49.             this.layerMask = layerMask;
    50.             this.overrideMaterial = overrideMaterial;
    51.         }
    52.  
    53.         public void Dispose()
    54.         {
    55.             _occ?.Release();
    56.         }
    57.  
    58.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    59.         {
    60.             CommandBuffer cmd = CommandBufferPool.Get();
    61.  
    62.             RenderingUtils.ReAllocateIfNeeded(ref _occ, renderingData.cameraData.cameraTargetDescriptor );
    63.             var result = new UnityEngine.Rendering.RendererUtils.RendererListDesc(ShaderTagId.none, renderingData.cullResults, renderingData.cameraData.camera)
    64.             {
    65.                 //rendererConfiguration = renderConfig,
    66.                 //renderQueueRange = GetRenderQueueRange(renderQueueType),
    67.                 //sortingCriteria = sortingCriteria,
    68.                 excludeObjectMotionVectors = false,
    69.                 //overrideShader = overrideMode == OverrideMaterialMode.Shader ? overrideShader : null,
    70.                 //overrideMaterial = overrideMode == OverrideMaterialMode.Material ? overrideMaterial : null,
    71.                 //overrideMaterialPassIndex = (overrideMaterial != null) ? overrideMaterial.FindPass(overrideMaterialPassName) : 0,
    72.                 //overrideShaderPassIndex = (overrideShader != null) ? overrideShaderMaterial.FindPass(overrideShaderPassName) : 0,
    73.                 //stateBlock = stateBlock,
    74.                 layerMask = layerMask,
    75.             };
    76.  
    77.             RendererListDesc rld = new RendererListDesc(ShaderTagId.none, renderingData.cullResults, renderingData.cameraData.camera);
    78.             ////rld.renderingLayerMask = 1;
    79.             //RendererList rl = context.CreateRendererList(result);
    80.  
    81.             using (new ProfilingScope(cmd,
    82.                 new ProfilingSampler("MyRenderPass")))
    83.             {
    84.  
    85.                 cmd.SetRenderTarget(_occ);
    86.                 //cmd.DrawRendererList(rl);
    87.             }
    88.  
    89.  
    90.  
    91.  
    92.  
    93.             context.ExecuteCommandBuffer(cmd);
    94.             CommandBufferPool.Release(cmd);
    95.         }
    96.     }
    97. }
    98.  
    99.  
    In the code above, the Editor prints Errors when i unComment the "RendererList rl" (79) line.
    The error is: "InvalidOperationException: The CullingResults instance is invalid. This can happen if you construct an instance using the default constructor."
    But to me is not clear where i made the error.
    I plan to use the Rendering Layers for the selected objects
     
  4. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    631
    Sorry that I also don't know what should be the cause of this error.

    It's possible to only draw objects with a specific layer (in renderer feature), and there're many examples on the Internet about it. You may refer to those to find out the reason.

    If no one answered here, you can also try submitting a bug report.