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

Single Pass Instancing Surface Shader with "vertex modifier"

Discussion in 'Shaders' started by MrPapayaMan, Jul 7, 2021.

  1. MrPapayaMan

    MrPapayaMan

    Joined:
    Feb 16, 2021
    Posts:
    39
    Trying to use DrawMeshInstancedIndirect with single pass instancing. I've read that surface shaders already have the macros by default:

    https://forum.unity.com/threads/do-...-rendering-method-for-vr.632497/#post-4237783

    However, with a "vertex modifier" it appears not to work.





    Even thought the above pics are very similar, the subtle offset from the right eye and left eye is pretty painful with a headset on.

    I can get it working with a vertex shader by following these instructions:

    https://docs.unity3d.com/Manual/SinglePassInstancing.html

    I seem to have issues applying the instructions to the following shader:

    Code (CSharp):
    1. Shader "Custom/TestShader"
    2. {
    3.     Properties{
    4.       _MainTex("Texture", 2D) = "white" {}
    5.     }
    6.         SubShader{
    7.           Tags { "RenderType" = "Opaque" }
    8.           CGPROGRAM
    9.           #pragma surface surf Lambert vertex:vert
    10.          
    11. #ifdef SHADER_API_D3D11
    12.           StructuredBuffer<float4x4> Matrices;
    13. #endif
    14.        
    15.       struct appdata
    16.         {
    17.             float4 vertex    : POSITION;  
    18.             float3 normal    : NORMAL;    
    19.             float4 texcoord  : TEXCOORD0;
    20.             float4 texcoord1 : TEXCOORD1;
    21.             float4 tangent   : TANGENT;  
    22.             float4 color     : COLOR;
    23. #ifdef SHADER_API_D3D11
    24.             uint vertexID : SV_VertexID;
    25.             uint instanceID : SV_InstanceID;
    26. #endif
    27.             //Shader error in 'Custom/TestShader': redefinition of 'appdata::instanceID' at line 27 (on d3d11)
    28.             //UNITY_VERTEX_INPUT_INSTANCE_ID
    29.         };
    30.  
    31.           struct Input {
    32.               float2 uv_MainTex;
    33.           };
    34.          
    35.  
    36.           void vert(inout appdata v, out Input o) {
    37.               UNITY_SETUP_INSTANCE_ID(v); //Insert
    38.               UNITY_INITIALIZE_OUTPUT(Input, o);
    39.               //Shader error in 'Custom/TestShader': invalid subscript 'stereoTargetEyeIndex' at line 47 (on d3d11)
    40.               //UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //Insert
    41.  
    42. #ifdef SHADER_API_D3D11
    43.               float4 pos = mul(Matrices[v.instanceID], v.vertex);
    44.               v.vertex = pos;
    45. #endif
    46.           }
    47.  
    48.           sampler2D _MainTex;
    49.           void surf(Input IN, inout SurfaceOutput o) {
    50.               o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
    51.           }
    52.           ENDCG
    53.       }
    54.           Fallback "Diffuse"
    55. }
    Anyone have thoughts on how to do this or is it even possible to add single pass instancing to a surface shader with a "vertex modifier"?

    Any ideas would be greatly appreciated.
     
  2. KCGames47

    KCGames47

    Joined:
    Nov 3, 2014
    Posts:
    39
    I have the same exact issue! did you find a solution?
     
  3. MrPapayaMan

    MrPapayaMan

    Joined:
    Feb 16, 2021
    Posts:
    39
    I did not. I gave up on it awhile ago. It’s something I still need so I’ll probably revisit it and update this if I get it working.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    There are several little issues happening here.

    First: You can't actually use custom
    appdata
    structs in Surface Shaders consistently. I know it doesn't every say that anywhere in Unity's documentation, and I think some of the official examples do it, but it doesn't actually work all of the time and the only correct way to use Surface Shaders with a vertex modifier is by using the built in
    appdata_full
    .

    Second: None of the macros you were calling would ever set
    v.instanceID
    , and it's not guaranteed to exist all of the time when using DX11, only when one of three defines are set. So we can check that. But in some cases the instance ID doesn't even come from the vertex data! Luckily the
    DEFAULT_UNITY_SETUP_INSTANCE_ID(v)
    macro guarantees that (if instancing is enabled) there will be a
    unity_InstanceID
    you can access that is the instance ID. So it's best to use that.

    Code (CSharp):
    1. Shader "Custom/TestShader"
    2. {
    3.     Properties{
    4.       _MainTex("Texture", 2D) = "white" {}
    5.     }
    6.         SubShader{
    7.           Tags { "RenderType" = "Opaque" }
    8.           CGPROGRAM
    9.           #pragma surface surf Lambert vertex:vert
    10.        
    11. #ifdef SHADER_API_D3D11
    12.           StructuredBuffer<float4x4> Matrices;
    13. #endif
    14.           struct Input {
    15.               float2 uv_MainTex;
    16.           };
    17.        
    18.           void vert(inout appdata_full v) {
    19.               DEFAULT_UNITY_SETUP_INSTANCE_ID(v);
    20. #if defined(UNITY_INSTANCING_ENABLED) || defined(UNITY_PROCEDURAL_INSTANCING_ENABLED) || defined(UNITY_STEREO_INSTANCING_ENABLED)
    21.               float4 pos = mul(Matrices[unity_InstanceID], v.vertex);
    22.               v.vertex = pos;
    23. #endif
    24.           }
    25.           sampler2D _MainTex;
    26.           void surf(Input IN, inout SurfaceOutput o) {
    27.               o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
    28.           }
    29.           ENDCG
    30.       }
    31.           Fallback "Diffuse"
    32. }
     
    Last edited: May 2, 2022
    Samustai and MrPapayaMan like this.
  5. MrPapayaMan

    MrPapayaMan

    Joined:
    Feb 16, 2021
    Posts:
    39
    It works! Thanks again @bgolus !!!!!