Search Unity

Is it possible to have GPU Instancing with Geometry shader?

Discussion in 'Shaders' started by CGDever, May 25, 2020.

  1. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    I need to spawn a few objects with geometry shader. Is it possible to have GPU Instancing & geometry shader?
    I didn't see any example at unity site.

    My source code approximatelly looks like this:
    Code (CSharp):
    1. Properties {
    2.         _MainTex ("Tex", 2D) = "white" {}
    3.  
    4.         _Color ("_Color", Color) = (0,1,1,1)
    5.        
    6.         _Burn("_Burn", Range(0,2)) = 0.0
    7.  
    8. // Geometry!
    9.         _Factor ("Factor", Range(0., 20.)) = 20
    10.     }
    11.  
    12.     SubShader {
    13.         Tags {
    14.             "Queue"="Geometry-2"
    15.             "IgnoreProjector"="true"
    16.             "RenderType"="Opaque"
    17.         }
    18.         LOD 100
    19.  
    20.         Zwrite             On// ff
    21.         Lighting           Off
    22.         Fog         { Mode Off }
    23.         // Cull               Off
    24.  
    25.  
    26.         Blend SrcAlpha OneMinusSrcAlpha
    27.         Pass {
    28.             CGPROGRAM
    29.             #pragma vertex vert
    30.             #pragma fragment frag
    31.             #pragma geometry geom
    32.  
    33.             #pragma multi_compile_instancing//<======
    34.             #include "UnityCG.cginc"
    35.  
    36.             struct appdata {
    37.                 float4 vertex : POSITION;
    38.                 float2 texcoord : TEXCOORD0;
    39.  
    40.                 UNITY_VERTEX_INPUT_INSTANCE_ID//<======
    41.             };
    42.             struct v2g {
    43.                 float4 vertex : POSITION;
    44.                 float2 uv  : TEXCOORD0;
    45.  
    46.                 UNITY_VERTEX_INPUT_INSTANCE_ID//<======
    47.             };
    48.             struct g2f {
    49.                 float4 pos : SV_POSITION;
    50.                 float2 uv : TEXCOORD0;
    51.  
    52.                 UNITY_VERTEX_INPUT_INSTANCE_ID//<======
    53.             };
    54.             sampler2D _MainTex;
    55.             fixed4 _Color;
    56.             fixed _Burn;
    57.  
    58.             //??UNITY_INSTANCING_BUFFER_START(Props)
    59.             //?    UNITY_DEFINE_INSTANCED_PROP(float, _Burn)
    60.             //?UNITY_INSTANCING_BUFFER_END(Props)
    61.  
    62. // Geom:
    63.             float _Factor;
    64.          
    65.             v2g vert (appdata v) {// appdata_base
    66.                 v2g o;
    67.  
    68.                 UNITY_SETUP_INSTANCE_ID(v);
    69.                 // Transfer to fragment shader.
    70.                 //UNITY_TRANSFER_INSTANCE_ID(v, o);
    71.                 //UNITY_INITIALIZE_OUTPUT(v2g, o);
    72.               //UNITY_SETUP_INSTANCE_ID(v);
    73.                 //UNITY_TRANSFER_INSTANCE_ID(v, o);
    74.                 //UNITY_INITIALIZE_OUTPUT(v2g, o);
    75.  
    76.                 o.vertex = v.vertex;
    77.                 o.uv = v.texcoord;
    78.  
    79.                 return o;
    80.             }
    81.             [maxvertexcount(24)]
    82.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> tristream) {
    83.                 g2f o;
    84.  
    85.                 // UNITY_SETUP_INSTANCE_ID(o);
    86.                 // UNITY_TRANSFER_INSTANCE_ID(IN, o);
    87.                 UNITY_INITIALIZE_OUTPUT(g2f, o);
    88.                 float3 edgeA = IN[1].vertex - IN[0].vertex;
    89.                 float3 edgeB = IN[2].vertex - IN[0].vertex;
    90.                 float3 normalFace = normalize(cross(edgeA, edgeB));
    91.  
    92.                 float _factor = _Factor * _Burn;// UNITY_ACCESS_INSTANCED_PROP(Props, _Burn);
    93.                 for(int i = 0; i < 3; i++)
    94.                 {
    95.                     o.pos = UnityObjectToClipPos(IN[i].vertex);
    96.                     o.uv = IN[i].uv;
    97.                     tristream.Append(o);
    98.                     o.pos = UnityObjectToClipPos(IN[i].vertex + float4(normalFace, 0) * _factor);
    99.                     o.uv = IN[i].uv;
    100.                     tristream.Append(o);
    101.                     int inext = (i+1) % 3;
    102.                     o.pos = UnityObjectToClipPos(IN[inext].vertex);
    103.                     o.uv = IN[inext].uv;
    104.                     tristream.Append(o);
    105.                     tristream.RestartStrip();
    106.                     o.pos = UnityObjectToClipPos(IN[i].vertex + float4(normalFace, 0) * _factor);
    107.                     o.uv = IN[i].uv;
    108.                     tristream.Append(o);
    109.                     o.pos = UnityObjectToClipPos(IN[inext].vertex);
    110.                     o.uv = IN[inext].uv;
    111.                     tristream.Append(o);
    112.                     o.pos = UnityObjectToClipPos(IN[inext].vertex + float4(normalFace, 0) * _factor);
    113.                     o.uv = IN[inext].uv;
    114.                     tristream.Append(o);
    115.                     tristream.RestartStrip();
    116.                 }
    117.                 for(int i = 0; i < 3; i++) {
    118.                     o.pos = UnityObjectToClipPos(IN[i].vertex + float4(normalFace, 0) * _factor);
    119.                     o.uv = IN[i].uv;
    120.                     tristream.Append(o);
    121.                 }
    122.                 tristream.RestartStrip();
    123.                 for(int i = 0; i < 3; i++) {
    124.                     o.pos = UnityObjectToClipPos(IN[i].vertex);
    125.                     o.uv = IN[i].uv;
    126.                     tristream.Append(o);
    127.                 }
    128.                 tristream.RestartStrip();
    129.             }
    130.          
    131.             fixed4 frag (g2f i) : SV_Target {
    132.                 // Setup.
    133.                 UNITY_SETUP_INSTANCE_ID(i);
    134.  
    135.                 return tex2D(_MainTex, i.uv) * _Color * _Burn;// UNITY_ACCESS_INSTANCED_PROP(Props, _Burn);
    136.             }
    137.             ENDCG
    138.         }
    139.     }
    I don't know what don't work. I guess it's relevant to UNITY_SETUP_INSTANCE_ID or UNITY_ACCESS_INSTANCED_PROP or UNITY_VERTEX_INPUT_INSTANCE_ID. I spent a big amont of time trying to find the problem, but unsuccessfully.

    Would you tell me, what's wrong? Guys, did you see some geometry shader+GPI Instancing examples?
     
    Last edited: Jun 1, 2020
  2. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    Bump!
    Nobody knows :/ ???
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    It's totally possible. But you have all of the lines you'd need to make it work commented out above for some reason. The vertex shader has the instance ID as an input. The main issue I see is you're calling
    UNITY_INITIALIZE_OUTPUT
    after you've setup the instance ID, which blows the value you just set away. That
    UNITY_INITIALIZE_OUTPUT
    macro sets all values of the struct to 0.0. So you need to call that first, before you call
    UNITY_TRANSFER_INSTANCE_ID
    if you're going to call it at all. Personally I never use it because it masks when I have an error someplace else, like having an unused value in the
    v2f
    , or in your case the
    v2g
    .
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Minimal instanced geometry shader, with instanced properties in both the geometry shader and fragment shader stages.
    Code (CSharp):
    1. Shader "Unlit/InstancedGeometryShader"
    2. {
    3.     Properties
    4.     {
    5.         _TriangleOffset ("Triangle Offset", Float) = 0.1
    6.         _Color ("Color", Color) = (1,1,1,1)
    7.     }
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Opaque" }
    11.         LOD 100
    12.  
    13.         Pass
    14.         {
    15.             CGPROGRAM
    16.             #pragma vertex vert
    17.             #pragma geometry geom
    18.             #pragma fragment frag
    19.  
    20.             #pragma multi_compile_instancing
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.             struct appdata
    25.             {
    26.                 float4 vertex : POSITION;
    27.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    28.             };
    29.  
    30.             struct v2g
    31.             {
    32.                 float4 worldPos : POSITION;
    33.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    34.             };
    35.  
    36.             struct g2f
    37.             {
    38.                 float4 pos : SV_POSITION;
    39.                 float3 barycentric : TEXCOORD0;
    40.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    41.             };
    42.  
    43.             v2g vert (appdata v)
    44.             {
    45.                 v2g o;
    46.  
    47.                 // set all values in the v2g o to 0.0
    48.                 UNITY_INITIALIZE_OUTPUT(v2g, o);
    49.  
    50.                 // setup the instanced id to be accessed
    51.                 UNITY_SETUP_INSTANCE_ID(v);
    52.  
    53.                 // copy instance id in the appdata v to the v2g o
    54.                 UNITY_TRANSFER_INSTANCE_ID(v, o);
    55.  
    56.                 o.worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0));
    57.                 return o;
    58.             }
    59.  
    60.             UNITY_INSTANCING_BUFFER_START(Props)
    61.                 UNITY_DEFINE_INSTANCED_PROP(float, _TriangleOffset)
    62.                 UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
    63.             UNITY_INSTANCING_BUFFER_END(Props)
    64.  
    65.             [maxvertexcount(24)]
    66.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> tristream)
    67.             {
    68.                 g2f o;
    69.  
    70.                 // set all values in the g2f o to 0.0
    71.                 UNITY_INITIALIZE_OUTPUT(g2f, o);
    72.  
    73.                 // setup the instanced id to be accessed
    74.                 UNITY_SETUP_INSTANCE_ID(IN[0]);
    75.  
    76.                 // copy instance id in the v2f IN[0] to the g2f o
    77.                 UNITY_TRANSFER_INSTANCE_ID(IN[0], o);
    78.  
    79.                 // access instanced property
    80.                 float triOffset = UNITY_ACCESS_INSTANCED_PROP(Props, _TriangleOffset);
    81.  
    82.                 // explode triangles by each triangle's actual surface normal
    83.                 float3 triNormal = normalize(cross(IN[0].worldPos.xyz - IN[1].worldPos.xyz, IN[0].worldPos.xyz - IN[2].worldPos.xyz)) * triOffset;
    84.  
    85.                 o.pos = UnityWorldToClipPos(IN[0].worldPos.xyz + triNormal);
    86.                 o.barycentric = float3(1,0,0);
    87.                 tristream.Append(o);
    88.  
    89.                 o.pos = UnityWorldToClipPos(IN[1].worldPos.xyz + triNormal);
    90.                 o.barycentric = float3(0,1,0);
    91.                 tristream.Append(o);
    92.  
    93.                 o.pos = UnityWorldToClipPos(IN[2].worldPos.xyz + triNormal);
    94.                 o.barycentric = float3(0,0,1);
    95.                 tristream.Append(o);
    96.             }
    97.  
    98.             fixed4 frag (g2f i) : SV_Target
    99.             {
    100.                 // setup instance id to be accessed
    101.                 UNITY_SETUP_INSTANCE_ID(i);
    102.  
    103.                 // access instanced property
    104.                 fixed4 col = UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
    105.                 col.rgb *= i.barycentric;
    106.  
    107.                 return col;
    108.             }
    109.             ENDCG
    110.         }
    111.     }
    112. }
     
    zhuchun, tmonestudio, suruz and 3 others like this.
  5. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    Tkank you SO much!!!
     
  6. jamespaterson

    jamespaterson

    Joined:
    Jun 19, 2018
    Posts:
    399
    Thanks really useful!
     
  7. grayson8030

    grayson8030

    Joined:
    Mar 13, 2021
    Posts:
    1
    To add on to this problem, I'm trying to reconfigure this script to support VR for single passed instanced rendering in the built-in renderer. But I can't figure out a way to show it in both eyes. The script is not made by me, its free and got it from here : https://www.patreon.com/posts/53587750.

    Code (CSharp):
    1. Shader "Custom/GrassForCompute"
    2. {
    3.     Properties
    4.     {
    5.         [Toggle(FADE)] _TransparentBottom("Transparency at Bottom", Float) = 0
    6.         _Fade("Fade Multiplier", Range(1,10)) = 6
    7.     }
    8.  
    9.         CGINCLUDE
    10. #include "UnityCG.cginc"
    11. #include "Lighting.cginc"
    12. #include "AutoLight.cginc"
    13. #pragma multi_compile _SHADOWS_SCREEN
    14. #pragma multi_compile_fwdbase_fullforwardshadows
    15. #pragma multi_compile_fog
    16. #pragma shader_feature FADE
    17.  
    18.     struct appdata
    19.     {
    20.      
    21.  
    22.         UNITY_VERTEX_INPUT_INSTANCE_ID
    23.     };
    24.  
    25.         struct DrawVertex
    26.     {
    27.         float3 positionWS; // The position in world space
    28.         float2 uv;
    29.         float3 diffuseColor;
    30.     };
    31.  
    32.     // A triangle on the generated mesh
    33.     struct DrawTriangle
    34.     {
    35.         float3 normalOS;
    36.         DrawVertex vertices[3]; // The three points on the triangle
    37.     };
    38.  
    39.     StructuredBuffer<DrawTriangle> _DrawTriangles;
    40.  
    41.     struct v2f
    42.     {
    43.         float4 pos : SV_POSITION; // Position in clip space
    44.         float2 uv : TEXCOORD0;          // The height of this vertex on the grass blade
    45.         float3 positionWS : TEXCOORD1; // Position in world space
    46.         float3 normalWS : TEXCOORD2;   // Normal vector in world space
    47.         float3 diffuseColor : COLOR;
    48.         LIGHTING_COORDS(3, 4)
    49.             UNITY_FOG_COORDS(5)
    50.             UNITY_VERTEX_OUTPUT_STEREO
    51.          
    52.     };
    53.  
    54.     // Properties
    55.     float4 _TopTint;
    56.     float4 _BottomTint;
    57.     float _AmbientStrength;
    58.     float _Fade;
    59.  
    60.     // Vertex function
    61.     struct unityTransferVertexToFragmentSucksHack
    62.     {
    63.        
    64.         float3 vertex : POSITION;
    65.     };
    66.  
    67.     v2f vert(appdata v)
    68.     {
    69.         v2f o;
    70.  
    71.         UNITY_SETUP_INSTANCE_ID(v); //Insert
    72.         UNITY_INITIALIZE_OUTPUT(v2f, o); //Insert
    73.         UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //Insert
    74.  
    75.      
    76.         return o;
    77.     }
    78.     // -- retrieve data generated from compute shader
    79.     v2f vert(uint vertexID : SV_VertexID)
    80.     {
    81.      
    82.         // Initialize the output struct
    83.         v2f output = (v2f)0;
    84.        
    85.         // Get the vertex from the buffer
    86.         // Since the buffer is structured in triangles, we need to divide the vertexID by three
    87.         // to get the triangle, and then modulo by 3 to get the vertex on the triangle
    88.         DrawTriangle tri = _DrawTriangles[vertexID / 3];
    89.         DrawVertex input = tri.vertices[vertexID % 3];
    90.  
    91.         output.pos = UnityObjectToClipPos(input.positionWS);
    92.         output.positionWS = input.positionWS;
    93.  
    94.         // float3 faceNormal = GetMainLight().direction * tri.normalOS;
    95.         float3 faceNormal = tri.normalOS;
    96.         // output.normalWS = TransformObjectToWorldNormal(faceNormal, true);
    97.         output.normalWS = faceNormal;
    98.  
    99.         output.uv = input.uv;
    100.  
    101.         output.diffuseColor = input.diffuseColor;
    102.  
    103.         // making pointlights work requires v.vertex
    104.         unityTransferVertexToFragmentSucksHack v;
    105.         v.vertex = output.pos;
    106.  
    107.         TRANSFER_VERTEX_TO_FRAGMENT(output);
    108.         UNITY_TRANSFER_FOG(output, output.pos);
    109.  
    110.         return output;
    111.     }
    112.  
    113.  
    114.  
    115.  
    116.     ENDCG
    117.         SubShader
    118.     {
    119.         Cull Off
    120.         Blend SrcAlpha OneMinusSrcAlpha // for the transparency
    121.         Pass // basic color with directional lights
    122.         {
    123.             Tags
    124.             {
    125.                 "LightMode" = "ForwardBase"
    126.             }
    127.  
    128.             CGPROGRAM
    129.             #pragma vertex vert
    130.             #pragma fragment frag    
    131.             #include "UnityCG.cginc"
    132.  
    133.             float4 frag(v2f i) : SV_Target
    134.             {
    135.            
    136.             // take shadow data
    137.             float shadow = 1;
    138.             #if defined(SHADOWS_SCREEN)
    139.                 shadow = (SAMPLE_DEPTH_TEXTURE_PROJ(_ShadowMapTexture, UNITY_PROJ_COORD(i._ShadowCoord)).r);
    140.             #endif          
    141.                 // base color by lerping 2 colors over the UVs
    142.                 float4 baseColor = lerp(_BottomTint , _TopTint , saturate(i.uv.y)) * float4(i.diffuseColor, 1);
    143.                 // multiply with lighting color
    144.                 float4 litColor = (baseColor * _LightColor0);
    145.                 // multiply with vertex color, and shadows
    146.                 float4 final = litColor;
    147.                 final.rgb = litColor * shadow;
    148.                 // add in baseColor when lights turned off
    149.                 final += saturate((1 - shadow) * baseColor * 0.2);
    150.                 // add in ambient color
    151.                 final += (unity_AmbientSky * baseColor * _AmbientStrength);
    152.  
    153.                 // add fog
    154.                 UNITY_APPLY_FOG(i.fogCoord, final);
    155.                 // fade the bottom based on the vertical uvs
    156.                 #if FADE
    157.                     float alpha = lerp(0, 1, saturate(i.uv.y * _Fade));
    158.                     final.a = alpha;
    159.                 #endif
    160.                 return final;
    161.             }
    162.             ENDCG
    163.         }
    164.  
    165.         Pass
    166.                 // point lights
    167.                 {
    168.                     Tags
    169.                     {
    170.                         "LightMode" = "ForwardAdd"
    171.                     }
    172.                     Blend OneMinusDstColor One
    173.                     ZWrite Off
    174.                     Cull Off
    175.  
    176.                     CGPROGRAM
    177.                     #pragma vertex vert
    178.                     #pragma fragment frag                                  
    179.                     #pragma multi_compile_fwdadd_fullforwardshadows
    180.                     #include "UnityCG.cginc"
    181.  
    182.                     float4 frag(v2f i) : SV_Target
    183.                     {
    184.                        
    185.                         UNITY_LIGHT_ATTENUATION(atten, i, i.positionWS);
    186.  
    187.             // base color by lerping 2 colors over the UVs
    188.             float3 baseColor = lerp(_BottomTint , _TopTint , saturate(i.uv.y)) * i.diffuseColor;
    189.  
    190.             float3 pointlights = atten * _LightColor0.rgb * baseColor;
    191.             #if FADE
    192.                 float alpha = lerp(0, 1, saturate(i.uv.y * _Fade));
    193.                 pointlights *= alpha;
    194.             #endif
    195.  
    196.             return float4(pointlights, 1);
    197.         }
    198.         ENDCG
    199.     }
    200.  
    201.     Pass // shadow pass
    202.     {
    203.  
    204.         Tags
    205.         {
    206.             "LightMode" = "ShadowCaster"
    207.         }
    208.  
    209.         CGPROGRAM
    210.         #pragma vertex vert
    211.         #pragma fragment frag
    212.         #pragma multi_compile_shadowcaster
    213.          #include "UnityCG.cginc"
    214.  
    215.         float4 frag(v2f i) : SV_Target
    216.         {
    217.          
    218.             SHADOW_CASTER_FRAGMENT(i)
    219.         }
    220.         ENDCG
    221.     }
    222.  
    223.  
    224.     }        Fallback "VertexLit"
    225. }
    226.  
     
    Last edited: Jan 5, 2022
  8. unity_Ie0Odhe1gaOaCg

    unity_Ie0Odhe1gaOaCg

    Unity Technologies

    Joined:
    Jan 30, 2021
    Posts:
    2
  9. nakoustix

    nakoustix

    Joined:
    Apr 21, 2019
    Posts:
    15
    Thank you @bgolus! I just started my shader learning journey and I already stumbled across many very helpful hints from you in the forums. Thank you so much!
     
  10. suruz

    suruz

    Joined:
    Jun 4, 2011
    Posts:
    36
    I'm trying to implement geometry shader, for low-poly mesh voxelization and I guess it will be helpful for voxel instancing. thanks!
     
  11. OliverVasvari

    OliverVasvari

    Joined:
    Sep 13, 2022
    Posts:
    1
    I'm trying to port a geometry shader to Single Pass Instanced render mode. I'm using the macros mentioned above, also tried to use
    UNITY_VERTEX_OUTPUT_STEREO
    ,
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO
    and
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
    with no luck.
    Is there a way to use geometry shader with Single Pass Instanced render mode?