Search Unity

Question How to render an ObjectID map in URP? (a.k.a. how to replace shaders at runtime?)

Discussion in 'Universal Render Pipeline' started by kodra_dev, Nov 11, 2022.

  1. kodra_dev

    kodra_dev

    Joined:
    Oct 31, 2022
    Posts:
    108
    I'm trying to implement this idea (render object ids to a texture for pixel perfect selection), but I got stuck at even the first step:

    Converted to a color, this id is rendered each frame to a `RenderTexture` using Replaced Shaders (https://docs.unity3d.com/Manual/SL-ShaderReplacement.html).​

    But ShaderReplacement is only for BiRP. So what if I'm using URP?

    Like, I got the idea. I got how to write a shader to output obj id as color. But how could I render it as an additional pass?
     
  2. kodra_dev

    kodra_dev

    Joined:
    Oct 31, 2022
    Posts:
    108
    I took some take studying this subject. It's actually pretty easy to render an object ID map in URP.

    First I write a simple shader like this:

    Code (CSharp):
    1.  
    2. Shader "IDMap/IDMap"
    3. {
    4.     Properties
    5.     {
    6.         _EncodedSubjectID("Encoded Subject ID", Vector) = (0, 0, 0, 0)
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
    12.  
    13.         Pass
    14.         {
    15.             HLSLPROGRAM
    16.             #pragma vertex vert
    17.             #pragma fragment frag
    18.  
    19.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    20.  
    21.             struct Attributes
    22.             {
    23.                 // The positionOS variable contains the vertex positions in object
    24.                 // space.
    25.                 float4 positionOS   : POSITION;
    26.             };
    27.  
    28.             struct Varyings
    29.             {
    30.                 float4 positionHCS  : SV_POSITION;
    31.             };
    32.  
    33.             CBUFFER_START(UnityPerMaterial)
    34.                 float4 _EncodedSubjectID;
    35.             CBUFFER_END
    36.  
    37.             Varyings vert(Attributes IN)
    38.             {
    39.                 Varyings OUT;
    40.                 OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
    41.                 return OUT;
    42.             }
    43.  
    44.             float4 frag() : SV_Target
    45.             {
    46.                 return _EncodedSubjectID;
    47.             }
    48.             ENDHLSL
    49.         }
    50.     }
    51. }
    And make a material with this shader. Since MaterialPropertyBlock can override the properties in a material, we can just set _EncodedSubjectID in a script.

    Then I set DrawingSetting.overrideMaterial to this material in a render feature. Now I have the object ID map.

    However it's not perfect. Because SRP batcher doesn't support MaterialPropertyBlock, all the objects whose IDs needed to be drawn can't be batched any more.

    If someone knows a better SRP-compatible solution, please share!
     
  3. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    642
    You probably want to look into custom RendererFeatures to implement this, perhaps based on the code for the 'Render Objects' feature, but modified to render to a different render target.
     
  4. kodra_dev

    kodra_dev

    Joined:
    Oct 31, 2022
    Posts:
    108
    Uh yes of course I'm already using RendererFeatures in my above example. The problem is how to pass the object id from game logic to the "_EncodedSubjectID" in my shader.

    MaterialPropertyBlock works, but it'll make SRP batcher useless.
     
  5. justin_sunblink

    justin_sunblink

    Joined:
    Dec 3, 2019
    Posts:
    18