Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

UpdateDepthTexture breaks per instance matrixes with Vertex Displacement + GPU instancing

Discussion in 'Shaders' started by FlyingOreos, May 20, 2020.

  1. FlyingOreos

    FlyingOreos

    Joined:
    Mar 24, 2014
    Posts:
    12
    Here's my setup:
    I have some grass, and some trees. It sways with the wind by animating vertex positions.
    I'm about to start generating a bunch of grass, and would like to get instancing working. The shader works perfectly fine without instancing.
    When I turn it on, I have two issues:

    - I can see the shadows of object behind the grass sometimes.
    upload_2020-5-20_23-1-16.png

    - When in front of my water, which reads from depth texture to determine color and foam, I can see that animated vertex positions for my grass differ between the depth texture write and the rest of the shader.

    upload_2020-5-20_23-1-49.png

    If I go into the frame debugger, I can clearly see that the grass animates the exact same way for all my meshes, during the depth pass. But in the actual drawing of the object, the animation is correct.

    I guess the GPU instancing breaks the per instance world matrixes for the depth pass, resulting in different outputs.

    I've been at this for some hours now and can't find a solution. Anybody out there with some ideas?
    Here's the shader code. Note that the lighting function and wind function are in cgincludes. The wind function is in the bottom.

    Code (CSharp):
    1.  
    2.  
    3.     SubShader
    4.     {
    5.         Pass
    6.         {
    7.             AlphaToMask On
    8.             Cull[_Cull]
    9.        
    10.             Tags
    11.             {
    12.                 "RenderType"="TransparentCutout"
    13.                 "LightMode"="ForwardBase"
    14.                 "IgnoreProjector"="True"
    15.                 "Queue"="AlphaTest"
    16.             }
    17.  
    18.             CGPROGRAM
    19.  
    20.             #include "Assets/Shaders/Fog/Fog.cginc"
    21.             #include "Assets/Shaders/Toon/ToonVegetation.cginc"
    22.             #include "Assets/Shaders/Wind.cginc"
    23.             #include "AutoLight.cginc"
    24.             #include "UnityCG.cginc"
    25.        
    26.             #pragma vertex vert
    27.             #pragma fragment frag
    28.        
    29.             #pragma shader_feature FOG_NOISE
    30.             #pragma shader_feature SSS
    31.             #pragma shader_feature RIM
    32.             #pragma shader_feature_local WIND
    33.             #pragma shader_feature_local WIND_UV
    34.  
    35.             #pragma multi_compile_fwdbase
    36.             #pragma multi_compile_instancing
    37.  
    38.             sampler2D _MainTex;
    39.             float4 _MainTex_ST;
    40.             fixed _Cutoff;
    41.  
    42.             struct appdata
    43.             {
    44.                 float4 vertex : POSITION;
    45.                 float3 normal : NORMAL;
    46.                 float2 uv : TEXCOORD0;
    47.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    48.             };
    49.        
    50.             struct v2f
    51.             {
    52.                 float4 pos : SV_POSITION;
    53.                 float2 uv : TEXCOORD0;
    54.                 float3 worldPos : TEXCOORD1;
    55.                 float3 viewDir : TEXCOORD2;
    56.                 SHADOW_COORDS(3)
    57.                 float3 normal : NORMAL;
    58.                 float4 color : COLOR;
    59.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    60.             };
    61.  
    62.             inline float3 positionToNormal(float3 position)
    63.             {
    64.                 float3 dpx = ddx( position );
    65.                 float3 dpy = ddy( position ) * _ProjectionParams.x;
    66.                 return normalize(cross(dpx, dpy));
    67.             }
    68.        
    69.             float _WindScale;
    70.  
    71.             v2f vert (appdata v)
    72.             {
    73.                 v2f o;
    74.                 UNITY_SETUP_INSTANCE_ID(v);
    75.                 UNITY_TRANSFER_INSTANCE_ID(v, o);
    76.                 float4 vert = v.vertex;
    77. #if WIND
    78.     #if WIND_UV
    79.                 vert = WindPosition(vert, _WindScale * step(0.1, v.uv.y));
    80.     #else
    81.                 vert = WindPosition(vert, _WindScale);
    82.     #endif
    83. #endif
    84.  
    85.                 o.pos = UnityObjectToClipPos(vert);
    86.                 o.worldPos = mul(unity_ObjectToWorld, vert);
    87.                 o.normal = mul(unity_ObjectToWorld, v.normal);
    88.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    89.                 o.color = lerp(UNITY_ACCESS_INSTANCED_PROP(Props, _BottomColor), UNITY_ACCESS_INSTANCED_PROP(Props, _TopColor), saturate(vert.y));
    90.                 o.viewDir = WorldSpaceViewDir(vert);
    91.                 TRANSFER_SHADOW(o);
    92.                 return o;
    93.             }
    94.  
    95.             half4 frag(v2f i) : SV_Target
    96.             {
    97.                 UNITY_SETUP_INSTANCE_ID(i);
    98.                 half4 col = i.color * tex2D(_MainTex, i.uv);
    99.            
    100.                 float3 origNormal = positionToNormal(i.worldPos);
    101.                 float viewFactor = abs(dot(origNormal, -normalize(i.viewDir)));
    102.  
    103.                 col.a = (col.a - _Cutoff) / max(fwidth(col.a), 0.0001) + 0.5;
    104.                 col.a *= viewFactor;
    105.  
    106.                 col = LightingToonVegetation(col, i.normal, _WorldSpaceLightPos0.xyz, i.viewDir, SHADOW_ATTENUATION(i));
    107.                 col = ApplyFogAlphaBlend(i.worldPos, i.pos.w, col);
    108.  
    109.                 return col;
    110.             }
    111.  
    112.             ENDCG
    113.         }
    114.  
    115.         Pass
    116.         {
    117.             Cull[_Cull]
    118.             AlphaToMask On
    119.  
    120.             Tags { "LightMode" = "ShadowCaster"}
    121.        
    122.             CGPROGRAM
    123.  
    124.             #include "Assets/Shaders/Wind.cginc"
    125.             #include "UnityCG.cginc"
    126.  
    127.             #pragma shader_feature_local WIND
    128.             #pragma shader_feature_local WIND_UV
    129.        
    130.             #pragma vertex vert
    131.             #pragma fragment frag
    132.  
    133.             #pragma multi_compile_shadowcaster
    134.             #pragma multi_compile_instancing
    135.  
    136.             float _Cutoff;
    137.             sampler2D _MainTex;
    138.             float4 _MainTex_ST;
    139.  
    140.             struct appdata
    141.             {
    142.                 float4 vertex : POSITION;
    143.                 float2 uv : TEXCOORD0;
    144.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    145.             };
    146.        
    147.             struct v2f
    148.             {
    149.                 V2F_SHADOW_CASTER;
    150.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    151.                 float3 worldPos : TEXCOORD2;
    152.                 float2 uv : TEXCOORD1;
    153.                 float3 viewDir : TEXCOORD03;
    154.             };
    155.  
    156.             inline float3 positionToNormal(float3 position)
    157.             {
    158.                 float3 dpx = ddx( position );
    159.                 float3 dpy = ddy( position ) * _ProjectionParams.x;
    160.                 return normalize(cross(dpx, dpy));
    161.             }
    162.  
    163.             float _WindScale;
    164.  
    165.             v2f vert(appdata v)
    166.             {
    167.                 v2f o;
    168.                 UNITY_SETUP_INSTANCE_ID(v);
    169.                 UNITY_TRANSFER_INSTANCE_ID(v, o);
    170.                 float4 vert = v.vertex;
    171. #if WIND
    172.     #if WIND_UV
    173.                 vert = WindPosition(vert, _WindScale * step(0.1, v.uv.y));
    174.     #else
    175.                 vert = WindPosition(vert, _WindScale);
    176.     #endif
    177. #endif
    178.            
    179.                 o.viewDir = WorldSpaceViewDir(v.vertex);
    180.                 o.worldPos = mul(unity_ObjectToWorld, vert.xyz);
    181.                 o.pos = UnityObjectToClipPos(vert);
    182.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    183.                 return o;
    184.             }
    185.        
    186.             half4 frag(v2f i) : SV_Target
    187.             {
    188.                 UNITY_SETUP_INSTANCE_ID(i);
    189.                 half4 col = tex2D(_MainTex, i.uv);
    190.  
    191.                 float3 origNormal = positionToNormal(i.worldPos);
    192.                 float viewFactor = abs(dot(origNormal, -normalize(i.viewDir)));
    193.            
    194.                 col.a = (col.a - _Cutoff) / max(fwidth(col.a), 0.0001) + 0.5;
    195.                 col.a *= viewFactor;
    196.                 return col;
    197.             }
    198.  
    199.             ENDCG
    200.         }
    201.     }
    202.  

    Wind function:

    Code (CSharp):
    1.  
    2. inline float4 WindPosition(float4 vertex, half strength)
    3. {
    4.     float3 worldPos = mul(unity_ObjectToWorld, float4(vertex.xyz, 1)).xyz;
    5.     float noise = tex2Dlod(_WindNoise, float4(( worldPos.xz * _WindFrequency - _WindDirection.xz * _WindSpeed * _Time.x)  ,0,0));
    6.     float3 wind = noise * _WindDirection * strength * _WindStrength;
    7.     return float4(mul(unity_WorldToObject, float4(worldPos + wind, 1)).xyz, vertex.w);
    8. }
    9.  
     
    Last edited: May 22, 2020
  2. FlyingOreos

    FlyingOreos

    Joined:
    Mar 24, 2014
    Posts:
    12
    Thought I'd clarify a bit with a GIF.

    You can see that the vertex displacement in depth for each grass object is the exact same (The bright outlines on the water), while the actual renders have the correct world space based vertex displacement.


    GrassDepth.gif

    What I've tried so far:
    - Fallback Diffuse / Cutout, and no fallback (Shouldn't matter since I have my own shadowcaster pass?)
    - Different rendertype tags in the shadowcaster pass
    - Different render queues (Geom/AlphaTest)
    - Transfering InstanceID to fragment shader and setting up the ID there was well, in shadowcaster pass
    - Turning AlphaToMask off for shadowcaster pass, and just clipping instead
    - Double checked that Unity is not somehow batching the meshes. Dynamic Batching is off, and DisableBatching tag added. Still no change.
     
    Last edited: May 21, 2020
  3. FlyingOreos

    FlyingOreos

    Joined:
    Mar 24, 2014
    Posts:
    12
    So, after many hours spent I got it working.
    I decided to just keep working on the "mass drawing" part and see if the problem might solve itself.
    What I had to do was use Graphics.DrawMeshInstancedIndirect.(Graphics.DrawMeshInstanced had the same problem!). Now the depth values are correct, since I'm providing my own ObjectToWorld and WorldToObject matrixes.
    I guess the benefit is that I have the flexibility of adding compute shader before rendering, and this would work for any object I need to mass spawn, not just grass.

    I would still however, like to get the other approach working, so if someone has ideas, please let me know!
    It's strange to me that the depth pass wouldn't support the world matrixes properly when just doing scene placed meshes and turning on instancing.
    If I can't get that working, and must always use DrawMeshInstancedIndirect, it basically means that I have to also write something that takes in transforms and makes a computebuffer from those, and apply that to every single tree-crown that I manually place in the world. (or some other placement tool), which breaks the normal unity workflow.


    Here is a couple gifs with ~20.000 grass instances. Now I just need to disallow grass spawning on the water covered part of the mesh :)

    GrassInstancedIndirect.gif GrassInstancedIndirect.gif
     
    Invertex likes this.