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.

Multipass shaders in URP

Discussion in 'Shaders' started by jRocket, Apr 9, 2020.

  1. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    In the classic pipeline, one could write a shader that could have multiple passes- and each pass would be rendered in-order(as least in transparent queue). I need to do something similar in URP. I have seen the custom render passes, but it looks like you can only insert them at specific points in the pipeline- but I need these passes to render whenever the object will render.

    Example
    Object 1 Pass 1
    Object 1 Pass 2
    Object 2 Pass 1
    Object 2 Pass 2

    Whereas custom passes will give me
    Object 1 Pass 1
    Object 2 Pass 1
    Object 1 Pass 2
    Object 2 Pass 2

    Any idea how I could go about going something like this? I am trying to do traditional object outlines/silhouette(with zwrite off), but I need them to be sorted.
     
  2. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I use the renderer features thing. It lets you control shader passes visually, does this help? It's a core feature of URP. You add a forward renderer and then control stencil/passes/etc when you want them to be drawn.
     
  3. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    That's what I was looking at, but as far as I can tell, you can't have it draw something right after or before another object is drawn. You have to do them in passes.
     
  4. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I do this for stencil portals, basically just abuse layers and remove the layer for sorted objects from the default list of objects to render, then you can fine grain it.
     
  5. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    Okay, I could make separate render features for each object I want to do this to- but then, if I am understanding you correctly, I would need different layers for each object. And I am limited to <32 layers. :(

    Is there a way to tell a render feature to just use a particular Renderer instead of filtering by a Layer Mask?
     
  6. BattleAngelAlita

    BattleAngelAlita

    Joined:
    Nov 20, 2016
    Posts:
    391
    Impossible. Not only in URP itself, but in any custom SRP. You only option is using multi-materials.
     
    INeatFreak likes this.
  7. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    Do you mean by multi-materials? Where some triangles of an object have different materials? In my case, I need the entire SkinnedMeshRenderer to render twice, one after another. I thought about adding a second SkinnedMeshRenderer to a child GameObject, but I think that would inefficient as the skinning would get evaluated twice, probably isn't guaranteed to render in-order, and it adds complexity.
     
  8. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    Here is the effect I am going for, which was pretty simple to make in the classic pipeline with multipass shaders-



    Is this really impossible to do in URP, or any scriptable pipeline? If so, why it URP more limiting than the classic pipeline? I thought it was supposed to be production-ready.
     
    mowax74, deus0 and INeatFreak like this.
  9. BattleAngelAlita

    BattleAngelAlita

    Joined:
    Nov 20, 2016
    Posts:
    391


    No, there is no such functionality in SRP api.

    Marketing, nothing personal.
     
  10. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    That looked promising, but I just tested it and it doesn't render them in-order. It will render all objects with pass 1 and then render all objects of pass 2, just like using the render feature. :confused:
    I'll try to see if I can manually render these objects out with a command buffer.
     
  11. scotthNM

    scotthNM

    Joined:
    Apr 17, 2015
    Posts:
    4
    Hang on. if multi-pass shaders don't work anymore, how do we do meta passes for lightmap support?
     
  12. BattleAngelAlita

    BattleAngelAlita

    Joined:
    Nov 20, 2016
    Posts:
    391
  13. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Sorry if late to the party, but since I'm here...
    You don't need a layer per object, if the effect is the same. For instance you can do the outline pass on multiple characters if they are using the same outline effect, with just one pass.

    Hope it helps!
     
  14. kkrg001

    kkrg001

    Joined:
    Mar 6, 2019
    Posts:
    35
    How can I use 2pass to make a transparent expression considering depth?
    In other words, it is a transparent expression like the left side of the attached image.
     
  15. comomomo

    comomomo

    Joined:
    Feb 16, 2014
    Posts:
    12

    Hi kkrg001! I'm trying to figure the same thing out but having trouble passing multiple textures through passes.

    Here's a link to a thread you might find helpful for your issue: https://forum.unity.com/threads/question-about-transparency-and-rear-faces.774224/#post-5213051

    It was nice when I could write a custom pass and it would just grab the maintexture from the model it was rendering. I'm not clear on how to do something similar in this pipeline without adding more passes.
     
  16. chavalo

    chavalo

    Joined:
    Feb 1, 2020
    Posts:
    1
    Hey. I ran into some issues with multi-pass shaders using URP too. I made a repository in which I tried my best to explain all the solutions I found to this problem. Some solutions are already in this thread, but I hope that having a project with all the examples will be more straightforward to understand.

    And I know this thread isn't recent, but it will hopefully help some people who stumble upon this thread in search of a solution like I did many times. :D

    https://github.com/chavaloart/
     
  17. Hysparions

    Hysparions

    Joined:
    Jan 7, 2019
    Posts:
    29
    Hello, Trying to get the same effect you show with the armored guys, I wanted to know if you successfuly created a renderer feature that can render those mesh in the right order
     
    Last edited: Mar 24, 2021
  18. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    No, I never got it to work and went back to the built-in pipeline. URP is kind of bad due to this and other limitations, and I would not recommend using it.
     
    Vacummus and Hysparions like this.
  19. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    190
    Yeah, not supporting multi-pass is really limiting for things like this. I ended up getting around the limitation by using the URP's Renderer Feature. Going back to the built-in pipeline is not an option for me since it doesn't support the DOTS hybrid renderer (which for the rendering performance (and architectural sanity) is a must-have for my game).
     
  20. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    190
    I am curious though if anyone knows whether unity will support multi-pass shaders for URP in the future?
     
  21. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    190
    I know this like a year old, but for anyone that this looking for answers to this kind of question. URP's Render Feature does not work on per object bases. It will apply the shader to all objects for the tag it is filtering by (or if no tag is specified then it will do it for all objects). Good tutorial right here for anyone interested on how to create hull outlines with URP's Render Feature:
     
    Lardalot and Hysparions like this.
  22. Hysparions

    Hysparions

    Joined:
    Jan 7, 2019
    Posts:
    29
    Yes but post process outlines do have their limitations, It doesn't work with depth of field, and it makes inner outlines. More over outlines are not depth dependent
     
    Vacummus likes this.
  23. INeatFreak

    INeatFreak

    Joined:
    Sep 12, 2018
    Posts:
    46
    Use "LightMode" = "UniversalForward" for the first and "LightMode" = "SRPDefaultUnlit" tag for the second pass. But this will only work for two passes and no more.

    EDIT: "SRPDefaultUnlit" is drawn AFTER the "UniversalForward" tagged pass regardless of the pass write orders in shader file.

    Code (CSharp):
    1. Pass { Name "Pass A"
    2.     Tags { "LightMode" = "UniversalForward" }
    3.  
    4.     // ...
    5. }
    6.  
    7. Pass { Name "Pass B"
    8.     Tags { "LightMode" = "SRPDefaultUnlit" }
    9.  
    10.     // ...
    11. }
     
    Last edited: Apr 7, 2021
  24. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    118
    I define a pass in my object shader for rendering outlines as:
    Code (csharp):
    1.         Pass
    2.         {
    3.             Name "OutlinePass"
    4.             Tags {
    5.                 "LightMode" = "Outlines"
    6.                 "DisableBatching" = "True"
    7.             }
    8.  
    To draw only objects whose materials use this pass in a SRP feature I use
    Code (CSharp):
    1. static ShaderTagId OutlinesShaderTagID = new ShaderTagId("Outlines");
    2.  
    3. ...
    4.  
    5.             var sort = new SortingSettings(renderingData.cameraData.camera);
    6.             var drawingSettings = new DrawingSettings(OutlinesShaderTagID, sort);
    7.             var filteringSettings = new FilteringSettings(RenderQueueRange.all);
    8.             context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings);
    The "Outlines" is just a name, and you can call it whatever you like for your shader.
     
  25. Noors84

    Noors84

    Joined:
    Jul 12, 2016
    Posts:
    73
    Hey. This used to work for me but but I'm on URP 10.4.0 and now it looks it's the other way around.
    SRPDefaultUnlit renders before UniversalForward.
    Or i'm going crazy.
     
    GameDeveloper1111 and deus0 like this.
  26. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    Thank you so much for this solution (Lightmode tags)
     
  27. ergexh

    ergexh

    Joined:
    Sep 10, 2020
    Posts:
    3
    Hey bro. I want this "Object 1 Pass 1 Object 2 Pass 1 Object 1 Pass 2 Object 2 Pass 2" rendering order, but my multi-pass shader performs just like your Example "Object 1 Pass 1 Object 1 Pass 2 Object 2 Pass 1 Object 2 Pass 2". I don't know if this result is caused by opaque/transparent render queue selection or batcher or anything else. Could you share your shader settings? thx
     
  28. ergexh

    ergexh

    Joined:
    Sep 10, 2020
    Posts:
    3
    Perhaps I'm using later version of URP so the rendering order has changed. Fine. I just solved it by using RenderObjects Feature to retrieve the order "Object 1 Pass 1 Object 2 Pass 1 Object 1 Pass 2 Object 2 Pass 2" which I intend to play.
     
  29. SunnyChow

    SunnyChow

    Joined:
    Jun 6, 2013
    Posts:
    360
    so..
    how do we do

    Object 1 Pass 1
    Object 1 Pass 2
    Object 2 Pass 1
    Object 2 Pass 2

    in URP?
     
    Naomi-DevOps likes this.
  30. GameDeveloper1111

    GameDeveloper1111

    Joined:
    Jul 24, 2020
    Posts:
    100
    Thanks to this thread I was able to render two passes with one shader in URP 10.5.1 in Unity 2020.3.14f1. In this example, the shader colors the object green, even if the object is occluded (allowing the object to be seen through walls); and then the shader colors the visible portion of the object red.

    Code (CSharp):
    1. Shader "Unlit/TwoPasses"
    2. {
    3.     SubShader
    4.     {
    5.         LOD 100
    6.  
    7.         Tags { "Queue" = "Transparent" }
    8.  
    9.         Pass  // Executes this first
    10.         {
    11.             Tags { "LightMode" = "SRPDefaultUnlit" }
    12.  
    13.             Cull Off
    14.             ZWrite Off
    15.             ZTest Always
    16.  
    17.             CGPROGRAM
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #include "UnityCG.cginc"
    21.  
    22.             struct appdata
    23.             {
    24.                 float4 vertex : POSITION;
    25.             };
    26.  
    27.             struct v2f
    28.             {
    29.                 float4 vertex : SV_POSITION;
    30.             };
    31.  
    32.             v2f vert(appdata v)
    33.             {
    34.                 v2f o;
    35.                 o.vertex = UnityObjectToClipPos(v.vertex);
    36.                 return o;
    37.             }
    38.  
    39.             fixed4 frag(v2f i) : SV_Target
    40.             {
    41.                 return float4(0,1,0,0);  // Green
    42.             }
    43.  
    44.             ENDCG
    45.         }
    46.  
    47.         Pass  // Executes this second
    48.         {
    49.             Tags { "LightMode" = "UniversalForward" }
    50.  
    51.             CGPROGRAM
    52.             #pragma vertex vert
    53.             #pragma fragment frag
    54.             #include "UnityCG.cginc"
    55.  
    56.             struct appdata
    57.             {
    58.                 float4 vertex : POSITION;
    59.             };
    60.  
    61.             struct v2f
    62.             {
    63.                 float4 vertex : SV_POSITION;
    64.             };
    65.  
    66.             v2f vert(appdata v)
    67.             {
    68.                 v2f o;
    69.                 o.vertex = UnityObjectToClipPos(v.vertex);
    70.                 return o;
    71.             }
    72.  
    73.             fixed4 frag(v2f i) : SV_Target
    74.             {
    75.                 return float4(1,0,0,0);  // Red
    76.             }
    77.             ENDCG
    78.         }
    79.     }
    80. }
    81.  
     
    Metthatron likes this.
  31. Niter88

    Niter88

    Joined:
    Jul 24, 2019
    Posts:
    100
    I know this thread is being kept alive for a while, and since it is one of the only places I found an coherent answer for that I will keep things here.

    I've posted a question on this Thread just like this and later I've posted the only two ways I found for doing it (after some hours of research). It contains details anyone shall be able to understand so I hope it may help some of you.

    @comomomo and @jRocket if you are still struggling with that, have a peek in that link.

    Thanks everyone who participated along this thread, I wouldn't be able to find a way without you.
     
    comomomo likes this.
  32. jRocket

    jRocket

    Joined:
    Jul 12, 2012
    Posts:
    650
    For my case, I needed more precise control over the rendering order.

    What I was looking for is-

    render pass 1 of all renderers in character 1.
    render pass 2 of all renderers in character 1.
    render pass 1 of all renderers in character 2.
    render pass 2 of all renderers in character 2.

    Just using rendering tags will not work because it only renders either all objects with that pass at once, or renders one pass after another for each object.

    In built-in, I accomplished this with CommandBuffer.DrawRenderer() to basically tell the renderer to manually draw renderer x with pass y, but I haven't figured out a way to do that in URP. It seems like such a simple things, but I fear it might not be possible. I don't understand why Unity had to diverge so much away from how things were done in built-in, but no longer work in URP/HDRP.

    My example here a little more complicated with multiple hull outlines, ztest always, and usage of stencil buffer.

    outlines.gif
     
    Niter88 likes this.
  33. Niter88

    Niter88

    Joined:
    Jul 24, 2019
    Posts:
    100
    wow, so pretty game.
    Yeah, on my implementation it renders pass 1 for all objects and then pass 2 for all. Accounting for ZTest.
    The implementation of a highlight shader is much easier than what I was looking for (if it is what you want and not only an example), people had made great tutorials since 2020.

    Using ShaderGraph for highlight in 2D
    and
    Per Object Outlines with shadergraph (3D) with edge detection (this is a simpler one). This guy has an entire series on outlines, like this one with screen shader.

    URP is modern and amazing tool, may be worth to create a learning project to test things. Cheers
     
    Last edited: Jun 8, 2022
  34. lingdu_y

    lingdu_y

    Joined:
    Apr 15, 2021
    Posts:
    10
    yes, I maybe found why this happened.
    upload_2022-6-13_20-9-55.png
     
  35. kruskal21

    kruskal21

    Joined:
    May 17, 2022
    Posts:
    65
    I have been doing some experimenting to see if I can get the same effect from jRocket's post above in URP 12. While I didn't figure out how to interleave passes between rendering objects, I managed to get some pretty nice-looking results by using custom renderer features.

    Outlines.png

    The objects are just single-mesh unity primitives. The outlines are per-object, and drawn outside of the mesh. For anyone looking to create a similar effect, you can do this by having a custom renderer feature with three custom passes:
    1. A pass using context.DrawRenderers that render objects into a temporary render texture with only a single color channel, you can use a format like R8. This pass renders an arbitrary ID of the object so that they can be differentiated. You can render this pass pretty much as early as you like, I chose AfterRenderingPrePasses. Define an _ObjectID property in your shader and in the fragment function, just do something like:
      return (float)_ObjectID / 255;
    2. A pass using context.DrawRenderers that render objects into another temporary render texture with all RGBA channels. This pass renders just the color of the object outlines. Again, you can render this pass early. Declare an _OutlineColor property in your shader, and do this in the fragment function:
      return _OutlineColor;
    3. A last full-screen pass using Blit that actually draws the outlines. The pass has each fragment sample object IDs from neighbouring pixels using the temporary texture from pass 1. If any neighbouring pixels' object IDs are different, then test using the camera depth texture to see if they are closer to the camera, if yes, then sample from the temporary texture from pass 2 to determine the outline color. To prevent transparents from overwriting the outline pixels, you will also want to write to SV_Depth in the fragment function using the depth value of the neighbouring pixel.
    Unfortunately as of right now all of this is only possible if you are willing to write code shaders. But something like this is definitely possible with URP.
     
  36. noirb

    noirb

    Joined:
    Apr 10, 2014
    Posts:
    64
    @kruskal21 : How did you pass the ObjectID to the shader? Are you able to do this from within the RenderPass directly?
     
  37. kruskal21

    kruskal21

    Joined:
    May 17, 2022
    Posts:
    65
    I did it by having _ObjectID as a normal material property, and having each game object set its own value according to a global variable that stores the last-used object ID. This means that technically each object will have a different material, instantiated at runtime.