Search Unity

GPU instancing on Geometry Shader

Discussion in 'Shaders' started by Tralamy, May 7, 2021.

  1. Tralamy

    Tralamy

    Joined:
    Jun 2, 2020
    Posts:
    2
    I followed two tutorials on making a grass shader using Geometry Shader (this one, and this one to upgrade the first one). Even if the second article upgraded the performance a bit, it wasn't enough. So I learned about GPU instancing, but I didnt found any thing on the subject on the Unity Documentation. So my question is, Is it possible to use GPU instancing and how to do it.

    Code-
    Grass.shader:
    Code (CSharp):
    1. Shader "Roystan/Grass"
    2. {
    3.     Properties
    4.     {
    5.         [Header(Shading)]
    6.         _TopColor("Top Color", Color) = (1,1,1,1)
    7.         _BottomColor("Bottom Color", Color) = (1,1,1,1)
    8.         _TranslucentGain("Translucent Gain", Range(0,1)) = 0.5
    9.         _BendRotationRandom("Bend Rotation Random", Range(0, 1)) = 0.2
    10.         _BladeWidth("Blade Width", Float) = 0.05
    11.         _BladeWidthRandom("Blade Width Random", Float) = 0.02
    12.         _BladeHeight("Blade Height", Float) = 0.5
    13.         _BladeHeightRandom("Blade Height Random", Float) = 0.3
    14.         _TessellationUniform("Tessellation Uniform", Range(1, 64)) = 1
    15.         _WindDistortionMap("Wind Distortion Map", 2D) = "white" {}
    16.         _WindFrequency("Wind Frequency", Vector) = (0.05, 0.05, 0, 0)
    17.         _WindStrength("Wind Strength", Float) = 1
    18.         _BladeForward("Blade Forward Amount", Float) = 0.38
    19.         _BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2
    20.         _minDist("Minimum Distance", Float) = 10
    21.         _maxDist("Maximum Distance", Float) = 25
    22.         _SlopeLimit("Slope Limit", Float) = 0
    23.         _BeachLimit("Beach Limit", Float) = 0
    24.         _HeightLimit("Height Limit", Float) = 0
    25.     }
    26.  
    27.     CGINCLUDE
    28.     #include "Shaders/CustomTessellation.cginc"
    29.     #include "UnityCG.cginc"
    30.     #include "Autolight.cginc"
    31.  
    32.     #define BLADE_SEGMENTS 2
    33.  
    34.     float _BendRotationRandom;
    35.     float _BladeHeight;
    36.     float _BladeHeightRandom;
    37.     float _BladeWidth;
    38.     float _BladeWidthRandom;
    39.     sampler2D _WindDistortionMap;
    40.     float4 _WindDistortionMap_ST;
    41.     float2 _WindFrequency;
    42.     float _WindStrength;
    43.     float _BladeForward;
    44.     float _BladeCurve;
    45.     float _minDistance;
    46.     float _maxDistance;
    47.     float _SlopeLimit;
    48.     float _BeachLimit;
    49.     float _HeightLimit;
    50.    
    51.  
    52.     float rand(float3 co)
    53.     {
    54.         return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
    55.     }
    56.  
    57.     // Construct a rotation matrix that rotates around the provided axis, sourced from:
    58.     // https://gist.github.com/keijiro/ee439d5e7388f3aafc5296005c8c3f33
    59.     float3x3 AngleAxis3x3(float angle, float3 axis)
    60.     {
    61.         float c, s;
    62.         sincos(angle, s, c);
    63.  
    64.         float t = 1 - c;
    65.         float x = axis.x;
    66.         float y = axis.y;
    67.         float z = axis.z;
    68.  
    69.         return float3x3(
    70.             t * x * x + c, t * x * y - s * z, t * x * z + s * y,
    71.             t * x * y + s * z, t * y * y + c, t * y * z - s * x,
    72.             t * x * z - s * y, t * y * z + s * x, t * z * z + c
    73.         );
    74.     }
    75.  
    76.     struct geometryOutput
    77.     {
    78.         float4 pos : SV_POSITION;
    79.         float2 uv : TEXCOORD0;
    80.         unityShadowCoord4 _ShadowCoord : TEXCOORD1;
    81.         float3 normal : NORMAL;
    82.     };
    83.  
    84.     geometryOutput VertexOutput(float3 pos, float2 uv, float3 normal)
    85.     {
    86.         geometryOutput o;
    87.         o.pos = UnityObjectToClipPos(pos);
    88.         o.uv = uv;
    89.         o._ShadowCoord = ComputeScreenPos(o.pos);
    90.         o.normal = UnityObjectToWorldNormal(normal);
    91.         #if UNITY_PASS_SHADOWCASTER
    92.             o.pos = UnityApplyLinearShadowBias(o.pos);
    93.         #endif
    94.  
    95.         return o;
    96.     }
    97.  
    98.     geometryOutput GenerateGrassVertex(float3 vertexPosition, float width, float height, float forward, float2 uv,
    99.                                        float3x3 transformMatrix)
    100.     {
    101.         float3 tangentPoint = float3(width, forward, height);
    102.         float3 tangentNormal = normalize(float3(0, -1, forward));
    103.         float3 localNormal = mul(transformMatrix, tangentNormal);
    104.         float3 localPosition = vertexPosition + mul(transformMatrix, tangentPoint);
    105.         return VertexOutput(localPosition, uv, localNormal);
    106.     }
    107.  
    108.     [maxvertexcount(BLADE_SEGMENTS * 2 + 1)]
    109.     void geo(triangle vertexOutput IN[3]: SV_POSITION, inout TriangleStream<geometryOutput> triStream)
    110.     {
    111.         float3 pos = IN[0].vertex;
    112.  
    113.         float3 vNormal = IN[0].normal;
    114.         float4 vTangent = IN[0].tangent;
    115.         float3 vBinormal = cross(vNormal, vTangent) * vTangent.w;
    116.  
    117.         float3x3 tangentToLocal = float3x3(
    118.             vTangent.x, vBinormal.x, vNormal.x,
    119.             vTangent.y, vBinormal.y, vNormal.y,
    120.             vTangent.z, vBinormal.z, vNormal.z
    121.         );
    122.  
    123.         float3x3 facingRotationMatrix = AngleAxis3x3(rand(pos) * UNITY_TWO_PI, float3(0, 0, 1));
    124.  
    125.         float3x3 bendRotationMatrix = AngleAxis3x3(rand(pos.zzx) * _BendRotationRandom * UNITY_PI * 0.5,
    126.                                                    float3(-1, 0, 0));
    127.  
    128.         float2 uv = pos.xz * _WindDistortionMap_ST.xy + _WindDistortionMap_ST.zw + _WindFrequency * _Time.y;
    129.         float2 windSample = (tex2Dlod(_WindDistortionMap, float4(uv, 0, 0)).xy * 2 - 1) * _WindStrength;
    130.         float3 wind = normalize(float3(windSample.x, windSample.y, 0));
    131.         float3x3 windRotation = AngleAxis3x3(UNITY_PI * windSample, wind);
    132.         float3x3 transformationMatrix = mul(mul(mul(tangentToLocal, windRotation), facingRotationMatrix),
    133.                                             bendRotationMatrix);
    134.         float3x3 transformationMatrixFacing = mul(tangentToLocal, facingRotationMatrix);
    135.         float height = (rand(pos.zyx) * 2 - 1) * _BladeHeightRandom + _BladeHeight;
    136.         float forward = rand(pos.yyz) * _BladeForward;
    137.         float width = (rand(pos.xzy) * 2 - 1) * _BladeWidthRandom + _BladeWidth;
    138.  
    139.         float3 worldPos = mul(unity_ObjectToWorld, IN[0].vertex.xyz);
    140.         float3 worldNormal = mul(unity_ObjectToWorld, float4(IN[0].normal, 0.0)).xyz;
    141.         float slope = worldNormal.y;
    142.         float dist = distance(_WorldSpaceCameraPos, mul(unity_ObjectToWorld, IN[0].vertex).xyz);
    143.         int shouldCreateGrass = sign(slope - _SlopeLimit) + sign(_maxDist - dist) + sign(pos.y - _BeachLimit) + sign(
    144.             _HeightLimit - pos.y);
    145.         //we stick all the vertex generation code in this if statement
    146.         if (shouldCreateGrass == 4)
    147.         {
    148.             for (int i = 0; i < BLADE_SEGMENTS; i++)
    149.             {
    150.                 float t = i / (float)BLADE_SEGMENTS;
    151.                 float segmentHeight = height * t;
    152.                 float segmentWidth = width * (1 - t);
    153.                 float segmentForward = pow(t, _BladeCurve) * forward;
    154.  
    155.                 float3x3 transformMatrix = i == 0 ? transformationMatrixFacing : transformationMatrix;
    156.  
    157.                 triStream.Append(GenerateGrassVertex(pos, segmentWidth, segmentHeight, segmentForward, float2(0, t),
    158.                                                      transformMatrix));
    159.                 triStream.Append(GenerateGrassVertex(pos, -segmentWidth, segmentHeight, segmentForward, float2(1, t),
    160.                                                      transformMatrix));
    161.             }
    162.             triStream.Append(GenerateGrassVertex(pos, 0, height, forward, float2(0.5, 1), transformationMatrix));
    163.         }
    164.     }
    165.     ENDCG
    166.  
    167.     SubShader
    168.     {
    169.         Cull Off
    170.  
    171.         Pass
    172.         {
    173.             Tags
    174.             {
    175.                 "RenderType" = "Opaque"
    176.                 "LightMode" = "ForwardBase"
    177.             }
    178.  
    179.             CGPROGRAM
    180.             #pragma vertex vert
    181.             #pragma fragment frag
    182.             #pragma geometry geo
    183.             #pragma target 4.6
    184.             #pragma multi_compile_fwdbase
    185.             #pragma hull hull
    186.             #pragma domain domain
    187.  
    188.             #include "Lighting.cginc"
    189.  
    190.             float4 _TopColor;
    191.             float4 _BottomColor;
    192.             float _TranslucentGain;
    193.  
    194.             float4 frag(geometryOutput i, fixed facing : VFACE) : SV_Target
    195.             {
    196.                 float3 normal = facing > 0 ? i.normal : -i.normal;
    197.                 float shadow = SHADOW_ATTENUATION(i);
    198.                 float NdotL = saturate(saturate(dot(normal, _WorldSpaceLightPos0)) + _TranslucentGain) * shadow;
    199.  
    200.                 float3 ambient = ShadeSH9(float4(normal, 1));
    201.                 float4 lightIntensity = NdotL * _LightColor0 + float4(ambient, 1);
    202.                 float4 col = lerp(_BottomColor, _TopColor * lightIntensity, i.uv.y);
    203.  
    204.                 return col;
    205.             }
    206.             ENDCG
    207.         }
    208.         Pass
    209.         {
    210.             Tags
    211.             {
    212.                 "LightMode" = "ShadowCaster"
    213.             }
    214.  
    215.             CGPROGRAM
    216.             #pragma vertex vert
    217.             #pragma geometry geo
    218.             #pragma fragment frag
    219.             #pragma hull hull
    220.             #pragma domain domain
    221.             #pragma target 4.6
    222.             #pragma multi_compile_shadowcaster
    223.  
    224.             float4 frag(geometryOutput i) : SV_Target
    225.             {
    226.                 SHADOW_CASTER_FRAGMENT(i);
    227.             }
    228.             ENDCG
    229.         }
    230.     }
    231. }
    CustomTessellation.cginc:
    Code (CSharp):
    1. // Tessellation programs based on this article by Catlike Coding:
    2. // https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/
    3.  
    4. struct vertexInput
    5. {
    6.     float4 vertex : POSITION;
    7.     float3 normal : NORMAL;
    8.     float4 tangent : TANGENT;
    9. };
    10.  
    11. struct vertexOutput
    12. {
    13.     float4 vertex : SV_POSITION;
    14.     float3 normal : NORMAL;
    15.     float4 tangent : TANGENT;
    16. };
    17.  
    18. struct TessellationFactors
    19. {
    20.     float edge[3] : SV_TessFactor;
    21.     float inside : SV_InsideTessFactor;
    22. };
    23.  
    24. vertexInput vert(vertexInput v)
    25. {
    26.     return v;
    27. }
    28.  
    29. vertexOutput tessVert(vertexInput v)
    30. {
    31.     vertexOutput o;
    32.     // Note that the vertex is NOT transformed to clip
    33.     // space here; this is done in the grass geometry shader.
    34.     o.vertex = v.vertex;
    35.     o.normal = v.normal;
    36.     o.tangent = v.tangent;
    37.     return o;
    38. }
    39.  
    40. float _TessellationUniform;
    41. float _minDist;
    42. float _maxDist;
    43. float TessellationEdgeFactor(vertexInput cp0, vertexInput cp1){
    44.     float3 p0 = mul(unity_ObjectToWorld, float4(cp0.vertex.xyz, 1)).xyz;
    45.     float3 p1 = mul(unity_ObjectToWorld, float4(cp1.vertex.xyz, 1)).xyz;
    46.     float edgeLength = distance(p0, p1);
    47.  
    48.     float3 edgeCenter = (p0 + p1) * 0.5;
    49.     float viewDistance = distance(edgeCenter, _WorldSpaceCameraPos);
    50.     return  clamp(1.0 - (viewDistance - _minDist) / (_maxDist - _minDist), 0.01, 1.0) * _TessellationUniform;
    51. }
    52. TessellationFactors patchConstantFunction (InputPatch<vertexInput, 3> patch)
    53. {
    54.     TessellationFactors f;
    55.     f.edge[0] = TessellationEdgeFactor(patch[1], patch[2]);
    56.     f.edge[1] = TessellationEdgeFactor(patch[2], patch[0]);
    57.     f.edge[2] = TessellationEdgeFactor(patch[0], patch[1]);
    58.     f.inside = (TessellationEdgeFactor(patch[1], patch[2]) +
    59.         TessellationEdgeFactor(patch[2], patch[0]) +
    60.         TessellationEdgeFactor(patch[0], patch[1])) * (1 / 3.0);
    61.     // f.edge[0] = _TessellationUniform;
    62.     // f.edge[1] = _TessellationUniform;
    63.     // f.edge[2] = _TessellationUniform;
    64.     // f.inside = _TessellationUniform;
    65.     return f;
    66. }
    67.  
    68. [UNITY_domain("tri")]
    69. [UNITY_outputcontrolpoints(3)]
    70. [UNITY_outputtopology("triangle_cw")]
    71. [UNITY_partitioning("integer")]
    72. [UNITY_patchconstantfunc("patchConstantFunction")]
    73. vertexInput hull (InputPatch<vertexInput, 3> patch, uint id : SV_OutputControlPointID)
    74. {
    75.     return patch[id];
    76. }
    77.  
    78. [UNITY_domain("tri")]
    79. vertexOutput domain(TessellationFactors factors, OutputPatch<vertexInput, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)
    80. {
    81.     vertexInput v;
    82.  
    83.     #define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) v.fieldName = \
    84.         patch[0].fieldName * barycentricCoordinates.x + \
    85.         patch[1].fieldName * barycentricCoordinates.y + \
    86.         patch[2].fieldName * barycentricCoordinates.z;
    87.  
    88.     MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)
    89.     MY_DOMAIN_PROGRAM_INTERPOLATE(normal)
    90.     MY_DOMAIN_PROGRAM_INTERPOLATE(tangent)
    91.  
    92.     return tessVert(v);
    93. }
     
  2. Tralamy

    Tralamy

    Joined:
    Jun 2, 2020
    Posts:
    2
    Bump?